From: Matt Corallo <649246+TheBlueMatt@users.noreply.github.com> Date: Mon, 20 Jan 2020 03:46:00 +0000 (+0000) Subject: Merge pull request #451 from lightning-signer/txkeys X-Git-Tag: v0.0.12~154 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=f263b3793f4ee593e3358b68c8822524faecb9f1;hp=0b5b2828f980b615187fae05a950cc41e4fb186b;p=rust-lightning Merge pull request #451 from lightning-signer/txkeys Provide remote channel public keys to signer --- diff --git a/.gitignore b/.gitignore index c795d9b5..19ed4bd1 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 d4a33427..38288649 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 e6496125..cd855f36 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 c0016462..689a1660 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 26354720..51a2c4b0 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -18,7 +18,7 @@ use ln::channelmanager::{PaymentHash, PaymentPreimage}; use ln::msgs::DecodeError; use util::ser::{Readable, Writeable, Writer, WriterWriteAdaptor}; -use secp256k1::key::{SecretKey,PublicKey}; +use secp256k1::key::{SecretKey, PublicKey}; use secp256k1::{Secp256k1, Signature}; use secp256k1; @@ -137,24 +137,57 @@ 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, /// The revocation key which is used to allow the owner of the commitment transaction to /// provide their counterparty the ability to punish them if they broadcast an old state. - pub revocation_key: PublicKey, + pub(crate) revocation_key: PublicKey, /// A's HTLC Key - pub a_htlc_key: PublicKey, + pub(crate) a_htlc_key: PublicKey, /// B's HTLC Key - pub b_htlc_key: PublicKey, + pub(crate) b_htlc_key: PublicKey, /// A's Payment Key (which isn't allowed to be spent from for some delay) - pub a_delayed_payment_key: PublicKey, + pub(crate) a_delayed_payment_key: PublicKey, /// B's Payment Key - pub b_payment_key: PublicKey, + pub(crate) 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 d2a0cae9..02b1ae4e 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; @@ -335,11 +335,8 @@ pub(super) struct Channel { //implied by OUR_MAX_HTLCS: our_max_accepted_htlcs: u16, minimum_depth: u32, - their_funding_pubkey: Option, - their_revocation_basepoint: Option, - their_payment_basepoint: Option, - their_delayed_payment_basepoint: Option, - their_htlc_basepoint: Option, + their_pubkeys: Option, + their_cur_commitment_point: Option, their_prev_commitment_point: Option, @@ -506,11 +503,7 @@ impl Channel { their_max_accepted_htlcs: 0, minimum_depth: 0, // Filled in in accept_channel - their_funding_pubkey: None, - their_revocation_basepoint: None, - their_payment_basepoint: None, - their_delayed_payment_basepoint: None, - their_htlc_basepoint: None, + their_pubkeys: None, their_cur_commitment_point: None, their_prev_commitment_point: None, @@ -540,7 +533,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 their_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(&their_pubkeys); let mut local_config = (*config).channel_options.clone(); if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT { @@ -722,11 +722,7 @@ impl Channel { their_max_accepted_htlcs: msg.max_accepted_htlcs, minimum_depth: config.own_channel_config.minimum_depth, - their_funding_pubkey: Some(msg.funding_pubkey), - their_revocation_basepoint: Some(msg.revocation_basepoint), - their_payment_basepoint: Some(msg.payment_basepoint), - their_delayed_payment_basepoint: Some(msg.delayed_payment_basepoint), - their_htlc_basepoint: Some(msg.htlc_basepoint), + their_pubkeys: Some(their_pubkeys), their_cur_commitment_point: Some(msg.first_per_commitment_point), their_prev_commitment_point: None, @@ -761,11 +757,12 @@ impl Channel { let mut sha = Sha256::engine(); let our_payment_basepoint = PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.payment_base_key()); + let their_payment_basepoint = &self.their_pubkeys.as_ref().unwrap().payment_basepoint.serialize(); if self.channel_outbound { sha.input(&our_payment_basepoint.serialize()); - sha.input(&self.their_payment_basepoint.unwrap().serialize()); + sha.input(their_payment_basepoint); } else { - sha.input(&self.their_payment_basepoint.unwrap().serialize()); + sha.input(their_payment_basepoint); sha.input(&our_payment_basepoint.serialize()); } let res = Sha256::from_engine(sha).into_inner(); @@ -1084,8 +1081,9 @@ impl Channel { let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &self.build_local_commitment_secret(commitment_number)); let delayed_payment_base = PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.delayed_payment_base_key()); let htlc_basepoint = PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.htlc_base_key()); + let their_pubkeys = self.their_pubkeys.as_ref().unwrap(); - Ok(secp_check!(TxCreationKeys::new(&self.secp_ctx, &per_commitment_point, &delayed_payment_base, &htlc_basepoint, &self.their_revocation_basepoint.unwrap(), &self.their_payment_basepoint.unwrap(), &self.their_htlc_basepoint.unwrap()), "Local tx keys generation got bogus keys")) + Ok(secp_check!(TxCreationKeys::new(&self.secp_ctx, &per_commitment_point, &delayed_payment_base, &htlc_basepoint, &their_pubkeys.revocation_basepoint, &their_pubkeys.payment_basepoint, &their_pubkeys.htlc_basepoint), "Local tx keys generation got bogus keys")) } #[inline] @@ -1098,8 +1096,9 @@ impl Channel { let payment_basepoint = PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.payment_base_key()); let revocation_basepoint = PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.revocation_base_key()); let htlc_basepoint = PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.htlc_base_key()); + let their_pubkeys = self.their_pubkeys.as_ref().unwrap(); - Ok(secp_check!(TxCreationKeys::new(&self.secp_ctx, &self.their_cur_commitment_point.unwrap(), &self.their_delayed_payment_basepoint.unwrap(), &self.their_htlc_basepoint.unwrap(), &revocation_basepoint, &payment_basepoint, &htlc_basepoint), "Remote tx keys generation got bogus keys")) + Ok(secp_check!(TxCreationKeys::new(&self.secp_ctx, &self.their_cur_commitment_point.unwrap(), &their_pubkeys.delayed_payment_basepoint, &their_pubkeys.htlc_basepoint, &revocation_basepoint, &payment_basepoint, &htlc_basepoint), "Remote tx keys generation got bogus keys")) } /// Gets the redeemscript for the funding transaction output (ie the funding transaction output @@ -1107,8 +1106,7 @@ impl Channel { /// Panics if called before accept_channel/new_from_req pub fn get_funding_redeemscript(&self) -> Script { let our_funding_key = PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()); - let their_funding_key = self.their_funding_pubkey.expect("get_funding_redeemscript only allowed after accept_channel"); - make_funding_redeemscript(&our_funding_key, &their_funding_key) + make_funding_redeemscript(&our_funding_key, self.their_funding_pubkey()) } /// Builds the htlc-success or htlc-timeout transaction which spends a given HTLC output @@ -1382,11 +1380,18 @@ impl Channel { self.their_to_self_delay = msg.to_self_delay; self.their_max_accepted_htlcs = msg.max_accepted_htlcs; self.minimum_depth = msg.minimum_depth; - self.their_funding_pubkey = Some(msg.funding_pubkey); - self.their_revocation_basepoint = Some(msg.revocation_basepoint); - self.their_payment_basepoint = Some(msg.payment_basepoint); - self.their_delayed_payment_basepoint = Some(msg.delayed_payment_basepoint); - self.their_htlc_basepoint = Some(msg.htlc_basepoint); + + let their_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(&their_pubkeys); + self.their_pubkeys = Some(their_pubkeys); + self.their_cur_commitment_point = Some(msg.first_per_commitment_point); self.their_shutdown_scriptpubkey = their_shutdown_scriptpubkey; @@ -1395,7 +1400,6 @@ 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); Ok(()) } @@ -1408,9 +1412,9 @@ impl Channel { let local_sighash = hash_to_message!(&bip143::SighashComponents::new(&local_initial_commitment_tx).sighash_all(&local_initial_commitment_tx.input[0], &funding_script, self.channel_value_satoshis)[..]); // They sign the "local" commitment transaction... - secp_check!(self.secp_ctx.verify(&local_sighash, &sig, &self.their_funding_pubkey.unwrap()), "Invalid funding_created signature from peer"); + secp_check!(self.secp_ctx.verify(&local_sighash, &sig, self.their_funding_pubkey()), "Invalid funding_created signature from peer"); - let localtx = LocalCommitmentTransaction::new_missing_local_sig(local_initial_commitment_tx, sig, &PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()), self.their_funding_pubkey.as_ref().unwrap()); + let localtx = LocalCommitmentTransaction::new_missing_local_sig(local_initial_commitment_tx, sig, &PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()), self.their_funding_pubkey()); let remote_keys = self.build_remote_transaction_keys()?; let remote_initial_commitment_tx = self.build_commitment_transaction(self.cur_remote_commitment_transaction_number, &remote_keys, false, false, self.feerate_per_kw).0; @@ -1421,6 +1425,10 @@ impl Channel { Ok((remote_initial_commitment_tx, localtx, remote_signature, local_keys)) } + fn their_funding_pubkey(&self) -> &PublicKey { + &self.their_pubkeys.as_ref().expect("their_funding_pubkey() only allowed after accept_channel").funding_pubkey + } + pub fn funding_created(&mut self, msg: &msgs::FundingCreated) -> Result<(msgs::FundingSigned, ChannelMonitor), ChannelError> { if self.channel_outbound { return Err(ChannelError::Close("Received funding_created for an outbound channel?")); @@ -1485,11 +1493,13 @@ impl Channel { let local_initial_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false, self.feerate_per_kw).0; let local_sighash = hash_to_message!(&bip143::SighashComponents::new(&local_initial_commitment_tx).sighash_all(&local_initial_commitment_tx.input[0], &funding_script, self.channel_value_satoshis)[..]); + let their_funding_pubkey = &self.their_pubkeys.as_ref().unwrap().funding_pubkey; + // They sign the "local" commitment transaction, allowing us to broadcast the tx if we wish. - secp_check!(self.secp_ctx.verify(&local_sighash, &msg.signature, &self.their_funding_pubkey.unwrap()), "Invalid funding_signed signature from peer"); + secp_check!(self.secp_ctx.verify(&local_sighash, &msg.signature, their_funding_pubkey), "Invalid funding_signed signature from peer"); self.channel_monitor.provide_latest_local_commitment_tx_info( - LocalCommitmentTransaction::new_missing_local_sig(local_initial_commitment_tx, &msg.signature, &PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()), self.their_funding_pubkey.as_ref().unwrap()), + LocalCommitmentTransaction::new_missing_local_sig(local_initial_commitment_tx, &msg.signature, &PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()), their_funding_pubkey), local_keys, self.feerate_per_kw, Vec::new()); self.channel_state = ChannelState::FundingSent as u32 | (self.channel_state & (ChannelState::MonitorUpdateFailed as u32)); self.cur_local_commitment_transaction_number -= 1; @@ -1737,8 +1747,8 @@ impl Channel { }; let local_commitment_txid = local_commitment_tx.0.txid(); let local_sighash = hash_to_message!(&bip143::SighashComponents::new(&local_commitment_tx.0).sighash_all(&local_commitment_tx.0.input[0], &funding_script, self.channel_value_satoshis)[..]); - log_trace!(self, "Checking commitment tx signature {} by key {} against tx {} with redeemscript {}", log_bytes!(msg.signature.serialize_compact()[..]), log_bytes!(self.their_funding_pubkey.unwrap().serialize()), encode::serialize_hex(&local_commitment_tx.0), encode::serialize_hex(&funding_script)); - secp_check!(self.secp_ctx.verify(&local_sighash, &msg.signature, &self.their_funding_pubkey.unwrap()), "Invalid commitment tx signature from peer"); + log_trace!(self, "Checking commitment tx signature {} by key {} against tx {} with redeemscript {}", log_bytes!(msg.signature.serialize_compact()[..]), log_bytes!(self.their_funding_pubkey().serialize()), encode::serialize_hex(&local_commitment_tx.0), encode::serialize_hex(&funding_script)); + secp_check!(self.secp_ctx.verify(&local_sighash, &msg.signature, &self.their_funding_pubkey()), "Invalid commitment tx signature from peer"); //If channel fee was updated by funder confirm funder can afford the new fee rate when applied to the current local commitment transaction if update_fee { @@ -1786,9 +1796,10 @@ impl Channel { } } + let their_funding_pubkey = self.their_pubkeys.as_ref().unwrap().funding_pubkey; self.channel_monitor.provide_latest_local_commitment_tx_info( - LocalCommitmentTransaction::new_missing_local_sig(local_commitment_tx.0, &msg.signature, &PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()), self.their_funding_pubkey.as_ref().unwrap()), + LocalCommitmentTransaction::new_missing_local_sig(local_commitment_tx.0, &msg.signature, &PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()), &their_funding_pubkey), local_keys, self.feerate_per_kw, htlcs_and_sigs); for htlc in self.pending_inbound_htlcs.iter_mut() { @@ -2646,7 +2657,7 @@ impl Channel { tx.input[0].witness.push(Vec::new()); // First is the multisig dummy let our_funding_key = PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()).serialize(); - let their_funding_key = self.their_funding_pubkey.unwrap().serialize(); + let their_funding_key = self.their_funding_pubkey().serialize(); if our_funding_key[..] < their_funding_key[..] { tx.input[0].witness.push(our_sig.serialize_der().to_vec()); tx.input[0].witness.push(their_sig.serialize_der().to_vec()); @@ -2681,14 +2692,16 @@ impl Channel { } let mut sighash = hash_to_message!(&bip143::SighashComponents::new(&closing_tx).sighash_all(&closing_tx.input[0], &funding_redeemscript, self.channel_value_satoshis)[..]); - match self.secp_ctx.verify(&sighash, &msg.signature, &self.their_funding_pubkey.unwrap()) { + let their_funding_pubkey = &self.their_pubkeys.as_ref().unwrap().funding_pubkey; + + match self.secp_ctx.verify(&sighash, &msg.signature, their_funding_pubkey) { Ok(_) => {}, Err(_e) => { // The remote end may have decided to revoke their output due to inconsistent dust // limits, so check for that case by re-checking the signature here. closing_tx = self.build_closing_transaction(msg.fee_satoshis, true).0; sighash = hash_to_message!(&bip143::SighashComponents::new(&closing_tx).sighash_all(&closing_tx.input[0], &funding_redeemscript, self.channel_value_satoshis)[..]); - secp_check!(self.secp_ctx.verify(&sighash, &msg.signature, &self.their_funding_pubkey.unwrap()), "Invalid closing tx signature from peer"); + secp_check!(self.secp_ctx.verify(&sighash, &msg.signature, self.their_funding_pubkey()), "Invalid closing tx signature from peer"); }, }; @@ -3221,8 +3234,8 @@ impl Channel { short_channel_id: self.get_short_channel_id().unwrap(), node_id_1: if were_node_one { our_node_id } else { self.get_their_node_id() }, node_id_2: if were_node_one { self.get_their_node_id() } else { our_node_id }, - bitcoin_key_1: if were_node_one { our_bitcoin_key } else { self.their_funding_pubkey.unwrap() }, - bitcoin_key_2: if were_node_one { self.their_funding_pubkey.unwrap() } else { our_bitcoin_key }, + bitcoin_key_1: if were_node_one { our_bitcoin_key } else { self.their_funding_pubkey().clone() }, + bitcoin_key_2: if were_node_one { self.their_funding_pubkey().clone() } else { our_bitcoin_key }, excess_data: Vec::new(), }; @@ -3785,11 +3798,7 @@ impl Writeable for Channel { self.their_max_accepted_htlcs.write(writer)?; self.minimum_depth.write(writer)?; - write_option!(self.their_funding_pubkey); - write_option!(self.their_revocation_basepoint); - write_option!(self.their_payment_basepoint); - write_option!(self.their_delayed_payment_basepoint); - write_option!(self.their_htlc_basepoint); + write_option!(self.their_pubkeys); write_option!(self.their_cur_commitment_point); write_option!(self.their_prev_commitment_point); @@ -3939,11 +3948,7 @@ impl> ReadableArgs> ReadableArgs [u8; 32] { [0; 32] } } + fn public_from_secret_hex(secp_ctx: &Secp256k1, hex: &str) -> PublicKey { + PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode(hex).unwrap()[..]).unwrap()) + } + #[test] fn outbound_commitment_test() { // Test vectors from BOLT 3 Appendix C: @@ -4121,7 +4126,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()[..]); @@ -4137,19 +4142,22 @@ mod tests { let funding_info = OutPoint::new(Sha256dHash::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), 0); chan.channel_monitor.set_funding_info((funding_info, Script::new())); - chan.their_payment_basepoint = Some(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode("4444444444444444444444444444444444444444444444444444444444444444").unwrap()[..]).unwrap())); - assert_eq!(chan.their_payment_basepoint.unwrap().serialize()[..], - hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]); + let their_pubkeys = ChannelPublicKeys { + funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"), + revocation_basepoint: PublicKey::from_slice(&hex::decode("02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27").unwrap()[..]).unwrap(), + payment_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444"), + delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"), + htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444") + }; - chan.their_funding_pubkey = Some(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode("1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13").unwrap()[..]).unwrap())); - assert_eq!(chan.their_funding_pubkey.unwrap().serialize()[..], - hex::decode("030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c1").unwrap()[..]); + assert_eq!(their_pubkeys.payment_basepoint.serialize()[..], + hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]); - chan.their_htlc_basepoint = Some(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode("4444444444444444444444444444444444444444444444444444444444444444").unwrap()[..]).unwrap())); - assert_eq!(chan.their_htlc_basepoint.unwrap().serialize()[..], - hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]); + assert_eq!(their_pubkeys.funding_pubkey.serialize()[..], + hex::decode("030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c1").unwrap()[..]); - chan.their_revocation_basepoint = Some(PublicKey::from_slice(&hex::decode("02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27").unwrap()[..]).unwrap()); + assert_eq!(their_pubkeys.htlc_basepoint.serialize()[..], + hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]); // We can't just use build_local_transaction_keys here as the per_commitment_secret is not // derived from a commitment_seed, so instead we copy it here and call @@ -4158,7 +4166,9 @@ mod tests { let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap(); let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret); let htlc_basepoint = PublicKey::from_secret_key(&secp_ctx, chan.local_keys.htlc_base_key()); - let keys = TxCreationKeys::new(&secp_ctx, &per_commitment_point, &delayed_payment_base, &htlc_basepoint, &chan.their_revocation_basepoint.unwrap(), &chan.their_payment_basepoint.unwrap(), &chan.their_htlc_basepoint.unwrap()).unwrap(); + let keys = TxCreationKeys::new(&secp_ctx, &per_commitment_point, &delayed_payment_base, &htlc_basepoint, &their_pubkeys.revocation_basepoint, &their_pubkeys.payment_basepoint, &their_pubkeys.htlc_basepoint).unwrap(); + + chan.their_pubkeys = Some(their_pubkeys); let mut unsigned_tx: (Transaction, Vec); @@ -4174,9 +4184,9 @@ mod tests { let redeemscript = chan.get_funding_redeemscript(); let their_signature = Signature::from_der(&hex::decode($their_sig_hex).unwrap()[..]).unwrap(); let sighash = Message::from_slice(&bip143::SighashComponents::new(&unsigned_tx.0).sighash_all(&unsigned_tx.0.input[0], &redeemscript, chan.channel_value_satoshis)[..]).unwrap(); - secp_ctx.verify(&sighash, &their_signature, &chan.their_funding_pubkey.unwrap()).unwrap(); + secp_ctx.verify(&sighash, &their_signature, chan.their_funding_pubkey()).unwrap(); - let mut localtx = LocalCommitmentTransaction::new_missing_local_sig(unsigned_tx.0.clone(), &their_signature, &PublicKey::from_secret_key(&secp_ctx, chan.local_keys.funding_key()), chan.their_funding_pubkey.as_ref().unwrap()); + let mut localtx = LocalCommitmentTransaction::new_missing_local_sig(unsigned_tx.0.clone(), &their_signature, &PublicKey::from_secret_key(&secp_ctx, chan.local_keys.funding_key()), chan.their_funding_pubkey()); localtx.add_local_sig(chan.local_keys.funding_key(), &redeemscript, chan.channel_value_satoshis, &chan.secp_ctx); assert_eq!(serialize(localtx.with_valid_witness())[..], diff --git a/lightning/src/util/enforcing_trait_impls.rs b/lightning/src/util/enforcing_trait_impls.rs index ee90fe7a..e5b5a317 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