From: Devrandom Date: Fri, 17 Jan 2020 00:51:15 +0000 (-0800) Subject: Provide remote channel public keys to signer X-Git-Tag: v0.0.12~154^2~2 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=42d773823447bd219943cc795ab71f2915c24df7;p=rust-lightning Provide remote channel public keys to signer --- diff --git a/.gitignore b/.gitignore index c795d9b53..19ed4bd11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target/ +/hfuzz_target/ /net-tokio/target/ **/*.rs.bk Cargo.lock diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index d4a33427e..382886492 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -158,7 +158,7 @@ impl KeysInterface for KeyProvider { delayed_payment_base_key: SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, self.node_id]).unwrap(), htlc_base_key: SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, self.node_id]).unwrap(), commitment_seed: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, self.node_id], - remote_funding_pubkey: None, + remote_channel_pubkeys: None, }) } diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 7e6135b5c..a98c29bc8 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -257,7 +257,7 @@ impl KeysInterface for KeyProvider { delayed_payment_base_key: SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, ctr]).unwrap(), htlc_base_key: SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, ctr]).unwrap(), commitment_seed: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, ctr], - remote_funding_pubkey: None, + remote_channel_pubkeys: None, } } else { InMemoryChannelKeys { @@ -267,7 +267,7 @@ impl KeysInterface for KeyProvider { delayed_payment_base_key: SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, ctr]).unwrap(), htlc_base_key: SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, ctr]).unwrap(), commitment_seed: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, ctr], - remote_funding_pubkey: None, + remote_channel_pubkeys: None, } }) } diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index c00164626..689a16603 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -24,7 +24,7 @@ use util::logger::Logger; use util::ser::Writeable; use ln::chan_utils; -use ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment, make_funding_redeemscript}; +use ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys}; use ln::msgs; use std::sync::Arc; @@ -142,7 +142,7 @@ pub trait ChannelKeys : Send { /// TODO: Document the things someone using this interface should enforce before signing. /// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and /// making the callee generate it via some util function we expose)! - fn sign_remote_commitment(&self, channel_value_satoshis: u64, feerate_per_kw: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()>; + fn sign_remote_commitment(&self, channel_value_satoshis: u64, feerate_per_kw: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()>; /// Create a signature for a (proposed) closing transaction. /// @@ -158,11 +158,11 @@ pub trait ChannelKeys : Send { /// protocol. fn sign_channel_announcement(&self, msg: &msgs::UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) -> Result; - /// Set the remote funding key. This is done immediately on incoming channels + /// Set the remote channel basepoints. This is done immediately on incoming channels /// and as soon as the channel is accepted on outgoing channels. /// /// Will be called before any signatures are applied. - fn set_remote_funding_pubkey(&mut self, key: &PublicKey); + fn set_remote_channel_pubkeys(&mut self, channel_points: &ChannelPublicKeys); } #[derive(Clone)] @@ -181,7 +181,7 @@ pub struct InMemoryChannelKeys { /// Commitment seed pub commitment_seed: [u8; 32], /// Remote funding pubkey - pub remote_funding_pubkey: Option, + pub remote_channel_pubkeys: Option, } impl ChannelKeys for InMemoryChannelKeys { @@ -192,12 +192,12 @@ impl ChannelKeys for InMemoryChannelKeys { fn htlc_base_key(&self) -> &SecretKey { &self.htlc_base_key } fn commitment_seed(&self) -> &[u8; 32] { &self.commitment_seed } - fn sign_remote_commitment(&self, channel_value_satoshis: u64, feerate_per_kw: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { + fn sign_remote_commitment(&self, channel_value_satoshis: u64, feerate_per_kw: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { if commitment_tx.input.len() != 1 { return Err(()); } let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); - let remote_funding_pubkey = self.remote_funding_pubkey.as_ref().expect("must set remote funding key before signing"); - let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, remote_funding_pubkey); + let remote_channel_pubkeys = self.remote_channel_pubkeys.as_ref().expect("must set remote channel pubkeys before signing"); + let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &remote_channel_pubkeys.funding_pubkey); let commitment_sighash = hash_to_message!(&bip143::SighashComponents::new(&commitment_tx).sighash_all(&commitment_tx.input[0], &channel_funding_redeemscript, channel_value_satoshis)[..]); let commitment_sig = secp_ctx.sign(&commitment_sighash, &self.funding_key); @@ -236,9 +236,9 @@ impl ChannelKeys for InMemoryChannelKeys { Ok(secp_ctx.sign(&msghash, &self.funding_key)) } - fn set_remote_funding_pubkey(&mut self, key: &PublicKey) { - assert!(self.remote_funding_pubkey.is_none(), "Already set remote funding key"); - self.remote_funding_pubkey = Some(*key); + fn set_remote_channel_pubkeys(&mut self, channel_pubkeys: &ChannelPublicKeys) { + assert!(self.remote_channel_pubkeys.is_none(), "Already set remote channel pubkeys"); + self.remote_channel_pubkeys = Some(channel_pubkeys.clone()); } } @@ -249,7 +249,7 @@ impl_writeable!(InMemoryChannelKeys, 0, { delayed_payment_base_key, htlc_base_key, commitment_seed, - remote_funding_pubkey + remote_channel_pubkeys }); /// Simple KeysInterface implementor that takes a 32-byte seed for use as a BIP 32 extended key @@ -398,7 +398,7 @@ impl KeysInterface for KeysManager { delayed_payment_base_key, htlc_base_key, commitment_seed, - remote_funding_pubkey: None, + remote_channel_pubkeys: None, } } diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 19a07aaa1..5338c06c5 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -118,6 +118,7 @@ pub(super) fn derive_public_revocation_key(secp_ctx: /// The set of public keys which are used in the creation of one commitment transaction. /// These are derived from the channel base keys and per-commitment data. +#[derive(PartialEq)] pub struct TxCreationKeys { /// The per-commitment public key which was used to derive the other keys. pub per_commitment_point: PublicKey, @@ -134,8 +135,40 @@ pub struct TxCreationKeys { pub b_payment_key: PublicKey, } +/// One counterparty's public keys which do not change over the life of a channel. +#[derive(Clone)] +pub struct ChannelPublicKeys { + /// The public key which is used to sign all commitment transactions, as it appears in the + /// on-chain channel lock-in 2-of-2 multisig output. + pub funding_pubkey: PublicKey, + /// The base point which is used (with derive_public_revocation_key) to derive per-commitment + /// revocation keys. The per-commitment revocation private key is then revealed by the owner of + /// a commitment transaction so that their counterparty can claim all available funds if they + /// broadcast an old state. + pub revocation_basepoint: PublicKey, + /// The base point which is used (with derive_public_key) to derive a per-commitment payment + /// public key which receives immediately-spendable non-HTLC-encumbered funds. + pub payment_basepoint: PublicKey, + /// The base point which is used (with derive_public_key) to derive a per-commitment payment + /// public key which receives non-HTLC-encumbered funds which are only available for spending + /// after some delay (or can be claimed via the revocation path). + pub delayed_payment_basepoint: PublicKey, + /// The base point which is used (with derive_public_key) to derive a per-commitment public key + /// which is used to encumber HTLC-in-flight outputs. + pub htlc_basepoint: PublicKey, +} + +impl_writeable!(ChannelPublicKeys, 33*5, { + funding_pubkey, + revocation_basepoint, + payment_basepoint, + delayed_payment_basepoint, + htlc_basepoint +}); + + impl TxCreationKeys { - pub(super) fn new(secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, a_delayed_payment_base: &PublicKey, a_htlc_base: &PublicKey, b_revocation_base: &PublicKey, b_payment_base: &PublicKey, b_htlc_base: &PublicKey) -> Result { + pub(crate) fn new(secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, a_delayed_payment_base: &PublicKey, a_htlc_base: &PublicKey, b_revocation_base: &PublicKey, b_payment_base: &PublicKey, b_htlc_base: &PublicKey) -> Result { Ok(TxCreationKeys { per_commitment_point: per_commitment_point.clone(), revocation_key: derive_public_revocation_key(&secp_ctx, &per_commitment_point, &b_revocation_base)?, diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 3d8962b4f..a9a887640 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -20,7 +20,7 @@ use ln::msgs; use ln::msgs::{DecodeError, OptionalField, DataLossProtect}; use ln::channelmonitor::ChannelMonitor; use ln::channelmanager::{PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingForwardHTLCInfo, RAACommitmentOrder, PaymentPreimage, PaymentHash, BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT}; -use ln::chan_utils::{LocalCommitmentTransaction, TxCreationKeys, HTLCOutputInCommitment, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, make_funding_redeemscript}; +use ln::chan_utils::{LocalCommitmentTransaction, TxCreationKeys, HTLCOutputInCommitment, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, make_funding_redeemscript, ChannelPublicKeys}; use ln::chan_utils; use chain::chaininterface::{FeeEstimator,ConfirmationTarget}; use chain::transaction::OutPoint; @@ -546,7 +546,14 @@ impl Channel { /// Assumes chain_hash has already been checked and corresponds with what we expect! pub fn new_from_req(fee_estimator: &FeeEstimator, keys_provider: &Arc>, their_node_id: PublicKey, their_features: InitFeatures, msg: &msgs::OpenChannel, user_id: u64, logger: Arc, config: &UserConfig) -> Result, ChannelError> { let mut chan_keys = keys_provider.get_channel_keys(true); - chan_keys.set_remote_funding_pubkey(&msg.funding_pubkey); + let channel_pubkeys = ChannelPublicKeys { + funding_pubkey: msg.funding_pubkey, + revocation_basepoint: msg.revocation_basepoint, + payment_basepoint: msg.payment_basepoint, + delayed_payment_basepoint: msg.delayed_payment_basepoint, + htlc_basepoint: msg.htlc_basepoint + }; + chan_keys.set_remote_channel_pubkeys(&channel_pubkeys); let mut local_config = (*config).channel_options.clone(); if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT { @@ -1401,7 +1408,14 @@ impl Channel { self.channel_monitor.set_basic_channel_info(&msg.htlc_basepoint, &msg.delayed_payment_basepoint, msg.to_self_delay, funding_redeemscript, self.channel_value_satoshis, obscure_factor); self.channel_state = ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32; - self.local_keys.set_remote_funding_pubkey(&msg.funding_pubkey); + let channel_pubkeys = ChannelPublicKeys { + funding_pubkey: msg.funding_pubkey, + revocation_basepoint: msg.revocation_basepoint, + payment_basepoint: msg.payment_basepoint, + delayed_payment_basepoint: msg.delayed_payment_basepoint, + htlc_basepoint: msg.htlc_basepoint + }; + self.local_keys.set_remote_channel_pubkeys(&channel_pubkeys); Ok(()) } @@ -4127,7 +4141,7 @@ mod tests { // These aren't set in the test vectors: revocation_base_key: SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(), commitment_seed: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], - remote_funding_pubkey: None, + remote_channel_pubkeys: None, }; assert_eq!(PublicKey::from_secret_key(&secp_ctx, chan_keys.funding_key()).serialize()[..], hex::decode("023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb").unwrap()[..]); diff --git a/lightning/src/util/enforcing_trait_impls.rs b/lightning/src/util/enforcing_trait_impls.rs index ee90fe7ac..e5b5a3179 100644 --- a/lightning/src/util/enforcing_trait_impls.rs +++ b/lightning/src/util/enforcing_trait_impls.rs @@ -1,4 +1,4 @@ -use ln::chan_utils::{HTLCOutputInCommitment, TxCreationKeys}; +use ln::chan_utils::{HTLCOutputInCommitment, TxCreationKeys, ChannelPublicKeys}; use ln::msgs; use chain::keysinterface::{ChannelKeys, InMemoryChannelKeys}; @@ -27,6 +27,27 @@ impl EnforcingChannelKeys { } } } + +impl EnforcingChannelKeys { + fn check_keys(&self, secp_ctx: &Secp256k1, + keys: &TxCreationKeys) { + let revocation_base = PublicKey::from_secret_key(secp_ctx, &self.inner.revocation_base_key); + let payment_base = PublicKey::from_secret_key(secp_ctx, &self.inner.payment_base_key); + let htlc_base = PublicKey::from_secret_key(secp_ctx, &self.inner.htlc_base_key); + + let remote_points = self.inner.remote_channel_pubkeys.as_ref().unwrap(); + + let keys_expected = TxCreationKeys::new(secp_ctx, + &keys.per_commitment_point, + &remote_points.delayed_payment_basepoint, + &remote_points.htlc_basepoint, + &revocation_base, + &payment_base, + &htlc_base).unwrap(); + if keys != &keys_expected { panic!("derived different per-tx keys") } + } +} + impl ChannelKeys for EnforcingChannelKeys { fn funding_key(&self) -> &SecretKey { self.inner.funding_key() } fn revocation_base_key(&self) -> &SecretKey { self.inner.revocation_base_key() } @@ -35,8 +56,9 @@ impl ChannelKeys for EnforcingChannelKeys { fn htlc_base_key(&self) -> &SecretKey { self.inner.htlc_base_key() } fn commitment_seed(&self) -> &[u8; 32] { self.inner.commitment_seed() } - fn sign_remote_commitment(&self, channel_value_satoshis: u64, feerate_per_kw: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { - if commitment_tx.input.len() != 1 { panic!(); } + fn sign_remote_commitment(&self, channel_value_satoshis: u64, feerate_per_kw: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { + if commitment_tx.input.len() != 1 { panic!("lightning commitment transactions have a single input"); } + self.check_keys(secp_ctx, keys); let obscured_commitment_transaction_number = (commitment_tx.lock_time & 0xffffff) as u64 | ((commitment_tx.input[0].sequence as u64 & 0xffffff) << 3*8); { @@ -60,11 +82,12 @@ impl ChannelKeys for EnforcingChannelKeys { self.inner.sign_channel_announcement(msg, secp_ctx) } - fn set_remote_funding_pubkey(&mut self, key: &PublicKey) { - self.inner.set_remote_funding_pubkey(key) + fn set_remote_channel_pubkeys(&mut self, channel_pubkeys: &ChannelPublicKeys) { + self.inner.set_remote_channel_pubkeys(channel_pubkeys) } } + impl_writeable!(EnforcingChannelKeys, 0, { inner, commitment_number_obscure_and_last