Skip to content

Commit e934813

Browse files
committed
draft KeysInterface.sign_holder_commitment_phase2
1 parent 8640173 commit e934813

File tree

6 files changed

+335
-18
lines changed

6 files changed

+335
-18
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 146 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@ use util::ser::{Writeable, Writer, Readable};
3333

3434
use chain::transaction::OutPoint;
3535
use ln::chan_utils;
36-
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys};
36+
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys, derive_public_key, derive_public_revocation_key, TxCreationKeys};
3737
use ln::msgs::UnsignedChannelAnnouncement;
3838

3939
use std::sync::atomic::{AtomicUsize, Ordering};
4040
use std::io::Error;
4141
use ln::msgs::DecodeError;
42+
use ln::transaction::{CommitmentInfo, build_commitment_tx, get_commitment_transaction_number_obscure_factor};
4243

4344
/// When on-chain outputs are created by rust-lightning (which our counterparty is not able to
4445
/// claim at any point in the future) an event is generated which you must track and be able to
@@ -79,7 +80,7 @@ pub enum SpendableOutputDescriptor {
7980
///
8081
/// To derive the revocation_pubkey provided here (which is used in the witness
8182
/// script generation), you must pass the counterparty revocation_basepoint (which appears in the
82-
/// call to ChannelKeys::on_accept) and the provided per_commitment point
83+
/// call to ChannelKeys::on_funding_created) and the provided per_commitment point
8384
/// to chan_utils::derive_public_revocation_key.
8485
///
8586
/// The witness script which is hashed and included in the output script_pubkey may be
@@ -244,6 +245,17 @@ pub trait ChannelKeys : Send+Clone {
244245
// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and
245246
fn sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;
246247

248+
/// Create a signature for a holder's commitment transaction and attached HTLC transactions.
249+
fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(
250+
&self,
251+
commitment_number: u64,
252+
feerate_per_kw: u32,
253+
to_holder_value_sat: u64,
254+
to_counterparty_value_sat: u64,
255+
htlcs: &Vec<HTLCOutputInCommitment>,
256+
secp_ctx: &Secp256k1<T>,
257+
) -> Result<(Signature, Vec<Signature>), ()>;
258+
247259
/// Same as sign_holder_commitment, but exists only for tests to get access to holder commitment
248260
/// transactions which will be broadcasted later, after the channel has moved on to a newer
249261
/// state. Thus, needs its own method as sign_holder_commitment may enforce that we only ever
@@ -319,13 +331,13 @@ pub trait ChannelKeys : Send+Clone {
319331
/// protocol.
320332
fn sign_channel_announcement<T: secp256k1::Signing>(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;
321333

322-
/// Set the counterparty channel basepoints and counterparty_selected/holder_selected_contest_delay.
323-
/// This is done immediately on incoming channels and as soon as the channel is accepted on outgoing channels.
334+
/// Set the counterparty channel basepoints, counterparty_selected/holder_selected_contest_delay and funding outpoint.
335+
/// This is done as soon as the funding outpoint is known.
324336
///
325337
/// We bind holder_selected_contest_delay late here for API convenience.
326338
///
327339
/// Will be called before any signatures are applied.
328-
fn on_accept(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16);
340+
fn on_funding_created(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint);
329341
}
330342

331343
/// A trait to describe an object which can get user secrets and key material.
@@ -365,6 +377,8 @@ struct AcceptedChannelData {
365377
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
366378
/// if they broadcast a transaction.
367379
holder_selected_contest_delay: u16,
380+
/// Whether the holder is the initiator of this channel
381+
is_outbound: bool,
368382
}
369383

370384
#[derive(Clone)]
@@ -386,6 +400,8 @@ pub struct InMemoryChannelKeys {
386400
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
387401
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
388402
accepted_channel_data: Option<AcceptedChannelData>,
403+
/// Funding outpoint, populated on channel funding creation
404+
funding_outpoint: Option<OutPoint>,
389405
/// The total value of this channel
390406
channel_value_satoshis: u64,
391407
/// Key derivation parameters
@@ -418,6 +434,7 @@ impl InMemoryChannelKeys {
418434
channel_value_satoshis,
419435
holder_channel_pubkeys,
420436
accepted_channel_data: None,
437+
funding_outpoint: None,
421438
key_derivation_params,
422439
}
423440
}
@@ -439,21 +456,92 @@ impl InMemoryChannelKeys {
439456
}
440457

441458
/// Counterparty pubkeys.
442-
/// Will panic if on_accept wasn't called.
459+
/// Will panic if on_funding_created wasn't called.
443460
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.accepted_channel_data.as_ref().unwrap().counterparty_channel_pubkeys }
444461

445462
/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
446463
/// transactions, ie the amount of time that we have to wait to recover our funds if we
447464
/// broadcast a transaction. You'll likely want to pass this to the
448465
/// ln::chan_utils::build*_transaction functions when signing holder's transactions.
449-
/// Will panic if on_accept wasn't called.
466+
/// Will panic if on_funding_created wasn't called.
450467
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().counterparty_selected_contest_delay }
451468

452469
/// The contest_delay value specified by us and applied on transactions broadcastable
453470
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
454471
/// if they broadcast a transaction.
455-
/// Will panic if on_accept wasn't called.
472+
/// Will panic if on_funding_created wasn't called.
456473
pub fn holder_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().holder_selected_contest_delay }
474+
475+
/// Whether the holder is the initiator
476+
pub fn is_outbound(&self) -> bool { self.accepted_channel_data.as_ref().unwrap().is_outbound }
477+
478+
/// Funding outpoint
479+
/// Will panic if on_funding_created wasn't called.
480+
pub fn funding_outpoint(&self) -> &OutPoint { self.funding_outpoint.as_ref().unwrap() }
481+
482+
/// Prepare build information for a holder-broadcast commitment tx
483+
pub fn build_holder_commitment_info<T: secp256k1::Signing + secp256k1::Verification>(
484+
&self,
485+
per_commitment_point: &PublicKey,
486+
to_holder_value_sat: u64,
487+
to_counterparty_value_sat: u64,
488+
htlcs: &Vec<HTLCOutputInCommitment>,
489+
secp_ctx: &Secp256k1<T>,
490+
) -> Result<CommitmentInfo, ()> {
491+
let counterparty_points = self.counterparty_pubkeys();
492+
493+
let to_holder_delayed_pubkey = derive_public_key(
494+
&secp_ctx,
495+
&per_commitment_point,
496+
&self.pubkeys().delayed_payment_basepoint,
497+
).map_err(|_| ())?;
498+
499+
let revocation_pubkey = derive_public_revocation_key(
500+
&secp_ctx,
501+
&per_commitment_point,
502+
&counterparty_points.revocation_basepoint,
503+
).map_err(|_| ())?;
504+
505+
Ok(CommitmentInfo {
506+
is_counterparty_broadcaster: false,
507+
to_countersigner_pubkey: counterparty_points.payment_point,
508+
to_countersigner_value_sat: to_counterparty_value_sat,
509+
revocation_pubkey,
510+
to_broadcaster_delayed_pubkey: to_holder_delayed_pubkey,
511+
to_broadcaster_value_sat: to_holder_value_sat,
512+
to_self_delay: self.counterparty_selected_contest_delay(),
513+
htlcs: htlcs.clone(),
514+
})
515+
}
516+
517+
fn get_commitment_transaction_number_obscure_factor(&self) -> u64 {
518+
get_commitment_transaction_number_obscure_factor(
519+
&self.pubkeys().payment_point,
520+
&self.counterparty_pubkeys().payment_point,
521+
self.is_outbound(),
522+
)
523+
}
524+
525+
/// Build a commitment tx
526+
pub fn build_commitment_tx<T: secp256k1::Signing + secp256k1::Verification>(&self, per_commitment_point: &PublicKey,
527+
commitment_number: u64, info: &CommitmentInfo,
528+
secp_ctx: &Secp256k1<T>) -> Result<(bitcoin::Transaction, Vec<HTLCOutputInCommitment>, TxCreationKeys, Vec<Script>), ()> {
529+
let keys = if !info.is_counterparty_broadcaster {
530+
TxCreationKeys::make_tx_keys(per_commitment_point, self.pubkeys(), self.counterparty_pubkeys(), secp_ctx)
531+
} else {
532+
TxCreationKeys::make_tx_keys(per_commitment_point, self.counterparty_pubkeys(), self.pubkeys(), secp_ctx)
533+
}.unwrap();
534+
535+
let obscured_commitment_transaction_number =
536+
self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
537+
let (tx, htlcs, scripts) = build_commitment_tx(
538+
&keys,
539+
info,
540+
obscured_commitment_transaction_number,
541+
self.funding_outpoint().into_bitcoin_outpoint(),
542+
);
543+
Ok((tx, htlcs, keys, scripts))
544+
}
457545
}
458546

459547
impl ChannelKeys for InMemoryChannelKeys {
@@ -507,6 +595,47 @@ impl ChannelKeys for InMemoryChannelKeys {
507595
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
508596
}
509597

598+
fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_number: u64, feerate_per_kw: u32, to_holder_value_sat: u64, to_counterparty_value_sat: u64, htlcs: &Vec<HTLCOutputInCommitment>, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
599+
let per_commitment_point = self.get_per_commitment_point(commitment_number, secp_ctx);
600+
let info = self.build_holder_commitment_info(
601+
&per_commitment_point,
602+
to_holder_value_sat,
603+
to_counterparty_value_sat,
604+
htlcs,
605+
secp_ctx,
606+
)?;
607+
let (tx, htlcs, keys, _) =
608+
self.build_commitment_tx(&per_commitment_point, commitment_number, &info, secp_ctx)?;
609+
610+
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
611+
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
612+
let sighash = hash_to_message!(&bip143::SigHashCache::new(&tx)
613+
.signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
614+
let sig = secp_ctx.sign(&sighash, &self.funding_key);
615+
616+
// We provide a dummy signature for the remote, since we don't require that sig
617+
// to be passed in to this call. It would have been better if HolderCommitmentTransaction
618+
// didn't require the remote sig.
619+
let dummy_sig = Secp256k1::new().sign(
620+
&secp256k1::Message::from_slice(&[42; 32]).unwrap(),
621+
&SecretKey::from_slice(&[42; 32]).unwrap(),
622+
);
623+
let htlcs_with_sig = htlcs.iter().map(|h| (h.clone(), Some(dummy_sig.clone()))).collect();
624+
let commitment_tx = HolderCommitmentTransaction::new_missing_holder_sig(
625+
tx,
626+
dummy_sig,
627+
&self.pubkeys().funding_pubkey,
628+
&self.counterparty_pubkeys().funding_pubkey,
629+
keys,
630+
feerate_per_kw,
631+
htlcs_with_sig,
632+
);
633+
let htlc_sigs_o = self.sign_holder_commitment_htlc_transactions(&commitment_tx, secp_ctx)?;
634+
let htlc_sigs = htlc_sigs_o.iter().map(|o| o.unwrap()).collect();
635+
636+
Ok((sig, htlc_sigs))
637+
}
638+
510639
#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
511640
fn unsafe_sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
512641
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
@@ -588,18 +717,21 @@ impl ChannelKeys for InMemoryChannelKeys {
588717
Ok(secp_ctx.sign(&msghash, &self.funding_key))
589718
}
590719

591-
fn on_accept(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16) {
592-
assert!(self.accepted_channel_data.is_none(), "Already accepted");
720+
fn on_funding_created(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint) {
721+
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
593722
self.accepted_channel_data = Some(AcceptedChannelData {
594723
counterparty_channel_pubkeys: channel_pubkeys.clone(),
595724
counterparty_selected_contest_delay,
596725
holder_selected_contest_delay,
726+
is_outbound,
597727
});
728+
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
729+
self.funding_outpoint = Some(funding_outpoint.clone());
598730
}
599731
}
600732

601733
impl_writeable!(AcceptedChannelData, 0,
602-
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
734+
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });
603735

604736
impl Writeable for InMemoryChannelKeys {
605737
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
@@ -610,6 +742,7 @@ impl Writeable for InMemoryChannelKeys {
610742
self.htlc_base_key.write(writer)?;
611743
self.commitment_seed.write(writer)?;
612744
self.accepted_channel_data.write(writer)?;
745+
self.funding_outpoint.write(writer)?;
613746
self.channel_value_satoshis.write(writer)?;
614747
self.key_derivation_params.0.write(writer)?;
615748
self.key_derivation_params.1.write(writer)?;
@@ -627,6 +760,7 @@ impl Readable for InMemoryChannelKeys {
627760
let htlc_base_key = Readable::read(reader)?;
628761
let commitment_seed = Readable::read(reader)?;
629762
let counterparty_channel_data = Readable::read(reader)?;
763+
let funding_outpoint = Readable::read(reader)?;
630764
let channel_value_satoshis = Readable::read(reader)?;
631765
let secp_ctx = Secp256k1::signing_only();
632766
let holder_channel_pubkeys =
@@ -646,6 +780,7 @@ impl Readable for InMemoryChannelKeys {
646780
channel_value_satoshis,
647781
holder_channel_pubkeys,
648782
accepted_channel_data: counterparty_channel_data,
783+
funding_outpoint,
649784
key_derivation_params: (params_1, params_2),
650785
})
651786
}

lightning/src/ln/chan_utils.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,18 @@ impl TxCreationKeys {
382382
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
383383
})
384384
}
385+
386+
/// Make transaction creation keys from channel keys
387+
pub fn make_tx_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TxCreationKeys, SecpError> {
388+
TxCreationKeys::derive_new(
389+
&secp_ctx,
390+
&per_commitment_point,
391+
&broadcaster_keys.delayed_payment_basepoint,
392+
&broadcaster_keys.htlc_basepoint,
393+
&countersignatory_keys.revocation_basepoint,
394+
&countersignatory_keys.htlc_basepoint,
395+
)
396+
}
385397
}
386398

387399
/// A script either spendable by the revocation

lightning/src/ln/channel.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -582,15 +582,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
582582
where K::Target: KeysInterface<ChanKeySigner = ChanSigner>,
583583
F::Target: FeeEstimator
584584
{
585-
let mut chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
585+
let chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
586586
let counterparty_pubkeys = ChannelPublicKeys {
587587
funding_pubkey: msg.funding_pubkey,
588588
revocation_basepoint: msg.revocation_basepoint,
589589
payment_point: msg.payment_point,
590590
delayed_payment_basepoint: msg.delayed_payment_basepoint,
591591
htlc_basepoint: msg.htlc_basepoint
592592
};
593-
chan_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, config.own_channel_config.our_to_self_delay);
594593
let mut local_config = (*config).channel_options.clone();
595594

596595
if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
@@ -1463,7 +1462,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
14631462
htlc_basepoint: msg.htlc_basepoint
14641463
};
14651464

1466-
self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay);
14671465
self.counterparty_pubkeys = Some(counterparty_pubkeys);
14681466

14691467
self.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
@@ -1518,6 +1516,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
15181516
}
15191517

15201518
let funding_txo = OutPoint{ txid: msg.funding_txid, index: msg.funding_output_index };
1519+
self.holder_keys.on_funding_created(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
15211520
self.funding_txo = Some(funding_txo.clone());
15221521

15231522
let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
@@ -3545,6 +3544,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
35453544
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
35463545
}
35473546

3547+
self.holder_keys.on_funding_created(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
35483548
self.funding_txo = Some(funding_txo.clone());
35493549
let signature = match self.get_outbound_funding_created_signature(logger) {
35503550
Ok(res) => res,
@@ -4647,7 +4647,7 @@ mod tests {
46474647
chan.holder_dust_limit_satoshis = 546;
46484648

46494649
let funding_info = OutPoint{ txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 };
4650-
chan.funding_txo = Some(funding_info);
4650+
chan.funding_txo = Some(funding_info.clone());
46514651

46524652
let counterparty_pubkeys = ChannelPublicKeys {
46534653
funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
@@ -4656,7 +4656,7 @@ mod tests {
46564656
delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
46574657
htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444")
46584658
};
4659-
chan_keys.on_accept(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay);
4659+
chan_keys.on_funding_created(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay, false, &funding_info);
46604660

46614661
assert_eq!(counterparty_pubkeys.payment_point.serialize()[..],
46624662
hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]);

lightning/src/ln/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub mod peer_handler;
2525
pub mod chan_utils;
2626
pub mod features;
2727
pub(crate) mod onchaintx;
28+
pub mod transaction;
2829

2930
#[cfg(feature = "fuzztarget")]
3031
pub mod peer_channel_encryptor;

0 commit comments

Comments
 (0)