From 6200302dc790bc3aeb1095c783e039272393b445 Mon Sep 17 00:00:00 2001 From: Devrandom Date: Thu, 9 Jan 2020 17:28:48 -0800 Subject: [PATCH] construct funding redeem script in signer --- fuzz/src/chanmon_consistency.rs | 1 + fuzz/src/full_stack.rs | 2 ++ lightning/src/chain/keysinterface.rs | 28 ++++++++++++++++++--- lightning/src/ln/chan_utils.rs | 16 ++++++++++++ lightning/src/ln/channel.rs | 27 +++++++++----------- lightning/src/util/enforcing_trait_impls.rs | 10 +++++--- 6 files changed, 62 insertions(+), 22 deletions(-) diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 9e2dbf359..d4a33427e 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -158,6 +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, }) } diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 6ed7263a7..7e6135b5c 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -257,6 +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, } } else { InMemoryChannelKeys { @@ -266,6 +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, } }) } diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index fae609cc8..c00164626 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}; +use ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment, make_funding_redeemscript}; 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, channel_funding_redeemscript: &Script, 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. /// @@ -157,6 +157,12 @@ pub trait ChannelKeys : Send { /// our counterparty may (though likely will not) close the channel on us for violating the /// 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 + /// 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); } #[derive(Clone)] @@ -174,6 +180,8 @@ pub struct InMemoryChannelKeys { pub htlc_base_key: SecretKey, /// Commitment seed pub commitment_seed: [u8; 32], + /// Remote funding pubkey + pub remote_funding_pubkey: Option, } impl ChannelKeys for InMemoryChannelKeys { @@ -184,8 +192,13 @@ 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, channel_funding_redeemscript: &Script, 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 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); @@ -222,6 +235,11 @@ impl ChannelKeys for InMemoryChannelKeys { let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]); 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); + } } impl_writeable!(InMemoryChannelKeys, 0, { @@ -230,7 +248,8 @@ impl_writeable!(InMemoryChannelKeys, 0, { payment_base_key, delayed_payment_base_key, htlc_base_key, - commitment_seed + commitment_seed, + remote_funding_pubkey }); /// Simple KeysInterface implementor that takes a 32-byte seed for use as a BIP 32 extended key @@ -379,6 +398,7 @@ impl KeysInterface for KeysManager { delayed_payment_base_key, htlc_base_key, commitment_seed, + remote_funding_pubkey: None, } } diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 747e622f0..19a07aaa1 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -255,6 +255,22 @@ pub fn get_htlc_redeemscript(htlc: &HTLCOutputInCommitment, keys: &TxCreationKey get_htlc_redeemscript_with_explicit_keys(htlc, &keys.a_htlc_key, &keys.b_htlc_key, &keys.revocation_key) } +/// Gets the redeemscript for a funding output from the two funding public keys. +/// Note that the order of funding public keys does not matter. +pub fn make_funding_redeemscript(a: &PublicKey, b: &PublicKey) -> Script { + let our_funding_key = a.serialize(); + let their_funding_key = b.serialize(); + + let builder = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2); + if our_funding_key[..] < their_funding_key[..] { + builder.push_slice(&our_funding_key) + .push_slice(&their_funding_key) + } else { + builder.push_slice(&their_funding_key) + .push_slice(&our_funding_key) + }.push_opcode(opcodes::all::OP_PUSHNUM_2).push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script() +} + /// panics if htlc.transaction_output_index.is_none()! pub fn build_htlc_transaction(prev_hash: &Sha256dHash, feerate_per_kw: u64, to_self_delay: u16, htlc: &HTLCOutputInCommitment, a_delayed_payment_key: &PublicKey, revocation_key: &PublicKey) -> Transaction { let mut txins: Vec = Vec::new(); diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 6f509f214..3d8962b4f 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}; +use ln::chan_utils::{LocalCommitmentTransaction, TxCreationKeys, HTLCOutputInCommitment, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, make_funding_redeemscript}; use ln::chan_utils; use chain::chaininterface::{FeeEstimator,ConfirmationTarget}; use chain::transaction::OutPoint; @@ -545,7 +545,8 @@ impl Channel { /// Creates a new channel from a remote sides' request for one. /// 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 chan_keys = keys_provider.get_channel_keys(true); + let mut chan_keys = keys_provider.get_channel_keys(true); + chan_keys.set_remote_funding_pubkey(&msg.funding_pubkey); let mut local_config = (*config).channel_options.clone(); if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT { @@ -1111,16 +1112,9 @@ impl Channel { /// pays to get_funding_redeemscript().to_v0_p2wsh()). /// Panics if called before accept_channel/new_from_req pub fn get_funding_redeemscript(&self) -> Script { - let builder = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2); - 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.expect("get_funding_redeemscript only allowed after accept_channel").serialize(); - if our_funding_key[..] < their_funding_key[..] { - builder.push_slice(&our_funding_key) - .push_slice(&their_funding_key) - } else { - builder.push_slice(&their_funding_key) - .push_slice(&our_funding_key) - }.push_opcode(opcodes::all::OP_PUSHNUM_2).push_opcode(opcodes::all::OP_CHECKMULTISIG).into_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) } /// Builds the htlc-success or htlc-timeout transaction which spends a given HTLC output @@ -1407,6 +1401,7 @@ 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(()) } @@ -1425,7 +1420,7 @@ impl Channel { 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; - let remote_signature = self.local_keys.sign_remote_commitment(self.channel_value_satoshis, &self.get_funding_redeemscript(), self.feerate_per_kw, &remote_initial_commitment_tx, &remote_keys, &Vec::new(), self.our_to_self_delay, &self.secp_ctx) + let remote_signature = self.local_keys.sign_remote_commitment(self.channel_value_satoshis, self.feerate_per_kw, &remote_initial_commitment_tx, &remote_keys, &Vec::new(), self.our_to_self_delay, &self.secp_ctx) .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed"))?.0; // We sign the "remote" commitment transaction, allowing them to broadcast the tx if they wish. @@ -2645,6 +2640,7 @@ impl Channel { self.channel_state |= ChannelState::LocalShutdownSent as u32; self.channel_update_count += 1; + Ok((our_shutdown, self.maybe_propose_first_closing_signed(fee_estimator), dropped_outbound_htlcs)) } @@ -3151,7 +3147,7 @@ impl Channel { fn get_outbound_funding_created_signature(&mut self) -> Result<(Signature, Transaction), ChannelError> { 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; - Ok((self.local_keys.sign_remote_commitment(self.channel_value_satoshis, &self.get_funding_redeemscript(), self.feerate_per_kw, &remote_initial_commitment_tx, &remote_keys, &Vec::new(), self.our_to_self_delay, &self.secp_ctx) + Ok((self.local_keys.sign_remote_commitment(self.channel_value_satoshis, self.feerate_per_kw, &remote_initial_commitment_tx, &remote_keys, &Vec::new(), self.our_to_self_delay, &self.secp_ctx) .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed"))?.0, remote_initial_commitment_tx)) } @@ -3459,7 +3455,7 @@ impl Channel { htlcs.push(htlc); } - let res = self.local_keys.sign_remote_commitment(self.channel_value_satoshis, &self.get_funding_redeemscript(), feerate_per_kw, &remote_commitment_tx.0, &remote_keys, &htlcs, self.our_to_self_delay, &self.secp_ctx) + let res = self.local_keys.sign_remote_commitment(self.channel_value_satoshis, feerate_per_kw, &remote_commitment_tx.0, &remote_keys, &htlcs, self.our_to_self_delay, &self.secp_ctx) .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed"))?; signature = res.0; htlc_signatures = res.1; @@ -4131,6 +4127,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, }; 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 e9c82c1bd..ee90fe7ac 100644 --- a/lightning/src/util/enforcing_trait_impls.rs +++ b/lightning/src/util/enforcing_trait_impls.rs @@ -9,7 +9,7 @@ use bitcoin::blockdata::transaction::Transaction; use bitcoin::blockdata::script::Script; use secp256k1; -use secp256k1::key::SecretKey; +use secp256k1::key::{SecretKey, PublicKey}; use secp256k1::{Secp256k1, Signature}; /// Enforces some rules on ChannelKeys calls. Eventually we will probably want to expose a variant @@ -35,7 +35,7 @@ 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, channel_funding_script: &Script, 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 { panic!(); } let obscured_commitment_transaction_number = (commitment_tx.lock_time & 0xffffff) as u64 | ((commitment_tx.input[0].sequence as u64 & 0xffffff) << 3*8); @@ -49,7 +49,7 @@ impl ChannelKeys for EnforcingChannelKeys { commitment_data.1 = cmp::max(commitment_number, commitment_data.1) } - Ok(self.inner.sign_remote_commitment(channel_value_satoshis, channel_funding_script, feerate_per_kw, commitment_tx, keys, htlcs, to_self_delay, secp_ctx).unwrap()) + Ok(self.inner.sign_remote_commitment(channel_value_satoshis, feerate_per_kw, commitment_tx, keys, htlcs, to_self_delay, secp_ctx).unwrap()) } fn sign_closing_transaction(&self, channel_value_satoshis: u64, channel_funding_redeemscript: &Script, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result { @@ -59,6 +59,10 @@ impl ChannelKeys for EnforcingChannelKeys { fn sign_channel_announcement(&self, msg: &msgs::UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) -> Result { self.inner.sign_channel_announcement(msg, secp_ctx) } + + fn set_remote_funding_pubkey(&mut self, key: &PublicKey) { + self.inner.set_remote_funding_pubkey(key) + } } impl_writeable!(EnforcingChannelKeys, 0, { -- 2.39.5