Skip to content

Commit 9d3b4ae

Browse files
committed
draft KeysInterface.sign_holder_commitment_phase2
1 parent 8640173 commit 9d3b4ae

File tree

7 files changed

+339
-10
lines changed

7 files changed

+339
-10
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 147 additions & 5 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
@@ -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
@@ -325,7 +337,12 @@ pub trait ChannelKeys : Send+Clone {
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_accept(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool);
341+
342+
/// Set the funding outpoint.
343+
///
344+
/// Used for re-constructing transactions as part of signing.
345+
fn on_funding_created(&mut self, funding_outpoint: &OutPoint);
329346
}
330347

331348
/// A trait to describe an object which can get user secrets and key material.
@@ -365,6 +382,8 @@ struct AcceptedChannelData {
365382
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
366383
/// if they broadcast a transaction.
367384
holder_selected_contest_delay: u16,
385+
/// Whether the holder is the initiator of this channel
386+
is_outbound: bool,
368387
}
369388

370389
#[derive(Clone)]
@@ -386,6 +405,8 @@ pub struct InMemoryChannelKeys {
386405
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
387406
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
388407
accepted_channel_data: Option<AcceptedChannelData>,
408+
/// Funding outpoint, populated on channel funding creation
409+
funding_outpoint: Option<OutPoint>,
389410
/// The total value of this channel
390411
channel_value_satoshis: u64,
391412
/// Key derivation parameters
@@ -418,6 +439,7 @@ impl InMemoryChannelKeys {
418439
channel_value_satoshis,
419440
holder_channel_pubkeys,
420441
accepted_channel_data: None,
442+
funding_outpoint: None,
421443
key_derivation_params,
422444
}
423445
}
@@ -454,6 +476,77 @@ impl InMemoryChannelKeys {
454476
/// if they broadcast a transaction.
455477
/// Will panic if on_accept wasn't called.
456478
pub fn holder_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().holder_selected_contest_delay }
479+
480+
/// Whether the holder is the initiator
481+
pub fn is_outbound(&self) -> bool { self.accepted_channel_data.as_ref().unwrap().is_outbound }
482+
483+
/// Funding outpoint
484+
/// Will panic if on_funding_created wasn't called.
485+
pub fn funding_outpoint(&self) -> &OutPoint { self.funding_outpoint.as_ref().unwrap() }
486+
487+
/// Prepare build information for a holder-broadcast commitment tx
488+
pub fn build_holder_commitment_info<T: secp256k1::Signing + secp256k1::Verification>(
489+
&self,
490+
per_commitment_point: &PublicKey,
491+
to_holder_value_sat: u64,
492+
to_counterparty_value_sat: u64,
493+
htlcs: &Vec<HTLCOutputInCommitment>,
494+
secp_ctx: &Secp256k1<T>,
495+
) -> Result<CommitmentInfo, ()> {
496+
let counterparty_points = self.counterparty_pubkeys();
497+
498+
let to_holder_delayed_pubkey = derive_public_key(
499+
&secp_ctx,
500+
&per_commitment_point,
501+
&self.pubkeys().delayed_payment_basepoint,
502+
).map_err(|_| ())?;
503+
504+
let revocation_pubkey = derive_public_revocation_key(
505+
&secp_ctx,
506+
&per_commitment_point,
507+
&counterparty_points.revocation_basepoint,
508+
).map_err(|_| ())?;
509+
510+
Ok(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+
522+
fn get_commitment_transaction_number_obscure_factor(&self) -> u64 {
523+
get_commitment_transaction_number_obscure_factor(
524+
&self.pubkeys().payment_point,
525+
&self.counterparty_pubkeys().payment_point,
526+
self.is_outbound(),
527+
)
528+
}
529+
530+
/// Build a commitment tx
531+
pub fn build_commitment_tx<T: secp256k1::Signing + secp256k1::Verification>(&self, per_commitment_point: &PublicKey,
532+
commitment_number: u64, info: &CommitmentInfo,
533+
secp_ctx: &Secp256k1<T>) -> Result<(bitcoin::Transaction, Vec<HTLCOutputInCommitment>, TxCreationKeys, Vec<Script>), ()> {
534+
let keys = if !info.is_counterparty_broadcaster {
535+
TxCreationKeys::make_tx_keys(per_commitment_point, self.pubkeys(), self.counterparty_pubkeys(), secp_ctx)
536+
} else {
537+
TxCreationKeys::make_tx_keys(per_commitment_point, self.counterparty_pubkeys(), self.pubkeys(), secp_ctx)
538+
}.unwrap();
539+
540+
let obscured_commitment_transaction_number =
541+
self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
542+
let (tx, htlcs, scripts) = build_commitment_tx(
543+
&keys,
544+
info,
545+
obscured_commitment_transaction_number,
546+
self.funding_outpoint().into_bitcoin_outpoint(),
547+
);
548+
Ok((tx, htlcs, keys, scripts))
549+
}
457550
}
458551

459552
impl ChannelKeys for InMemoryChannelKeys {
@@ -507,6 +600,47 @@ impl ChannelKeys for InMemoryChannelKeys {
507600
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
508601
}
509602

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

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");
725+
fn on_accept(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool) {
726+
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
593727
self.accepted_channel_data = Some(AcceptedChannelData {
594728
counterparty_channel_pubkeys: channel_pubkeys.clone(),
595729
counterparty_selected_contest_delay,
596730
holder_selected_contest_delay,
731+
is_outbound,
597732
});
598733
}
734+
735+
fn on_funding_created(&mut self, funding_outpoint: &OutPoint) {
736+
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
737+
self.funding_outpoint = Some(funding_outpoint.clone());
738+
}
599739
}
600740

601741
impl_writeable!(AcceptedChannelData, 0,
602-
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
742+
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });
603743

604744
impl Writeable for InMemoryChannelKeys {
605745
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
@@ -627,6 +767,7 @@ impl Readable for InMemoryChannelKeys {
627767
let htlc_base_key = Readable::read(reader)?;
628768
let commitment_seed = Readable::read(reader)?;
629769
let counterparty_channel_data = Readable::read(reader)?;
770+
let funding_outpoint = Readable::read(reader)?;
630771
let channel_value_satoshis = Readable::read(reader)?;
631772
let secp_ctx = Secp256k1::signing_only();
632773
let holder_channel_pubkeys =
@@ -646,6 +787,7 @@ impl Readable for InMemoryChannelKeys {
646787
channel_value_satoshis,
647788
holder_channel_pubkeys,
648789
accepted_channel_data: counterparty_channel_data,
790+
funding_outpoint,
649791
key_derivation_params: (params_1, params_2),
650792
})
651793
}

lightning/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
extern crate bitcoin;
3131
#[cfg(test)] extern crate hex;
3232
#[cfg(test)] extern crate regex;
33+
extern crate core;
3334

3435
#[macro_use]
3536
pub mod util;

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: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
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);
593+
chan_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, config.own_channel_config.our_to_self_delay, false);
594594
let mut local_config = (*config).channel_options.clone();
595595

596596
if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
@@ -1463,7 +1463,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
14631463
htlc_basepoint: msg.htlc_basepoint
14641464
};
14651465

1466-
self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay);
1466+
self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay, true);
14671467
self.counterparty_pubkeys = Some(counterparty_pubkeys);
14681468

14691469
self.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
@@ -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_accept(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay, false);
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)