Skip to content

Commit 5982347

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

File tree

6 files changed

+339
-27
lines changed

6 files changed

+339
-27
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 147 additions & 19 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::ready_channel) 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,15 @@ 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 static channel data, including basepoints,
335+
/// counterparty_selected/holder_selected_contest_delay and funding outpoint.
336+
/// This is done as soon as the funding outpoint is known. Since these are static channel data,
337+
/// they MUST NOT be allowed to change to different values once set.
324338
///
325339
/// We bind holder_selected_contest_delay late here for API convenience.
326340
///
327341
/// 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);
342+
fn ready_channel(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint);
329343
}
330344

331345
/// A trait to describe an object which can get user secrets and key material.
@@ -349,11 +363,11 @@ pub trait KeysInterface: Send + Sync {
349363
}
350364

351365
#[derive(Clone)]
352-
/// Holds late-bound channel data.
353-
/// This data is available after the channel is known to be accepted, either
354-
/// when receiving an open_channel for an inbound channel or when
355-
/// receiving accept_channel for an outbound channel.
356-
struct AcceptedChannelData {
366+
/// Holds late-bound static channel data.
367+
/// This data is available after the channel is readied, either
368+
/// when receiving an funding_created for an inbound channel or when
369+
/// creating a funding transaction for an outbound channel.
370+
struct StaticChannelData {
357371
/// Counterparty public keys and base points
358372
counterparty_channel_pubkeys: ChannelPublicKeys,
359373
/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
@@ -365,6 +379,8 @@ struct AcceptedChannelData {
365379
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
366380
/// if they broadcast a transaction.
367381
holder_selected_contest_delay: u16,
382+
/// Whether the holder is the initiator of this channel
383+
is_outbound: bool,
368384
}
369385

370386
#[derive(Clone)]
@@ -385,7 +401,9 @@ pub struct InMemoryChannelKeys {
385401
/// Holder public keys and basepoints
386402
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
387403
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
388-
accepted_channel_data: Option<AcceptedChannelData>,
404+
accepted_channel_data: Option<StaticChannelData>,
405+
/// Funding outpoint, populated on channel funding creation
406+
funding_outpoint: Option<OutPoint>,
389407
/// The total value of this channel
390408
channel_value_satoshis: u64,
391409
/// Key derivation parameters
@@ -418,6 +436,7 @@ impl InMemoryChannelKeys {
418436
channel_value_satoshis,
419437
holder_channel_pubkeys,
420438
accepted_channel_data: None,
439+
funding_outpoint: None,
421440
key_derivation_params,
422441
}
423442
}
@@ -439,21 +458,84 @@ impl InMemoryChannelKeys {
439458
}
440459

441460
/// Counterparty pubkeys.
442-
/// Will panic if on_accept wasn't called.
461+
/// Will panic if ready_channel wasn't called.
443462
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.accepted_channel_data.as_ref().unwrap().counterparty_channel_pubkeys }
444463

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

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

459541
impl ChannelKeys for InMemoryChannelKeys {
@@ -507,6 +589,46 @@ impl ChannelKeys for InMemoryChannelKeys {
507589
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
508590
}
509591

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

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");
593-
self.accepted_channel_data = Some(AcceptedChannelData {
713+
fn ready_channel(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint) {
714+
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
715+
self.accepted_channel_data = Some(StaticChannelData {
594716
counterparty_channel_pubkeys: channel_pubkeys.clone(),
595717
counterparty_selected_contest_delay,
596718
holder_selected_contest_delay,
719+
is_outbound,
597720
});
721+
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
722+
self.funding_outpoint = Some(funding_outpoint.clone());
598723
}
599724
}
600725

601-
impl_writeable!(AcceptedChannelData, 0,
602-
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
726+
impl_writeable!(StaticChannelData, 0,
727+
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });
603728

604729
impl Writeable for InMemoryChannelKeys {
605730
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
@@ -610,6 +735,7 @@ impl Writeable for InMemoryChannelKeys {
610735
self.htlc_base_key.write(writer)?;
611736
self.commitment_seed.write(writer)?;
612737
self.accepted_channel_data.write(writer)?;
738+
self.funding_outpoint.write(writer)?;
613739
self.channel_value_satoshis.write(writer)?;
614740
self.key_derivation_params.0.write(writer)?;
615741
self.key_derivation_params.1.write(writer)?;
@@ -627,6 +753,7 @@ impl Readable for InMemoryChannelKeys {
627753
let htlc_base_key = Readable::read(reader)?;
628754
let commitment_seed = Readable::read(reader)?;
629755
let counterparty_channel_data = Readable::read(reader)?;
756+
let funding_outpoint = Readable::read(reader)?;
630757
let channel_value_satoshis = Readable::read(reader)?;
631758
let secp_ctx = Secp256k1::signing_only();
632759
let holder_channel_pubkeys =
@@ -646,6 +773,7 @@ impl Readable for InMemoryChannelKeys {
646773
channel_value_satoshis,
647774
holder_channel_pubkeys,
648775
accepted_channel_data: counterparty_channel_data,
776+
funding_outpoint,
649777
key_derivation_params: (params_1, params_2),
650778
})
651779
}

lightning/src/ln/chan_utils.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,8 @@ impl_writeable!(ChannelPublicKeys, 33*5, {
372372

373373

374374
impl TxCreationKeys {
375-
/// Create a new TxCreationKeys from channel base points and the per-commitment point
375+
/// Create per-state keys from channel base points and the per-commitment point
376+
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
376377
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
377378
Ok(TxCreationKeys {
378379
per_commitment_point: per_commitment_point.clone(),
@@ -382,6 +383,19 @@ impl TxCreationKeys {
382383
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
383384
})
384385
}
386+
387+
/// Generate per-state keys from channel static keys.
388+
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
389+
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> {
390+
TxCreationKeys::derive_new(
391+
&secp_ctx,
392+
&per_commitment_point,
393+
&broadcaster_keys.delayed_payment_basepoint,
394+
&broadcaster_keys.htlc_basepoint,
395+
&countersignatory_keys.revocation_basepoint,
396+
&countersignatory_keys.htlc_basepoint,
397+
)
398+
}
385399
}
386400

387401
/// 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.ready_channel(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.ready_channel(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.ready_channel(&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)