From 3244e414952542cc6508d302bbbf20c6aff91319 Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Mon, 23 Mar 2020 21:22:14 -0400 Subject: [PATCH 01/10] Cache remote basepoint and remote_csv in new OnchainTxHandler::RemoteTxCache Used in next commits to avoid passing script between ChannelMonitor and OnchainTxHandler. ChannelMonitor duplicata will be removed in future commits. --- lightning/src/ln/channelmonitor.rs | 6 ++--- lightning/src/ln/onchaintx.rs | 35 +++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index c417671eca0..16532c2e27f 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -1064,7 +1064,7 @@ impl ChannelMonitor { let our_channel_close_key_hash = Hash160::hash(&shutdown_pubkey.serialize()); let shutdown_script = Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_close_key_hash[..]).into_script(); - let mut onchain_tx_handler = OnchainTxHandler::new(destination_script.clone(), keys.clone(), their_to_self_delay, logger.clone()); + let mut onchain_tx_handler = OnchainTxHandler::new(destination_script.clone(), keys.clone(), their_to_self_delay, their_delayed_payment_base_key.clone(), their_htlc_base_key.clone(), our_to_self_delay, logger.clone()); let local_tx_sequence = initial_local_commitment_tx.unsigned_tx.input[0].sequence as u64; let local_tx_locktime = initial_local_commitment_tx.unsigned_tx.lock_time as u64; @@ -1099,8 +1099,8 @@ impl ChannelMonitor { current_remote_commitment_txid: None, prev_remote_commitment_txid: None, - their_htlc_base_key: their_htlc_base_key.clone(), - their_delayed_payment_base_key: their_delayed_payment_base_key.clone(), + their_htlc_base_key: *their_htlc_base_key, + their_delayed_payment_base_key: *their_delayed_payment_base_key, funding_redeemscript, channel_value_satoshis: channel_value_satoshis, their_cur_revocation_points: None, diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index 49572f2cb8b..0779bab52f8 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -11,6 +11,7 @@ use bitcoin::util::bip143; use bitcoin_hashes::sha256d::Hash as Sha256dHash; use secp256k1::{Secp256k1, Signature}; +use secp256k1::key::PublicKey; use secp256k1; use ln::msgs::DecodeError; @@ -48,6 +49,13 @@ enum OnchainEvent { } } +/// Cache remote basepoint to compute any transaction on +/// remote outputs, either justice or preimage/timeout transactions. +struct RemoteTxCache { + remote_delayed_payment_base_key: PublicKey, + remote_htlc_base_key: PublicKey +} + /// Higher-level cache structure needed to re-generate bumped claim txn if needed #[derive(Clone, PartialEq)] pub struct ClaimTxBumpMaterial { @@ -195,6 +203,8 @@ pub struct OnchainTxHandler { prev_local_commitment: Option, prev_local_htlc_sigs: Option>>, local_csv: u16, + remote_tx_cache: RemoteTxCache, + remote_csv: u16, key_storage: ChanSigner, @@ -241,6 +251,10 @@ impl OnchainTxHandler { self.local_csv.write(writer)?; + self.remote_tx_cache.remote_delayed_payment_base_key.write(writer)?; + self.remote_tx_cache.remote_htlc_base_key.write(writer)?; + self.remote_csv.write(writer)?; + self.key_storage.write(writer)?; writer.write_all(&byte_utils::be64_to_array(self.pending_claim_requests.len() as u64))?; @@ -289,6 +303,16 @@ impl ReadableArgs> for OnchainTx let local_csv = Readable::read(reader)?; + let remote_tx_cache = { + let remote_delayed_payment_base_key = Readable::read(reader)?; + let remote_htlc_base_key = Readable::read(reader)?; + RemoteTxCache { + remote_delayed_payment_base_key, + remote_htlc_base_key, + } + }; + let remote_csv = Readable::read(reader)?; + let key_storage = Readable::read(reader)?; let pending_claim_requests_len: u64 = Readable::read(reader)?; @@ -341,6 +365,8 @@ impl ReadableArgs> for OnchainTx prev_local_commitment, prev_local_htlc_sigs, local_csv, + remote_tx_cache, + remote_csv, key_storage, claimable_outpoints, pending_claim_requests, @@ -352,10 +378,15 @@ impl ReadableArgs> for OnchainTx } impl OnchainTxHandler { - pub(super) fn new(destination_script: Script, keys: ChanSigner, local_csv: u16, logger: Arc) -> Self { + pub(super) fn new(destination_script: Script, keys: ChanSigner, local_csv: u16, remote_delayed_payment_base_key: PublicKey, remote_htlc_base_key: PublicKey, remote_csv: u16, logger: Arc) -> Self { let key_storage = keys; + let remote_tx_cache = RemoteTxCache { + remote_delayed_payment_base_key, + remote_htlc_base_key, + }; + OnchainTxHandler { destination_script, local_commitment: None, @@ -363,6 +394,8 @@ impl OnchainTxHandler { prev_local_commitment: None, prev_local_htlc_sigs: None, local_csv, + remote_tx_cache, + remote_csv, key_storage, pending_claim_requests: HashMap::new(), claimable_outpoints: HashMap::new(), From 3caa38fbe97d829873e770053a11d572a29824dc Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Mon, 23 Mar 2020 22:17:46 -0400 Subject: [PATCH 02/10] Cache remote HTLC inside OnchainTxHandler::RemoteTxCache As we can't predict if any and which revoked commitment tx is going to appear onchain we have by design to cache all htlc information to regenerate htlc script if needed. --- lightning/src/ln/channelmonitor.rs | 9 +++++++- lightning/src/ln/onchaintx.rs | 33 ++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 16532c2e27f..8d7bdfdf604 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -1205,7 +1205,7 @@ impl ChannelMonitor { log_trace!(self, "New potential remote commitment transaction: {}", encode::serialize_hex(unsigned_commitment_tx)); self.prev_remote_commitment_txid = self.current_remote_commitment_txid.take(); self.current_remote_commitment_txid = Some(new_txid); - self.remote_claimable_outpoints.insert(new_txid, htlc_outputs); + self.remote_claimable_outpoints.insert(new_txid, htlc_outputs.clone()); self.current_remote_commitment_number = commitment_number; //TODO: Merge this into the other per-remote-transaction output storage stuff match self.their_cur_revocation_points { @@ -1226,6 +1226,13 @@ impl ChannelMonitor { self.their_cur_revocation_points = Some((commitment_number, their_revocation_point, None)); } } + let mut htlcs = Vec::with_capacity(htlc_outputs.len()); + for htlc in htlc_outputs { + if htlc.0.transaction_output_index.is_some() { + htlcs.push(htlc.0); + } + } + self.onchain_tx_handler.provide_latest_remote_tx(new_txid, htlcs); } pub(super) fn provide_rescue_remote_commitment_tx_info(&mut self, their_revocation_point: PublicKey) { diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index 0779bab52f8..d2000bd6fba 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -17,7 +17,7 @@ use secp256k1; use ln::msgs::DecodeError; use ln::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER, InputMaterial, ClaimRequest}; use ln::channelmanager::PaymentPreimage; -use ln::chan_utils::{HTLCType, LocalCommitmentTransaction}; +use ln::chan_utils::{HTLCType, LocalCommitmentTransaction, HTLCOutputInCommitment}; use chain::chaininterface::{FeeEstimator, BroadcasterInterface, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT}; use chain::keysinterface::ChannelKeys; use util::logger::Logger; @@ -53,7 +53,8 @@ enum OnchainEvent { /// remote outputs, either justice or preimage/timeout transactions. struct RemoteTxCache { remote_delayed_payment_base_key: PublicKey, - remote_htlc_base_key: PublicKey + remote_htlc_base_key: PublicKey, + per_htlc: HashMap> } /// Higher-level cache structure needed to re-generate bumped claim txn if needed @@ -253,6 +254,14 @@ impl OnchainTxHandler { self.remote_tx_cache.remote_delayed_payment_base_key.write(writer)?; self.remote_tx_cache.remote_htlc_base_key.write(writer)?; + writer.write_all(&byte_utils::be64_to_array(self.remote_tx_cache.per_htlc.len() as u64))?; + for (ref txid, ref htlcs) in self.remote_tx_cache.per_htlc.iter() { + writer.write_all(&txid[..])?; + writer.write_all(&byte_utils::be64_to_array(htlcs.len() as u64))?; + for &ref htlc in htlcs.iter() { + htlc.write(writer)?; + } + } self.remote_csv.write(writer)?; self.key_storage.write(writer)?; @@ -306,9 +315,24 @@ impl ReadableArgs> for OnchainTx let remote_tx_cache = { let remote_delayed_payment_base_key = Readable::read(reader)?; let remote_htlc_base_key = Readable::read(reader)?; + let per_htlc_len: u64 = Readable::read(reader)?; + let mut per_htlc = HashMap::with_capacity(cmp::min(per_htlc_len as usize, MAX_ALLOC_SIZE / 64)); + for _ in 0..per_htlc_len { + let txid: Sha256dHash = Readable::read(reader)?; + let htlcs_count: u64 = Readable::read(reader)?; + let mut htlcs = Vec::with_capacity(cmp::min(htlcs_count as usize, MAX_ALLOC_SIZE / 32)); + for _ in 0..htlcs_count { + let htlc = Readable::read(reader)?; + htlcs.push(htlc); + } + if let Some(_) = per_htlc.insert(txid, htlcs) { + return Err(DecodeError::InvalidValue); + } + } RemoteTxCache { remote_delayed_payment_base_key, remote_htlc_base_key, + per_htlc, } }; let remote_csv = Readable::read(reader)?; @@ -385,6 +409,7 @@ impl OnchainTxHandler { let remote_tx_cache = RemoteTxCache { remote_delayed_payment_base_key, remote_htlc_base_key, + per_htlc: HashMap::new(), }; OnchainTxHandler { @@ -903,6 +928,10 @@ impl OnchainTxHandler { } } + pub(super) fn provide_latest_remote_tx(&mut self, commitment_txid: Sha256dHash, htlcs: Vec) { + self.remote_tx_cache.per_htlc.insert(commitment_txid, htlcs); + } + #[cfg(test)] pub(super) fn get_fully_signed_copy_local_tx(&mut self, funding_redeemscript: &Script) -> Option { if let Some(ref mut local_commitment) = self.local_commitment { From ea8df9da71da8246e995e2fddadc0c6a3de2edc5 Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Mon, 23 Mar 2020 23:28:00 -0400 Subject: [PATCH 03/10] Replace is_htlc in InputMaterial by InputDescriptor As we cache more and more transaction elements in OnchainTxHandler we should dry up completly InputMaterial until them being replaced directly by InputDescriptor --- lightning/src/ln/channelmonitor.rs | 18 ++++----- lightning/src/ln/onchaintx.rs | 63 ++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 8d7bdfdf604..626335e4bee 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -32,7 +32,7 @@ use ln::msgs::DecodeError; use ln::chan_utils; use ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, LocalCommitmentTransaction, HTLCType}; use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash}; -use ln::onchaintx::OnchainTxHandler; +use ln::onchaintx::{OnchainTxHandler, InputDescriptors}; use chain::chaininterface::{ChainListener, ChainWatchInterface, BroadcasterInterface, FeeEstimator}; use chain::transaction::OutPoint; use chain::keysinterface::{SpendableOutputDescriptor, ChannelKeys}; @@ -428,7 +428,7 @@ pub(crate) enum InputMaterial { witness_script: Script, pubkey: Option, key: SecretKey, - is_htlc: bool, + input_descriptor: InputDescriptors, amount: u64, }, RemoteHTLC { @@ -450,12 +450,12 @@ pub(crate) enum InputMaterial { impl Writeable for InputMaterial { fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { match self { - &InputMaterial::Revoked { ref witness_script, ref pubkey, ref key, ref is_htlc, ref amount} => { + &InputMaterial::Revoked { ref witness_script, ref pubkey, ref key, ref input_descriptor, ref amount} => { writer.write_all(&[0; 1])?; witness_script.write(writer)?; pubkey.write(writer)?; writer.write_all(&key[..])?; - is_htlc.write(writer)?; + input_descriptor.write(writer)?; writer.write_all(&byte_utils::be64_to_array(*amount))?; }, &InputMaterial::RemoteHTLC { ref witness_script, ref key, ref preimage, ref amount, ref locktime } => { @@ -487,13 +487,13 @@ impl Readable for InputMaterial { let witness_script = Readable::read(reader)?; let pubkey = Readable::read(reader)?; let key = Readable::read(reader)?; - let is_htlc = Readable::read(reader)?; + let input_descriptor = Readable::read(reader)?; let amount = Readable::read(reader)?; InputMaterial::Revoked { witness_script, pubkey, key, - is_htlc, + input_descriptor, amount } }, @@ -1474,7 +1474,7 @@ impl ChannelMonitor { // First, process non-htlc outputs (to_local & to_remote) for (idx, outp) in tx.output.iter().enumerate() { if outp.script_pubkey == revokeable_p2wsh { - let witness_data = InputMaterial::Revoked { witness_script: revokeable_redeemscript.clone(), pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: false, amount: outp.value }; + let witness_data = InputMaterial::Revoked { witness_script: revokeable_redeemscript.clone(), pubkey: Some(revocation_pubkey), key: revocation_key, input_descriptor: InputDescriptors::RevokedOutput, amount: outp.value }; claimable_outpoints.push(ClaimRequest { absolute_timelock: height + self.our_to_self_delay as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, witness_data}); } } @@ -1489,7 +1489,7 @@ impl ChannelMonitor { tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() { return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user } - let witness_data = InputMaterial::Revoked { witness_script: expected_script, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: true, amount: tx.output[transaction_output_index as usize].value }; + let witness_data = InputMaterial::Revoked { witness_script: expected_script, pubkey: Some(revocation_pubkey), key: revocation_key, input_descriptor: if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }, amount: tx.output[transaction_output_index as usize].value }; claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data }); } } @@ -1668,7 +1668,7 @@ impl ChannelMonitor { let redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.our_to_self_delay, &delayed_key); log_trace!(self, "Remote HTLC broadcast {}:{}", htlc_txid, 0); - let witness_data = InputMaterial::Revoked { witness_script: redeemscript, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: false, amount: tx.output[0].value }; + let witness_data = InputMaterial::Revoked { witness_script: redeemscript, pubkey: Some(revocation_pubkey), key: revocation_key, input_descriptor: InputDescriptors::RevokedOutput, amount: tx.output[0].value }; let claimable_outpoints = vec!(ClaimRequest { absolute_timelock: height + self.our_to_self_delay as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: htlc_txid, vout: 0}, witness_data }); (claimable_outpoints, Some((htlc_txid, tx.output.clone()))) } diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index d2000bd6fba..db89cb7ff4d 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -17,7 +17,7 @@ use secp256k1; use ln::msgs::DecodeError; use ln::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER, InputMaterial, ClaimRequest}; use ln::channelmanager::PaymentPreimage; -use ln::chan_utils::{HTLCType, LocalCommitmentTransaction, HTLCOutputInCommitment}; +use ln::chan_utils::{LocalCommitmentTransaction, HTLCOutputInCommitment}; use chain::chaininterface::{FeeEstimator, BroadcasterInterface, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT}; use chain::keysinterface::ChannelKeys; use util::logger::Logger; @@ -102,8 +102,8 @@ impl Readable for ClaimTxBumpMaterial { } } -#[derive(PartialEq)] -pub(super) enum InputDescriptors { +#[derive(PartialEq, Clone, Copy)] +pub(crate) enum InputDescriptors { RevokedOfferedHTLC, RevokedReceivedHTLC, OfferedHTLC, @@ -111,6 +111,53 @@ pub(super) enum InputDescriptors { RevokedOutput, // either a revoked to_local output on commitment tx, a revoked HTLC-Timeout output or a revoked HTLC-Success output } +impl Writeable for InputDescriptors { + fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + match self { + &InputDescriptors::RevokedOfferedHTLC => { + writer.write_all(&[0; 1])?; + }, + &InputDescriptors::RevokedReceivedHTLC => { + writer.write_all(&[1; 1])?; + }, + &InputDescriptors::OfferedHTLC => { + writer.write_all(&[2; 1])?; + }, + &InputDescriptors::ReceivedHTLC => { + writer.write_all(&[3; 1])?; + } + &InputDescriptors::RevokedOutput => { + writer.write_all(&[4; 1])?; + } + } + Ok(()) + } +} + +impl Readable for InputDescriptors { + fn read(reader: &mut R) -> Result { + let input_descriptor = match ::read(reader)? { + 0 => { + InputDescriptors::RevokedOfferedHTLC + }, + 1 => { + InputDescriptors::RevokedReceivedHTLC + }, + 2 => { + InputDescriptors::OfferedHTLC + }, + 3 => { + InputDescriptors::ReceivedHTLC + }, + 4 => { + InputDescriptors::RevokedOutput + } + _ => return Err(DecodeError::InvalidValue), + }; + Ok(input_descriptor) + } +} + macro_rules! subtract_high_prio_fee { ($self: ident, $fee_estimator: expr, $value: expr, $predicted_weight: expr, $used_feerate: expr) => { { @@ -548,8 +595,8 @@ impl OnchainTxHandler { let mut dynamic_fee = true; for per_outp_material in cached_claim_datas.per_input_material.values() { match per_outp_material { - &InputMaterial::Revoked { ref witness_script, ref is_htlc, ref amount, .. } => { - inputs_witnesses_weight += Self::get_witnesses_weight(if !is_htlc { &[InputDescriptors::RevokedOutput] } else if HTLCType::scriptlen_to_htlctype(witness_script.len()) == Some(HTLCType::OfferedHTLC) { &[InputDescriptors::RevokedOfferedHTLC] } else if HTLCType::scriptlen_to_htlctype(witness_script.len()) == Some(HTLCType::AcceptedHTLC) { &[InputDescriptors::RevokedReceivedHTLC] } else { unreachable!() }); + &InputMaterial::Revoked { ref input_descriptor, ref amount, .. } => { + inputs_witnesses_weight += Self::get_witnesses_weight(&[*input_descriptor]); amt += *amount; }, &InputMaterial::RemoteHTLC { ref preimage, ref amount, .. } => { @@ -587,19 +634,19 @@ impl OnchainTxHandler { for (i, (outp, per_outp_material)) in cached_claim_datas.per_input_material.iter().enumerate() { match per_outp_material { - &InputMaterial::Revoked { ref witness_script, ref pubkey, ref key, ref is_htlc, ref amount } => { + &InputMaterial::Revoked { ref witness_script, ref pubkey, ref key, ref input_descriptor, ref amount } => { let sighash_parts = bip143::SighashComponents::new(&bumped_tx); let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[i], &witness_script, *amount)[..]); let sig = self.secp_ctx.sign(&sighash, &key); bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); bumped_tx.input[i].witness[0].push(SigHashType::All as u8); - if *is_htlc { + if *input_descriptor != InputDescriptors::RevokedOutput { bumped_tx.input[i].witness.push(pubkey.unwrap().clone().serialize().to_vec()); } else { bumped_tx.input[i].witness.push(vec!(1)); } bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); - log_trace!(self, "Going to broadcast Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}...", bumped_tx.txid(), if !is_htlc { "to_local" } else if HTLCType::scriptlen_to_htlctype(witness_script.len()) == Some(HTLCType::OfferedHTLC) { "offered" } else if HTLCType::scriptlen_to_htlctype(witness_script.len()) == Some(HTLCType::AcceptedHTLC) { "received" } else { "" }, outp.vout, outp.txid, new_feerate); + log_trace!(self, "Going to broadcast Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}...", bumped_tx.txid(), if *input_descriptor == InputDescriptors::RevokedOutput { "to_local" } else if *input_descriptor == InputDescriptors::RevokedOfferedHTLC { "offered" } else if *input_descriptor == InputDescriptors::RevokedReceivedHTLC { "received" } else { "" }, outp.vout, outp.txid, new_feerate); }, &InputMaterial::RemoteHTLC { ref witness_script, ref key, ref preimage, ref amount, ref locktime } => { if !preimage.is_some() { bumped_tx.lock_time = *locktime }; // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation From b082827dfcda324b36cf78db32873c41ca84bb9c Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Mon, 23 Mar 2020 21:50:23 -0400 Subject: [PATCH 04/10] Build witness_script for justice tx inside OnchainTxHandler By moving script generation inside OnchainTxHandler, we may dry-up further ChannelMonitor in next commits. --- lightning/src/ln/channelmonitor.rs | 29 ++++++------------ lightning/src/ln/onchaintx.rs | 49 ++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 626335e4bee..28f926a077d 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -425,8 +425,7 @@ struct LocalSignedTx { #[derive(Clone, PartialEq)] pub(crate) enum InputMaterial { Revoked { - witness_script: Script, - pubkey: Option, + per_commitment_point: PublicKey, key: SecretKey, input_descriptor: InputDescriptors, amount: u64, @@ -450,10 +449,9 @@ pub(crate) enum InputMaterial { impl Writeable for InputMaterial { fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { match self { - &InputMaterial::Revoked { ref witness_script, ref pubkey, ref key, ref input_descriptor, ref amount} => { + &InputMaterial::Revoked { ref per_commitment_point, ref key, ref input_descriptor, ref amount} => { writer.write_all(&[0; 1])?; - witness_script.write(writer)?; - pubkey.write(writer)?; + per_commitment_point.write(writer)?; writer.write_all(&key[..])?; input_descriptor.write(writer)?; writer.write_all(&byte_utils::be64_to_array(*amount))?; @@ -484,14 +482,12 @@ impl Readable for InputMaterial { fn read(reader: &mut R) -> Result { let input_material = match ::read(reader)? { 0 => { - let witness_script = Readable::read(reader)?; - let pubkey = Readable::read(reader)?; + let per_commitment_point = Readable::read(reader)?; let key = Readable::read(reader)?; let input_descriptor = Readable::read(reader)?; let amount = Readable::read(reader)?; InputMaterial::Revoked { - witness_script, - pubkey, + per_commitment_point, key, input_descriptor, amount @@ -1456,10 +1452,8 @@ impl ChannelMonitor { let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key); let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.keys.pubkeys().revocation_basepoint)); let revocation_key = ignore_error!(chan_utils::derive_private_revocation_key(&self.secp_ctx, &per_commitment_key, &self.keys.revocation_base_key())); - let b_htlc_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &self.keys.pubkeys().htlc_basepoint)); let local_payment_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, &per_commitment_point, &self.keys.payment_base_key())); let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.their_delayed_payment_base_key)); - let a_htlc_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.their_htlc_base_key)); let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.our_to_self_delay, &delayed_key); let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh(); @@ -1474,7 +1468,7 @@ impl ChannelMonitor { // First, process non-htlc outputs (to_local & to_remote) for (idx, outp) in tx.output.iter().enumerate() { if outp.script_pubkey == revokeable_p2wsh { - let witness_data = InputMaterial::Revoked { witness_script: revokeable_redeemscript.clone(), pubkey: Some(revocation_pubkey), key: revocation_key, input_descriptor: InputDescriptors::RevokedOutput, amount: outp.value }; + let witness_data = InputMaterial::Revoked { per_commitment_point, key: revocation_key, input_descriptor: InputDescriptors::RevokedOutput, amount: outp.value }; claimable_outpoints.push(ClaimRequest { absolute_timelock: height + self.our_to_self_delay as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, witness_data}); } } @@ -1483,13 +1477,11 @@ impl ChannelMonitor { if let Some(ref per_commitment_data) = per_commitment_option { for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() { if let Some(transaction_output_index) = htlc.transaction_output_index { - let expected_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey); if transaction_output_index as usize >= tx.output.len() || - tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 || - tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() { + tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 { return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user } - let witness_data = InputMaterial::Revoked { witness_script: expected_script, pubkey: Some(revocation_pubkey), key: revocation_key, input_descriptor: if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }, amount: tx.output[transaction_output_index as usize].value }; + let witness_data = InputMaterial::Revoked { per_commitment_point, key: revocation_key, input_descriptor: if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }, amount: tx.output[transaction_output_index as usize].value }; claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data }); } } @@ -1662,13 +1654,10 @@ impl ChannelMonitor { let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); }; let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret)); let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key); - let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.keys.pubkeys().revocation_basepoint)); let revocation_key = ignore_error!(chan_utils::derive_private_revocation_key(&self.secp_ctx, &per_commitment_key, &self.keys.revocation_base_key())); - let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &self.their_delayed_payment_base_key)); - let redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.our_to_self_delay, &delayed_key); log_trace!(self, "Remote HTLC broadcast {}:{}", htlc_txid, 0); - let witness_data = InputMaterial::Revoked { witness_script: redeemscript, pubkey: Some(revocation_pubkey), key: revocation_key, input_descriptor: InputDescriptors::RevokedOutput, amount: tx.output[0].value }; + let witness_data = InputMaterial::Revoked { per_commitment_point, key: revocation_key, input_descriptor: InputDescriptors::RevokedOutput, amount: tx.output[0].value }; let claimable_outpoints = vec!(ClaimRequest { absolute_timelock: height + self.our_to_self_delay as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: htlc_txid, vout: 0}, witness_data }); (claimable_outpoints, Some((htlc_txid, tx.output.clone()))) } diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index db89cb7ff4d..50868a70abe 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -17,7 +17,8 @@ use secp256k1; use ln::msgs::DecodeError; use ln::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER, InputMaterial, ClaimRequest}; use ln::channelmanager::PaymentPreimage; -use ln::chan_utils::{LocalCommitmentTransaction, HTLCOutputInCommitment}; +use ln::chan_utils; +use ln::chan_utils::{TxCreationKeys, LocalCommitmentTransaction, HTLCOutputInCommitment}; use chain::chaininterface::{FeeEstimator, BroadcasterInterface, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT}; use chain::keysinterface::ChannelKeys; use util::logger::Logger; @@ -634,19 +635,41 @@ impl OnchainTxHandler { for (i, (outp, per_outp_material)) in cached_claim_datas.per_input_material.iter().enumerate() { match per_outp_material { - &InputMaterial::Revoked { ref witness_script, ref pubkey, ref key, ref input_descriptor, ref amount } => { - let sighash_parts = bip143::SighashComponents::new(&bumped_tx); - let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[i], &witness_script, *amount)[..]); - let sig = self.secp_ctx.sign(&sighash, &key); - bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); - bumped_tx.input[i].witness[0].push(SigHashType::All as u8); - if *input_descriptor != InputDescriptors::RevokedOutput { - bumped_tx.input[i].witness.push(pubkey.unwrap().clone().serialize().to_vec()); - } else { - bumped_tx.input[i].witness.push(vec!(1)); + &InputMaterial::Revoked { ref per_commitment_point, ref key, ref input_descriptor, ref amount } => { + if let Ok(chan_keys) = TxCreationKeys::new(&self.secp_ctx, &per_commitment_point, &self.remote_tx_cache.remote_delayed_payment_base_key, &self.remote_tx_cache.remote_htlc_base_key, &self.key_storage.pubkeys().revocation_basepoint, &self.key_storage.pubkeys().payment_basepoint, &self.key_storage.pubkeys().htlc_basepoint) { + + let mut this_htlc = None; + if *input_descriptor != InputDescriptors::RevokedOutput { + if let Some(htlcs) = self.remote_tx_cache.per_htlc.get(&outp.txid) { + for htlc in htlcs { + if htlc.transaction_output_index.unwrap() == outp.vout { + this_htlc = Some(htlc); + } + } + } + } + + let witness_script = if *input_descriptor != InputDescriptors::RevokedOutput && this_htlc.is_some() { + chan_utils::get_htlc_redeemscript_with_explicit_keys(&this_htlc.unwrap(), &chan_keys.a_htlc_key, &chan_keys.b_htlc_key, &chan_keys.revocation_key) + } else if *input_descriptor != InputDescriptors::RevokedOutput { + return None; + } else { + chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, self.remote_csv, &chan_keys.a_delayed_payment_key) + }; + + let sighash_parts = bip143::SighashComponents::new(&bumped_tx); + let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[i], &witness_script, *amount)[..]); + let sig = self.secp_ctx.sign(&sighash, &key); + bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); + bumped_tx.input[i].witness[0].push(SigHashType::All as u8); + if *input_descriptor != InputDescriptors::RevokedOutput { + bumped_tx.input[i].witness.push(chan_keys.revocation_key.clone().serialize().to_vec()); + } else { + bumped_tx.input[i].witness.push(vec!(1)); + } + bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); + log_trace!(self, "Going to broadcast Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}...", bumped_tx.txid(), if *input_descriptor == InputDescriptors::RevokedOutput { "to_local" } else if *input_descriptor == InputDescriptors::RevokedOfferedHTLC { "offered" } else if *input_descriptor == InputDescriptors::RevokedReceivedHTLC { "received" } else { "" }, outp.vout, outp.txid, new_feerate); } - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); - log_trace!(self, "Going to broadcast Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}...", bumped_tx.txid(), if *input_descriptor == InputDescriptors::RevokedOutput { "to_local" } else if *input_descriptor == InputDescriptors::RevokedOfferedHTLC { "offered" } else if *input_descriptor == InputDescriptors::RevokedReceivedHTLC { "received" } else { "" }, outp.vout, outp.txid, new_feerate); }, &InputMaterial::RemoteHTLC { ref witness_script, ref key, ref preimage, ref amount, ref locktime } => { if !preimage.is_some() { bumped_tx.lock_time = *locktime }; // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation From bb5b14c73c297e5840d15b42c955498eaa254202 Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Tue, 24 Mar 2020 14:22:16 -0400 Subject: [PATCH 05/10] Move justice transaction signature behind ChanSigner --- lightning/src/chain/keysinterface.rs | 21 ++++++++++++++++++++- lightning/src/ln/chan_utils.rs | 2 +- lightning/src/ln/channelmonitor.rs | 18 ++++++++---------- lightning/src/ln/onchaintx.rs | 15 +++------------ lightning/src/util/enforcing_trait_impls.rs | 5 +++++ 5 files changed, 37 insertions(+), 24 deletions(-) diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index 73c22684c54..2178d5a2683 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -2,7 +2,7 @@ //! spendable on-chain outputs which the user owns and is responsible for using just as any other //! on-chain output which is theirs. -use bitcoin::blockdata::transaction::{Transaction, OutPoint, TxOut}; +use bitcoin::blockdata::transaction::{Transaction, OutPoint, TxOut, SigHashType}; use bitcoin::blockdata::script::{Script, Builder}; use bitcoin::blockdata::opcodes; use bitcoin::network::constants::Network; @@ -245,6 +245,9 @@ pub trait ChannelKeys : Send+Clone { /// return value must contain a signature. fn sign_local_commitment_htlc_transactions(&self, local_commitment_tx: &LocalCommitmentTransaction, local_csv: u16, secp_ctx: &Secp256k1) -> Result>, ()>; + /// Signs a justice transaction. + fn sign_justice_transaction(&self, bumped_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_key: &SecretKey, revocation_pubkey: &PublicKey, is_htlc: bool, secp_ctx: &Secp256k1); + /// Create a signature for a (proposed) closing transaction. /// /// Note that, due to rounding, there may be one "missing" satoshi, and either party may have @@ -393,6 +396,22 @@ impl ChannelKeys for InMemoryChannelKeys { local_commitment_tx.get_htlc_sigs(&self.htlc_base_key, local_csv, secp_ctx) } + fn sign_justice_transaction(&self, bumped_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_key: &SecretKey, revocation_pubkey: &PublicKey, is_htlc: bool, secp_ctx: &Secp256k1) { + if let Ok(revocation_key) = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key) { + let sighash_parts = bip143::SighashComponents::new(&bumped_tx); + let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[input], &witness_script, amount)[..]); + let sig = secp_ctx.sign(&sighash, &revocation_key); + bumped_tx.input[input].witness.push(sig.serialize_der().to_vec()); + bumped_tx.input[input].witness[0].push(SigHashType::All as u8); + if is_htlc { + bumped_tx.input[input].witness.push(revocation_pubkey.clone().serialize().to_vec()); + } else { + bumped_tx.input[input].witness.push(vec!(1)); + } + bumped_tx.input[input].witness.push(witness_script.clone().into_bytes()); + } + } + fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result { if closing_tx.input.len() != 1 { return Err(()); } if closing_tx.input[0].witness.len() != 0 { return Err(()); } diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 62d3103337d..3794e8a9402 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -198,7 +198,7 @@ pub(super) fn derive_public_key(secp_ctx: &Secp256k1, /// Derives a revocation key from its constituent parts. /// Note that this is infallible iff we trust that at least one of the two input keys are randomly /// generated (ie our own). -pub(super) fn derive_private_revocation_key(secp_ctx: &Secp256k1, per_commitment_secret: &SecretKey, revocation_base_secret: &SecretKey) -> Result { +pub fn derive_private_revocation_key(secp_ctx: &Secp256k1, per_commitment_secret: &SecretKey, revocation_base_secret: &SecretKey) -> Result { let revocation_base_point = PublicKey::from_secret_key(&secp_ctx, &revocation_base_secret); let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret); diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 28f926a077d..d283147a5a9 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -426,7 +426,7 @@ struct LocalSignedTx { pub(crate) enum InputMaterial { Revoked { per_commitment_point: PublicKey, - key: SecretKey, + per_commitment_key: SecretKey, input_descriptor: InputDescriptors, amount: u64, }, @@ -449,10 +449,10 @@ pub(crate) enum InputMaterial { impl Writeable for InputMaterial { fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { match self { - &InputMaterial::Revoked { ref per_commitment_point, ref key, ref input_descriptor, ref amount} => { + &InputMaterial::Revoked { ref per_commitment_point, ref per_commitment_key, ref input_descriptor, ref amount} => { writer.write_all(&[0; 1])?; per_commitment_point.write(writer)?; - writer.write_all(&key[..])?; + writer.write_all(&per_commitment_key[..])?; input_descriptor.write(writer)?; writer.write_all(&byte_utils::be64_to_array(*amount))?; }, @@ -483,12 +483,12 @@ impl Readable for InputMaterial { let input_material = match ::read(reader)? { 0 => { let per_commitment_point = Readable::read(reader)?; - let key = Readable::read(reader)?; + let per_commitment_key = Readable::read(reader)?; let input_descriptor = Readable::read(reader)?; let amount = Readable::read(reader)?; InputMaterial::Revoked { per_commitment_point, - key, + per_commitment_key, input_descriptor, amount } @@ -1451,7 +1451,6 @@ impl ChannelMonitor { let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret)); let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key); let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.keys.pubkeys().revocation_basepoint)); - let revocation_key = ignore_error!(chan_utils::derive_private_revocation_key(&self.secp_ctx, &per_commitment_key, &self.keys.revocation_base_key())); let local_payment_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, &per_commitment_point, &self.keys.payment_base_key())); let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.their_delayed_payment_base_key)); @@ -1468,7 +1467,7 @@ impl ChannelMonitor { // First, process non-htlc outputs (to_local & to_remote) for (idx, outp) in tx.output.iter().enumerate() { if outp.script_pubkey == revokeable_p2wsh { - let witness_data = InputMaterial::Revoked { per_commitment_point, key: revocation_key, input_descriptor: InputDescriptors::RevokedOutput, amount: outp.value }; + let witness_data = InputMaterial::Revoked { per_commitment_point, per_commitment_key, input_descriptor: InputDescriptors::RevokedOutput, amount: outp.value }; claimable_outpoints.push(ClaimRequest { absolute_timelock: height + self.our_to_self_delay as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, witness_data}); } } @@ -1481,7 +1480,7 @@ impl ChannelMonitor { tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 { return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user } - let witness_data = InputMaterial::Revoked { per_commitment_point, key: revocation_key, input_descriptor: if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }, amount: tx.output[transaction_output_index as usize].value }; + let witness_data = InputMaterial::Revoked { per_commitment_point, per_commitment_key, input_descriptor: if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }, amount: tx.output[transaction_output_index as usize].value }; claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data }); } } @@ -1654,10 +1653,9 @@ impl ChannelMonitor { let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); }; let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret)); let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key); - let revocation_key = ignore_error!(chan_utils::derive_private_revocation_key(&self.secp_ctx, &per_commitment_key, &self.keys.revocation_base_key())); log_trace!(self, "Remote HTLC broadcast {}:{}", htlc_txid, 0); - let witness_data = InputMaterial::Revoked { per_commitment_point, key: revocation_key, input_descriptor: InputDescriptors::RevokedOutput, amount: tx.output[0].value }; + let witness_data = InputMaterial::Revoked { per_commitment_point, per_commitment_key, input_descriptor: InputDescriptors::RevokedOutput, amount: tx.output[0].value }; let claimable_outpoints = vec!(ClaimRequest { absolute_timelock: height + self.our_to_self_delay as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: htlc_txid, vout: 0}, witness_data }); (claimable_outpoints, Some((htlc_txid, tx.output.clone()))) } diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index 50868a70abe..3e95c0354bc 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -635,7 +635,7 @@ impl OnchainTxHandler { for (i, (outp, per_outp_material)) in cached_claim_datas.per_input_material.iter().enumerate() { match per_outp_material { - &InputMaterial::Revoked { ref per_commitment_point, ref key, ref input_descriptor, ref amount } => { + &InputMaterial::Revoked { ref per_commitment_point, ref per_commitment_key, ref input_descriptor, ref amount } => { if let Ok(chan_keys) = TxCreationKeys::new(&self.secp_ctx, &per_commitment_point, &self.remote_tx_cache.remote_delayed_payment_base_key, &self.remote_tx_cache.remote_htlc_base_key, &self.key_storage.pubkeys().revocation_basepoint, &self.key_storage.pubkeys().payment_basepoint, &self.key_storage.pubkeys().htlc_basepoint) { let mut this_htlc = None; @@ -657,17 +657,8 @@ impl OnchainTxHandler { chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, self.remote_csv, &chan_keys.a_delayed_payment_key) }; - let sighash_parts = bip143::SighashComponents::new(&bumped_tx); - let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[i], &witness_script, *amount)[..]); - let sig = self.secp_ctx.sign(&sighash, &key); - bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); - bumped_tx.input[i].witness[0].push(SigHashType::All as u8); - if *input_descriptor != InputDescriptors::RevokedOutput { - bumped_tx.input[i].witness.push(chan_keys.revocation_key.clone().serialize().to_vec()); - } else { - bumped_tx.input[i].witness.push(vec!(1)); - } - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); + self.key_storage.sign_justice_transaction(&mut bumped_tx, i, &witness_script, *amount, &per_commitment_key, &chan_keys.revocation_key, *input_descriptor != InputDescriptors::RevokedOutput, &self.secp_ctx); + log_trace!(self, "Going to broadcast Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}...", bumped_tx.txid(), if *input_descriptor == InputDescriptors::RevokedOutput { "to_local" } else if *input_descriptor == InputDescriptors::RevokedOfferedHTLC { "offered" } else if *input_descriptor == InputDescriptors::RevokedReceivedHTLC { "received" } else { "" }, outp.vout, outp.txid, new_feerate); } }, diff --git a/lightning/src/util/enforcing_trait_impls.rs b/lightning/src/util/enforcing_trait_impls.rs index 3e1ffd04446..a54ded65efa 100644 --- a/lightning/src/util/enforcing_trait_impls.rs +++ b/lightning/src/util/enforcing_trait_impls.rs @@ -6,6 +6,7 @@ use std::cmp; use std::sync::{Mutex, Arc}; use bitcoin::blockdata::transaction::Transaction; +use bitcoin::blockdata::script::Script; use bitcoin::util::bip143; use secp256k1; @@ -105,6 +106,10 @@ impl ChannelKeys for EnforcingChannelKeys { Ok(self.inner.sign_local_commitment_htlc_transactions(local_commitment_tx, local_csv, secp_ctx).unwrap()) } + fn sign_justice_transaction(&self, bumped_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_key: &SecretKey, revocation_pubkey: &PublicKey, is_htlc: bool, secp_ctx: &Secp256k1) { + self.inner.sign_justice_transaction(bumped_tx, input, witness_script, amount, per_commitment_key, revocation_pubkey, is_htlc, secp_ctx); + } + fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result { Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap()) } From d2a6a7886a2f9917e4316f06860fc5e26dee97df Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Tue, 24 Mar 2020 14:47:37 -0400 Subject: [PATCH 06/10] Build witness_script for remote htlc transactions inside OnchainTxHandler By moving script generation inside OnchainTxHandler, we may dry-up further ChannelMonitor in next commits --- lightning/src/ln/channelmonitor.rs | 19 ++++++--------- lightning/src/ln/onchaintx.rs | 39 ++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index d283147a5a9..a2ebbf6342a 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -431,7 +431,7 @@ pub(crate) enum InputMaterial { amount: u64, }, RemoteHTLC { - witness_script: Script, + per_commitment_point: PublicKey, key: SecretKey, preimage: Option, amount: u64, @@ -456,9 +456,9 @@ impl Writeable for InputMaterial { input_descriptor.write(writer)?; writer.write_all(&byte_utils::be64_to_array(*amount))?; }, - &InputMaterial::RemoteHTLC { ref witness_script, ref key, ref preimage, ref amount, ref locktime } => { + &InputMaterial::RemoteHTLC { ref per_commitment_point, ref key, ref preimage, ref amount, ref locktime } => { writer.write_all(&[1; 1])?; - witness_script.write(writer)?; + per_commitment_point.write(writer)?; key.write(writer)?; preimage.write(writer)?; writer.write_all(&byte_utils::be64_to_array(*amount))?; @@ -494,13 +494,13 @@ impl Readable for InputMaterial { } }, 1 => { - let witness_script = Readable::read(reader)?; + let per_commitment_point = Readable::read(reader)?; let key = Readable::read(reader)?; let preimage = Readable::read(reader)?; let amount = Readable::read(reader)?; let locktime = Readable::read(reader)?; InputMaterial::RemoteHTLC { - witness_script, + per_commitment_point, key, preimage, amount, @@ -1598,10 +1598,7 @@ impl ChannelMonitor { if revocation_points.0 == commitment_number + 1 { Some(point) } else { None } } else { None }; if let Some(revocation_point) = revocation_point_option { - let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, revocation_point, &self.keys.pubkeys().revocation_basepoint)); - let b_htlc_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &self.keys.pubkeys().htlc_basepoint)); let htlc_privkey = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &self.keys.htlc_base_key())); - let a_htlc_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &self.their_htlc_base_key)); let local_payment_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &self.keys.payment_base_key())); self.broadcasted_remote_payment_script = { @@ -1614,16 +1611,14 @@ impl ChannelMonitor { // Then, try to find htlc outputs for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() { if let Some(transaction_output_index) = htlc.transaction_output_index { - let expected_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey); if transaction_output_index as usize >= tx.output.len() || - tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 || - tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() { + tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 { return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user } let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None }; let aggregable = if !htlc.offered { false } else { true }; if preimage.is_some() || !htlc.offered { - let witness_data = InputMaterial::RemoteHTLC { witness_script: expected_script, key: htlc_privkey, preimage, amount: htlc.amount_msat / 1000, locktime: htlc.cltv_expiry }; + let witness_data = InputMaterial::RemoteHTLC { per_commitment_point: *revocation_point, key: htlc_privkey, preimage, amount: htlc.amount_msat / 1000, locktime: htlc.cltv_expiry }; claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data }); } } diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index 3e95c0354bc..c043722bfd6 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -662,20 +662,33 @@ impl OnchainTxHandler { log_trace!(self, "Going to broadcast Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}...", bumped_tx.txid(), if *input_descriptor == InputDescriptors::RevokedOutput { "to_local" } else if *input_descriptor == InputDescriptors::RevokedOfferedHTLC { "offered" } else if *input_descriptor == InputDescriptors::RevokedReceivedHTLC { "received" } else { "" }, outp.vout, outp.txid, new_feerate); } }, - &InputMaterial::RemoteHTLC { ref witness_script, ref key, ref preimage, ref amount, ref locktime } => { - if !preimage.is_some() { bumped_tx.lock_time = *locktime }; // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation - let sighash_parts = bip143::SighashComponents::new(&bumped_tx); - let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[i], &witness_script, *amount)[..]); - let sig = self.secp_ctx.sign(&sighash, &key); - bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); - bumped_tx.input[i].witness[0].push(SigHashType::All as u8); - if let &Some(preimage) = preimage { - bumped_tx.input[i].witness.push(preimage.clone().0.to_vec()); - } else { - bumped_tx.input[i].witness.push(vec![]); + &InputMaterial::RemoteHTLC { ref per_commitment_point, ref key, ref preimage, ref amount, ref locktime } => { + if let Ok(chan_keys) = TxCreationKeys::new(&self.secp_ctx, &per_commitment_point, &self.remote_tx_cache.remote_delayed_payment_base_key, &self.remote_tx_cache.remote_htlc_base_key, &self.key_storage.pubkeys().revocation_basepoint, &self.key_storage.pubkeys().payment_basepoint, &self.key_storage.pubkeys().htlc_basepoint) { + let mut this_htlc = None; + if let Some(htlcs) = self.remote_tx_cache.per_htlc.get(&outp.txid) { + for htlc in htlcs { + if htlc.transaction_output_index.unwrap() == outp.vout { + this_htlc = Some(htlc); + } + } + } + if this_htlc.is_none() { return None; } + let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&this_htlc.unwrap(), &chan_keys.a_htlc_key, &chan_keys.b_htlc_key, &chan_keys.revocation_key); + + if !preimage.is_some() { bumped_tx.lock_time = *locktime }; // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation + let sighash_parts = bip143::SighashComponents::new(&bumped_tx); + let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[i], &witness_script, *amount)[..]); + let sig = self.secp_ctx.sign(&sighash, &key); + bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); + bumped_tx.input[i].witness[0].push(SigHashType::All as u8); + if let &Some(preimage) = preimage { + bumped_tx.input[i].witness.push(preimage.clone().0.to_vec()); + } else { + bumped_tx.input[i].witness.push(vec![0]); + } + bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); + log_trace!(self, "Going to broadcast Claim Transaction {} claiming remote {} htlc output {} from {} with new feerate {}...", bumped_tx.txid(), if preimage.is_some() { "offered" } else { "received" }, outp.vout, outp.txid, new_feerate); } - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); - log_trace!(self, "Going to broadcast Claim Transaction {} claiming remote {} htlc output {} from {} with new feerate {}...", bumped_tx.txid(), if preimage.is_some() { "offered" } else { "received" }, outp.vout, outp.txid, new_feerate); }, _ => unreachable!() } From 5a8f68fad5505b2e85717f32e044f539f3d1c22f Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Tue, 24 Mar 2020 15:04:36 -0400 Subject: [PATCH 07/10] Move remote htlc transaction signature behind ChanSigner --- lightning/src/chain/keysinterface.rs | 20 ++++++++++++++++++++ lightning/src/ln/channelmonitor.rs | 9 ++------- lightning/src/ln/onchaintx.rs | 17 +++-------------- lightning/src/util/enforcing_trait_impls.rs | 5 +++++ 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index 2178d5a2683..584b8428a18 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -26,6 +26,7 @@ use util::ser::{Writeable, Writer, Readable}; use ln::chan_utils; use ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, LocalCommitmentTransaction}; use ln::msgs; +use ln::channelmanager::PaymentPreimage; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -248,6 +249,9 @@ pub trait ChannelKeys : Send+Clone { /// Signs a justice transaction. fn sign_justice_transaction(&self, bumped_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_key: &SecretKey, revocation_pubkey: &PublicKey, is_htlc: bool, secp_ctx: &Secp256k1); + /// Signs a remote htlc transaction. + fn sign_remote_htlc_transaction(&self, bumped_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_point: &PublicKey, preimage: &Option, secp_ctx: &Secp256k1); + /// Create a signature for a (proposed) closing transaction. /// /// Note that, due to rounding, there may be one "missing" satoshi, and either party may have @@ -412,6 +416,22 @@ impl ChannelKeys for InMemoryChannelKeys { } } + fn sign_remote_htlc_transaction(&self, bumped_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_point: &PublicKey, preimage: &Option, secp_ctx: &Secp256k1) { + if let Ok(htlc_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key) { + let sighash_parts = bip143::SighashComponents::new(&bumped_tx); + let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[input], &witness_script, amount)[..]); + let sig = secp_ctx.sign(&sighash, &htlc_key); + bumped_tx.input[input].witness.push(sig.serialize_der().to_vec()); + bumped_tx.input[input].witness[0].push(SigHashType::All as u8); + if let &Some(preimage) = preimage { + bumped_tx.input[input].witness.push(preimage.0.to_vec()); + } else { + bumped_tx.input[input].witness.push(vec![0]); + } + bumped_tx.input[input].witness.push(witness_script.clone().into_bytes()); + } + } + fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result { if closing_tx.input.len() != 1 { return Err(()); } if closing_tx.input[0].witness.len() != 0 { return Err(()); } diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index a2ebbf6342a..1096aef78df 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -432,7 +432,6 @@ pub(crate) enum InputMaterial { }, RemoteHTLC { per_commitment_point: PublicKey, - key: SecretKey, preimage: Option, amount: u64, locktime: u32, @@ -456,10 +455,9 @@ impl Writeable for InputMaterial { input_descriptor.write(writer)?; writer.write_all(&byte_utils::be64_to_array(*amount))?; }, - &InputMaterial::RemoteHTLC { ref per_commitment_point, ref key, ref preimage, ref amount, ref locktime } => { + &InputMaterial::RemoteHTLC { ref per_commitment_point, ref preimage, ref amount, ref locktime } => { writer.write_all(&[1; 1])?; per_commitment_point.write(writer)?; - key.write(writer)?; preimage.write(writer)?; writer.write_all(&byte_utils::be64_to_array(*amount))?; writer.write_all(&byte_utils::be32_to_array(*locktime))?; @@ -495,13 +493,11 @@ impl Readable for InputMaterial { }, 1 => { let per_commitment_point = Readable::read(reader)?; - let key = Readable::read(reader)?; let preimage = Readable::read(reader)?; let amount = Readable::read(reader)?; let locktime = Readable::read(reader)?; InputMaterial::RemoteHTLC { per_commitment_point, - key, preimage, amount, locktime @@ -1598,7 +1594,6 @@ impl ChannelMonitor { if revocation_points.0 == commitment_number + 1 { Some(point) } else { None } } else { None }; if let Some(revocation_point) = revocation_point_option { - let htlc_privkey = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &self.keys.htlc_base_key())); let local_payment_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &self.keys.payment_base_key())); self.broadcasted_remote_payment_script = { @@ -1618,7 +1613,7 @@ impl ChannelMonitor { let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None }; let aggregable = if !htlc.offered { false } else { true }; if preimage.is_some() || !htlc.offered { - let witness_data = InputMaterial::RemoteHTLC { per_commitment_point: *revocation_point, key: htlc_privkey, preimage, amount: htlc.amount_msat / 1000, locktime: htlc.cltv_expiry }; + let witness_data = InputMaterial::RemoteHTLC { per_commitment_point: *revocation_point, preimage, amount: htlc.amount_msat / 1000, locktime: htlc.cltv_expiry }; claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data }); } } diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index c043722bfd6..829449ded7a 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -3,10 +3,9 @@ //! OnchainTxHandler objetcs are fully-part of ChannelMonitor and encapsulates all //! building, tracking, bumping and notifications functions. -use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, SigHashType}; +use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut}; use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint; use bitcoin::blockdata::script::Script; -use bitcoin::util::bip143; use bitcoin_hashes::sha256d::Hash as Sha256dHash; @@ -662,7 +661,7 @@ impl OnchainTxHandler { log_trace!(self, "Going to broadcast Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}...", bumped_tx.txid(), if *input_descriptor == InputDescriptors::RevokedOutput { "to_local" } else if *input_descriptor == InputDescriptors::RevokedOfferedHTLC { "offered" } else if *input_descriptor == InputDescriptors::RevokedReceivedHTLC { "received" } else { "" }, outp.vout, outp.txid, new_feerate); } }, - &InputMaterial::RemoteHTLC { ref per_commitment_point, ref key, ref preimage, ref amount, ref locktime } => { + &InputMaterial::RemoteHTLC { ref per_commitment_point, ref preimage, ref amount, ref locktime } => { if let Ok(chan_keys) = TxCreationKeys::new(&self.secp_ctx, &per_commitment_point, &self.remote_tx_cache.remote_delayed_payment_base_key, &self.remote_tx_cache.remote_htlc_base_key, &self.key_storage.pubkeys().revocation_basepoint, &self.key_storage.pubkeys().payment_basepoint, &self.key_storage.pubkeys().htlc_basepoint) { let mut this_htlc = None; if let Some(htlcs) = self.remote_tx_cache.per_htlc.get(&outp.txid) { @@ -676,17 +675,7 @@ impl OnchainTxHandler { let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&this_htlc.unwrap(), &chan_keys.a_htlc_key, &chan_keys.b_htlc_key, &chan_keys.revocation_key); if !preimage.is_some() { bumped_tx.lock_time = *locktime }; // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation - let sighash_parts = bip143::SighashComponents::new(&bumped_tx); - let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[i], &witness_script, *amount)[..]); - let sig = self.secp_ctx.sign(&sighash, &key); - bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); - bumped_tx.input[i].witness[0].push(SigHashType::All as u8); - if let &Some(preimage) = preimage { - bumped_tx.input[i].witness.push(preimage.clone().0.to_vec()); - } else { - bumped_tx.input[i].witness.push(vec![0]); - } - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); + self.key_storage.sign_remote_htlc_transaction(&mut bumped_tx, i, &witness_script, *amount, &per_commitment_point, preimage, &self.secp_ctx); log_trace!(self, "Going to broadcast Claim Transaction {} claiming remote {} htlc output {} from {} with new feerate {}...", bumped_tx.txid(), if preimage.is_some() { "offered" } else { "received" }, outp.vout, outp.txid, new_feerate); } }, diff --git a/lightning/src/util/enforcing_trait_impls.rs b/lightning/src/util/enforcing_trait_impls.rs index a54ded65efa..90108d009a7 100644 --- a/lightning/src/util/enforcing_trait_impls.rs +++ b/lightning/src/util/enforcing_trait_impls.rs @@ -1,5 +1,6 @@ use ln::chan_utils::{HTLCOutputInCommitment, TxCreationKeys, ChannelPublicKeys, LocalCommitmentTransaction}; use ln::{chan_utils, msgs}; +use ln::channelmanager::PaymentPreimage; use chain::keysinterface::{ChannelKeys, InMemoryChannelKeys}; use std::cmp; @@ -110,6 +111,10 @@ impl ChannelKeys for EnforcingChannelKeys { self.inner.sign_justice_transaction(bumped_tx, input, witness_script, amount, per_commitment_key, revocation_pubkey, is_htlc, secp_ctx); } + fn sign_remote_htlc_transaction(&self, bumped_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_point: &PublicKey, preimage: &Option, secp_ctx: &Secp256k1) { + self.inner.sign_remote_htlc_transaction(bumped_tx, input, witness_script, amount, per_commitment_point, preimage, secp_ctx); + } + fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result { Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap()) } From 4fd727f5913c2dee52e26ef2a4274126a7e09795 Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Tue, 24 Mar 2020 15:43:21 -0400 Subject: [PATCH 08/10] Add test-only ChannelMonitor::get_chan_signer to sign spendable output spending --- lightning/src/ln/channelmonitor.rs | 7 +++++++ lightning/src/ln/functional_test_utils.rs | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 1096aef78df..8de194239b5 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -1822,6 +1822,13 @@ impl ChannelMonitor { Vec::new() } + /// Get a reference to Channel signer to spend SpendableOutputDescriptor in + /// test framework + #[cfg(test)] + pub fn get_chan_signer(&self) -> &ChanSigner { + &self.keys + } + /// Called by SimpleManyChannelMonitor::block_connected, which implements /// ChainListener::block_connected. /// Eventually this should be pub and, roughly, implement ChainListener, however this requires diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 91365d5a29b..f89c959dd1f 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -305,6 +305,22 @@ macro_rules! check_added_monitors { } } +macro_rules! get_chan_signer { + ($node: expr, $channel_id: expr) => { + { + let mut monitors = $node.chan_monitor.simple_monitor.monitors.lock().unwrap(); + let mut chan_signer = None; + for (funding_txo, monitor) in monitors.iter_mut() { + if funding_txo.to_channel_id() == $channel_id { + chan_signer = Some(monitor.get_chan_signer().clone()); + break; + } + } + chan_signer.unwrap() + } + } +} + pub fn create_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, expected_chan_value: u64, expected_user_chan_id: u64) -> ([u8; 32], Transaction, OutPoint) { let chan_id = *node.network_chan_count.borrow(); From 13a6604a57b5f0db1c0485038bb19197cb1ace54 Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Tue, 24 Mar 2020 16:26:46 -0400 Subject: [PATCH 09/10] Remove SecretKey from DynamicOutputP2WSH descriptor Add sign_delayed_transaction in ChanSigner to be able to spend SpendableOutputDescriptor in test framework. --- lightning/src/chain/keysinterface.rs | 25 ++++++++--- lightning/src/ln/channelmonitor.rs | 14 +++--- lightning/src/ln/functional_tests.rs | 50 ++++++++++++--------- lightning/src/util/enforcing_trait_impls.rs | 4 ++ 4 files changed, 59 insertions(+), 34 deletions(-) diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index 584b8428a18..b751e2e4688 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -65,8 +65,8 @@ pub enum SpendableOutputDescriptor { DynamicOutputP2WSH { /// The outpoint which is spendable outpoint: OutPoint, - /// The secret key which must be used to sign the spending transaction - key: SecretKey, + /// Per commitment point to derive delayed_payment_key by key holder + per_commitment_point: PublicKey, /// The witness redeemScript which is hashed to create the script_pubkey in the given output witness_script: Script, /// The nSequence value which must be set in the spending input to satisfy the OP_CSV in @@ -98,10 +98,10 @@ impl Writeable for SpendableOutputDescriptor { outpoint.write(writer)?; output.write(writer)?; }, - &SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref key, ref witness_script, ref to_self_delay, ref output } => { + &SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref per_commitment_point, ref witness_script, ref to_self_delay, ref output } => { 1u8.write(writer)?; outpoint.write(writer)?; - key.write(writer)?; + per_commitment_point.write(writer)?; witness_script.write(writer)?; to_self_delay.write(writer)?; output.write(writer)?; @@ -126,7 +126,7 @@ impl Readable for SpendableOutputDescriptor { }), 1u8 => Ok(SpendableOutputDescriptor::DynamicOutputP2WSH { outpoint: Readable::read(reader)?, - key: Readable::read(reader)?, + per_commitment_point: Readable::read(reader)?, witness_script: Readable::read(reader)?, to_self_delay: Readable::read(reader)?, output: Readable::read(reader)?, @@ -258,6 +258,9 @@ pub trait ChannelKeys : Send+Clone { /// chosen to forgo their output as dust. fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result; + /// Create a signature for a delayed transaction. + fn sign_delayed_transaction(&self, spend_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_point: &PublicKey, secp_ctx: &Secp256k1); + /// Signs a channel announcement message with our funding key, proving it comes from one /// of the channel participants. /// @@ -446,6 +449,18 @@ impl ChannelKeys for InMemoryChannelKeys { Ok(secp_ctx.sign(&sighash, &self.funding_key)) } + fn sign_delayed_transaction(&self, spend_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_point: &PublicKey, secp_ctx: &Secp256k1) { + if let Ok(htlc_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.delayed_payment_base_key) { + let sighash_parts = bip143::SighashComponents::new(&spend_tx); + let sighash = hash_to_message!(&sighash_parts.sighash_all(&spend_tx.input[input], witness_script, amount)[..]); + let local_delaysig = secp_ctx.sign(&sighash, &htlc_key); + spend_tx.input[0].witness.push(local_delaysig.serialize_der().to_vec()); + spend_tx.input[0].witness[0].push(SigHashType::All as u8); + spend_tx.input[0].witness.push(vec!(0)); + spend_tx.input[0].witness.push(witness_script.clone().into_bytes()); + } + } + fn sign_channel_announcement(&self, msg: &msgs::UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) -> Result { let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]); Ok(secp_ctx.sign(&msghash, &self.funding_key)) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 8de194239b5..f8dcd1c5aaf 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -713,7 +713,7 @@ pub struct ChannelMonitor { commitment_transaction_number_obscure_factor: u64, destination_script: Script, - broadcasted_local_revokable_script: Option<(Script, SecretKey, Script)>, + broadcasted_local_revokable_script: Option<(Script, PublicKey, Script)>, broadcasted_remote_payment_script: Option<(Script, SecretKey)>, shutdown_script: Script, @@ -1650,14 +1650,12 @@ impl ChannelMonitor { (claimable_outpoints, Some((htlc_txid, tx.output.clone()))) } - fn broadcast_by_local_state(&self, commitment_tx: &Transaction, local_tx: &LocalSignedTx) -> (Vec, Vec, Option<(Script, SecretKey, Script)>) { + fn broadcast_by_local_state(&self, commitment_tx: &Transaction, local_tx: &LocalSignedTx) -> (Vec, Vec, Option<(Script, PublicKey, Script)>) { let mut claim_requests = Vec::with_capacity(local_tx.htlc_outputs.len()); let mut watch_outputs = Vec::with_capacity(local_tx.htlc_outputs.len()); let redeemscript = chan_utils::get_revokeable_redeemscript(&local_tx.revocation_key, self.their_to_self_delay, &local_tx.delayed_payment_key); - let broadcasted_local_revokable_script = if let Ok(local_delayedkey) = chan_utils::derive_private_key(&self.secp_ctx, &local_tx.per_commitment_point, self.keys.delayed_payment_base_key()) { - Some((redeemscript.to_v0_p2wsh(), local_delayedkey, redeemscript)) - } else { None }; + let broadcasted_local_revokable_script = Some((redeemscript.to_v0_p2wsh(), local_tx.per_commitment_point.clone(), redeemscript)); for &(ref htlc, _, _) in local_tx.htlc_outputs.iter() { if let Some(transaction_output_index) = htlc.transaction_output_index { @@ -2161,7 +2159,7 @@ impl ChannelMonitor { if broadcasted_local_revokable_script.0 == outp.script_pubkey { spendable_output = Some(SpendableOutputDescriptor::DynamicOutputP2WSH { outpoint: BitcoinOutPoint { txid: tx.txid(), vout: i as u32 }, - key: broadcasted_local_revokable_script.1, + per_commitment_point: broadcasted_local_revokable_script.1, witness_script: broadcasted_local_revokable_script.2.clone(), to_self_delay: self.their_to_self_delay, output: outp.clone(), @@ -2225,9 +2223,9 @@ impl ReadableArgs> for (Sha256dH let broadcasted_local_revokable_script = match ::read(reader)? { 0 => { let revokable_address = Readable::read(reader)?; - let local_delayedkey = Readable::read(reader)?; + let per_commitment_point = Readable::read(reader)?; let revokable_script = Readable::read(reader)?; - Some((revokable_address, local_delayedkey, revokable_script)) + Some((revokable_address, per_commitment_point, revokable_script)) }, 1 => { None }, _ => return Err(DecodeError::InvalidValue), diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 267d4bbdc06..a73fcee36ff 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -4087,7 +4087,7 @@ fn test_manager_serialize_deserialize_inconsistent_monitor() { } macro_rules! check_spendable_outputs { - ($node: expr, $der_idx: expr) => { + ($node: expr, $der_idx: expr, $chan_signer: expr) => { { let events = $node.chan_monitor.simple_monitor.get_and_clear_pending_events(); let mut txn = Vec::new(); @@ -4123,7 +4123,7 @@ macro_rules! check_spendable_outputs { spend_tx.input[0].witness.push(remotepubkey.serialize().to_vec()); txn.push(spend_tx); }, - SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref key, ref witness_script, ref to_self_delay, ref output } => { + SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref per_commitment_point, ref witness_script, ref to_self_delay, ref output } => { let input = TxIn { previous_output: outpoint.clone(), script_sig: Script::new(), @@ -4141,12 +4141,7 @@ macro_rules! check_spendable_outputs { output: vec![outp], }; let secp_ctx = Secp256k1::new(); - let sighash = Message::from_slice(&bip143::SighashComponents::new(&spend_tx).sighash_all(&spend_tx.input[0], witness_script, output.value)[..]).unwrap(); - let local_delaysig = secp_ctx.sign(&sighash, key); - spend_tx.input[0].witness.push(local_delaysig.serialize_der().to_vec()); - spend_tx.input[0].witness[0].push(SigHashType::All as u8); - spend_tx.input[0].witness.push(vec!()); - spend_tx.input[0].witness.push(witness_script.clone().into_bytes()); + $chan_signer.sign_delayed_transaction(&mut spend_tx, 0, &witness_script, output.value, per_commitment_point, &secp_ctx); txn.push(spend_tx); }, SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => { @@ -4219,7 +4214,8 @@ fn test_claim_sizeable_push_msat() { nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()] }, 0); connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); - let spend_txn = check_spendable_outputs!(nodes[1], 1); + let chan_signer = get_chan_signer!(nodes[1], chan.2); + let spend_txn = check_spendable_outputs!(nodes[1], 1, chan_signer); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], node_txn[0]); } @@ -4249,7 +4245,8 @@ fn test_claim_on_remote_sizeable_push_msat() { check_added_monitors!(nodes[1], 1); connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); - let spend_txn = check_spendable_outputs!(nodes[1], 1); + let chan_signer = get_chan_signer!(nodes[1], chan.2); + let spend_txn = check_spendable_outputs!(nodes[1], 1, chan_signer); assert_eq!(spend_txn.len(), 2); assert_eq!(spend_txn[0], spend_txn[1]); check_spends!(spend_txn[0], node_txn[0]); @@ -4282,7 +4279,8 @@ fn test_claim_on_remote_revoked_sizeable_push_msat() { nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone()] }, 1); connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); - let spend_txn = check_spendable_outputs!(nodes[1], 1); + let chan_signer = get_chan_signer!(nodes[1], chan.2); + let spend_txn = check_spendable_outputs!(nodes[1], 1, chan_signer); assert_eq!(spend_txn.len(), 3); assert_eq!(spend_txn[0], spend_txn[1]); // to_remote output on revoked remote commitment_tx check_spends!(spend_txn[0], revoked_local_txn[0]); @@ -4333,7 +4331,8 @@ fn test_static_spendable_outputs_preimage_tx() { nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone()] }, 1); connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); - let spend_txn = check_spendable_outputs!(nodes[1], 1); + let chan_signer = get_chan_signer!(nodes[1], chan_1.2); + let spend_txn = check_spendable_outputs!(nodes[1], 1, chan_signer); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], node_txn[0]); } @@ -4380,7 +4379,8 @@ fn test_static_spendable_outputs_timeout_tx() { connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); expect_payment_failed!(nodes[1], our_payment_hash, true); - let spend_txn = check_spendable_outputs!(nodes[1], 1); + let chan_signer = get_chan_signer!(nodes[1], chan_1.2); + let spend_txn = check_spendable_outputs!(nodes[1], 1, chan_signer); assert_eq!(spend_txn.len(), 3); // SpendableOutput: remote_commitment_tx.to_remote (*2), timeout_tx.output (*1) check_spends!(spend_txn[2], node_txn[0].clone()); } @@ -4416,7 +4416,8 @@ fn test_static_spendable_outputs_justice_tx_revoked_commitment_tx() { nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone()] }, 1); connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); - let spend_txn = check_spendable_outputs!(nodes[1], 1); + let chan_signer = get_chan_signer!(nodes[1], chan_1.2); + let spend_txn = check_spendable_outputs!(nodes[1], 1, chan_signer); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], node_txn[0]); } @@ -4471,7 +4472,8 @@ fn test_static_spendable_outputs_justice_tx_revoked_htlc_timeout_tx() { connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); // Check B's ChannelMonitor was able to generate the right spendable output descriptor - let spend_txn = check_spendable_outputs!(nodes[1], 1); + let chan_signer = get_chan_signer!(nodes[1], chan_1.2); + let spend_txn = check_spendable_outputs!(nodes[1], 1, chan_signer); assert_eq!(spend_txn.len(), 2); check_spends!(spend_txn[0], node_txn[0]); check_spends!(spend_txn[1], node_txn[2]); @@ -4521,7 +4523,8 @@ fn test_static_spendable_outputs_justice_tx_revoked_htlc_success_tx() { connect_blocks(&nodes[0].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); // Check A's ChannelMonitor was able to generate the right spendable output descriptor - let spend_txn = check_spendable_outputs!(nodes[0], 1); + let chan_signer = get_chan_signer!(nodes[0], chan_1.2); + let spend_txn = check_spendable_outputs!(nodes[0], 1, chan_signer); assert_eq!(spend_txn.len(), 5); // Duplicated SpendableOutput due to block rescan after revoked htlc output tracking assert_eq!(spend_txn[0], spend_txn[1]); assert_eq!(spend_txn[0], spend_txn[2]); @@ -4791,7 +4794,8 @@ fn test_dynamic_spendable_outputs_local_htlc_success_tx() { connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 201, true, header_201.bitcoin_hash()); // Verify that B is able to spend its own HTLC-Success tx thanks to spendable output event given back by its ChannelMonitor - let spend_txn = check_spendable_outputs!(nodes[1], 1); + let chan_signer = get_chan_signer!(nodes[1], chan_1.2); + let spend_txn = check_spendable_outputs!(nodes[1], 1, chan_signer); assert_eq!(spend_txn.len(), 2); check_spends!(spend_txn[0], node_txn[0]); check_spends!(spend_txn[1], node_txn[1]); @@ -5085,7 +5089,8 @@ fn test_dynamic_spendable_outputs_local_htlc_timeout_tx() { expect_payment_failed!(nodes[0], our_payment_hash, true); // Verify that A is able to spend its own HTLC-Timeout tx thanks to spendable output event given back by its ChannelMonitor - let spend_txn = check_spendable_outputs!(nodes[0], 1); + let chan_signer = get_chan_signer!(nodes[0], chan_1.2); + let spend_txn = check_spendable_outputs!(nodes[0], 1, chan_signer); assert_eq!(spend_txn.len(), 3); assert_eq!(spend_txn[0], spend_txn[1]); check_spends!(spend_txn[0], local_txn[0]); @@ -5108,14 +5113,16 @@ fn test_static_output_closing_tx() { nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![closing_tx.clone()] }, 0); connect_blocks(&nodes[0].block_notifier, ANTI_REORG_DELAY - 1, 0, true, header.bitcoin_hash()); - let spend_txn = check_spendable_outputs!(nodes[0], 2); + let chan_signer = get_chan_signer!(nodes[0], chan.2); + let spend_txn = check_spendable_outputs!(nodes[0], 2, chan_signer); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], closing_tx); nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![closing_tx.clone()] }, 0); connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 0, true, header.bitcoin_hash()); - let spend_txn = check_spendable_outputs!(nodes[1], 2); + let chan_signer = get_chan_signer!(nodes[1], chan.2); + let spend_txn = check_spendable_outputs!(nodes[1], 2, chan_signer); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], closing_tx); } @@ -6909,7 +6916,8 @@ fn test_data_loss_protect() { let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42}; nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()]}, 0); connect_blocks(&nodes[0].block_notifier, ANTI_REORG_DELAY - 1, 0, true, header.bitcoin_hash()); - let spend_txn = check_spendable_outputs!(nodes[0], 1); + let chan_signer = get_chan_signer!(nodes[0], chan.2); + let spend_txn = check_spendable_outputs!(nodes[0], 1, chan_signer); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], node_txn[0]); } diff --git a/lightning/src/util/enforcing_trait_impls.rs b/lightning/src/util/enforcing_trait_impls.rs index 90108d009a7..4b9ac375189 100644 --- a/lightning/src/util/enforcing_trait_impls.rs +++ b/lightning/src/util/enforcing_trait_impls.rs @@ -115,6 +115,10 @@ impl ChannelKeys for EnforcingChannelKeys { self.inner.sign_remote_htlc_transaction(bumped_tx, input, witness_script, amount, per_commitment_point, preimage, secp_ctx); } + fn sign_delayed_transaction(&self, spend_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_point: &PublicKey, secp_ctx: &Secp256k1) { + self.inner.sign_delayed_transaction(spend_tx, input, witness_script, amount, per_commitment_point, secp_ctx); + } + fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result { Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap()) } From 7a23c0cc88aad7c5169731df3bdc139c0ab3a2e6 Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Tue, 24 Mar 2020 17:03:26 -0400 Subject: [PATCH 10/10] Remove SecretKey from DynamicOuputP2WPKH descriptor Add sign_payment_transaction in ChanSigner to be able to spend SpendableOutputDescriptor in test framework --- lightning/src/chain/keysinterface.rs | 25 +++++++++++--- lightning/src/ln/channelmonitor.rs | 38 +++++++++------------ lightning/src/ln/functional_tests.rs | 10 ++---- lightning/src/util/enforcing_trait_impls.rs | 6 ++++ 4 files changed, 46 insertions(+), 33 deletions(-) diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index b751e2e4688..389e8568c2e 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -7,6 +7,7 @@ use bitcoin::blockdata::script::{Script, Builder}; use bitcoin::blockdata::opcodes; use bitcoin::network::constants::Network; use bitcoin::util::bip32::{ExtendedPrivKey, ExtendedPubKey, ChildNumber}; +use bitcoin::util::address::Address; use bitcoin::util::bip143; use bitcoin_hashes::{Hash, HashEngine}; @@ -84,7 +85,7 @@ pub enum SpendableOutputDescriptor { /// The outpoint which is spendable outpoint: OutPoint, /// The secret key which must be used to sign the spending transaction - key: SecretKey, + per_commitment_point: PublicKey, /// The output which is reference by the given outpoint output: TxOut, } @@ -106,10 +107,10 @@ impl Writeable for SpendableOutputDescriptor { to_self_delay.write(writer)?; output.write(writer)?; }, - &SpendableOutputDescriptor::DynamicOutputP2WPKH { ref outpoint, ref key, ref output } => { + &SpendableOutputDescriptor::DynamicOutputP2WPKH { ref outpoint, ref per_commitment_point, ref output } => { 2u8.write(writer)?; outpoint.write(writer)?; - key.write(writer)?; + per_commitment_point.write(writer)?; output.write(writer)?; }, } @@ -133,7 +134,7 @@ impl Readable for SpendableOutputDescriptor { }), 2u8 => Ok(SpendableOutputDescriptor::DynamicOutputP2WPKH { outpoint: Readable::read(reader)?, - key: Readable::read(reader)?, + per_commitment_point: Readable::read(reader)?, output: Readable::read(reader)?, }), _ => Err(DecodeError::InvalidValue), @@ -261,6 +262,9 @@ pub trait ChannelKeys : Send+Clone { /// Create a signature for a delayed transaction. fn sign_delayed_transaction(&self, spend_tx: &mut Transaction, input: usize, witness_script: &Script, amount: u64, per_commitment_point: &PublicKey, secp_ctx: &Secp256k1); + /// Create a signture for a payment transaction spending a to_remote output on a remote commitment tx. + fn sign_payment_transaction(&self, spend_tx: &mut Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, network: Network, secp_ctx: &Secp256k1); + /// Signs a channel announcement message with our funding key, proving it comes from one /// of the channel participants. /// @@ -461,6 +465,19 @@ impl ChannelKeys for InMemoryChannelKeys { } } + fn sign_payment_transaction(&self, spend_tx: &mut Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, network: Network, secp_ctx: &Secp256k1) { + if let Ok(payment_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.payment_base_key) { + let remotepubkey = PublicKey::from_secret_key(&secp_ctx, &payment_key); + let witness_script = Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, network).script_pubkey(); + let sighash_parts = bip143::SighashComponents::new(&spend_tx); + let sighash = hash_to_message!(&sighash_parts.sighash_all(&spend_tx.input[input], &witness_script, amount)[..]); + let remotesig = secp_ctx.sign(&sighash, &payment_key); + spend_tx.input[0].witness.push(remotesig.serialize_der().to_vec()); + spend_tx.input[0].witness[0].push(SigHashType::All as u8); + spend_tx.input[0].witness.push(remotepubkey.serialize().to_vec()); + } + } + fn sign_channel_announcement(&self, msg: &msgs::UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) -> Result { let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]); Ok(secp_ctx.sign(&msghash, &self.funding_key)) diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index f8dcd1c5aaf..5fbd76eb774 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -714,7 +714,7 @@ pub struct ChannelMonitor { destination_script: Script, broadcasted_local_revokable_script: Option<(Script, PublicKey, Script)>, - broadcasted_remote_payment_script: Option<(Script, SecretKey)>, + broadcasted_remote_payment_script: Option<(Script, PublicKey)>, shutdown_script: Script, keys: ChanSigner, @@ -1232,9 +1232,7 @@ impl ChannelMonitor { let to_remote_script = Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0) .push_slice(&Hash160::hash(&payment_key.serialize())[..]) .into_script(); - if let Ok(to_remote_key) = chan_utils::derive_private_key(&self.secp_ctx, &their_revocation_point, &self.keys.payment_base_key()) { - self.broadcasted_remote_payment_script = Some((to_remote_script, to_remote_key)); - } + self.broadcasted_remote_payment_script = Some((to_remote_script, their_revocation_point)); } } @@ -1447,18 +1445,17 @@ impl ChannelMonitor { let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret)); let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key); let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.keys.pubkeys().revocation_basepoint)); - let local_payment_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, &per_commitment_point, &self.keys.payment_base_key())); let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.their_delayed_payment_base_key)); let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.our_to_self_delay, &delayed_key); let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh(); - self.broadcasted_remote_payment_script = { - // Note that the Network here is ignored as we immediately drop the address for the - // script_pubkey version - let payment_hash160 = Hash160::hash(&PublicKey::from_secret_key(&self.secp_ctx, &local_payment_key).serialize()); - Some((Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script(), local_payment_key)) - }; + self.broadcasted_remote_payment_script = if let Ok(payment_key) = chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &self.keys.pubkeys().payment_basepoint) { + let payment_script = Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0) + .push_slice(&Hash160::hash(&payment_key.serialize())[..]) + .into_script(); + Some((payment_script, per_commitment_point)) + } else { None }; // First, process non-htlc outputs (to_local & to_remote) for (idx, outp) in tx.output.iter().enumerate() { @@ -1594,14 +1591,13 @@ impl ChannelMonitor { if revocation_points.0 == commitment_number + 1 { Some(point) } else { None } } else { None }; if let Some(revocation_point) = revocation_point_option { - let local_payment_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &self.keys.payment_base_key())); - self.broadcasted_remote_payment_script = { - // Note that the Network here is ignored as we immediately drop the address for the - // script_pubkey version - let payment_hash160 = Hash160::hash(&PublicKey::from_secret_key(&self.secp_ctx, &local_payment_key).serialize()); - Some((Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script(), local_payment_key)) - }; + self.broadcasted_remote_payment_script = if let Ok(payment_key) = chan_utils::derive_public_key(&self.secp_ctx, &revocation_point, &self.keys.pubkeys().payment_basepoint) { + let payment_script = Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0) + .push_slice(&Hash160::hash(&payment_key.serialize())[..]) + .into_script(); + Some((payment_script, *revocation_point)) + } else { None }; // Then, try to find htlc outputs for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() { @@ -2170,7 +2166,7 @@ impl ChannelMonitor { if broadcasted_remote_payment_script.0 == outp.script_pubkey { spendable_output = Some(SpendableOutputDescriptor::DynamicOutputP2WPKH { outpoint: BitcoinOutPoint { txid: tx.txid(), vout: i as u32 }, - key: broadcasted_remote_payment_script.1, + per_commitment_point: broadcasted_remote_payment_script.1, output: outp.clone(), }); break; @@ -2233,8 +2229,8 @@ impl ReadableArgs> for (Sha256dH let broadcasted_remote_payment_script = match ::read(reader)? { 0 => { let payment_address = Readable::read(reader)?; - let payment_key = Readable::read(reader)?; - Some((payment_address, payment_key)) + let per_commitment_point = Readable::read(reader)?; + Some((payment_address, per_commitment_point)) }, 1 => { None }, _ => return Err(DecodeError::InvalidValue), diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index a73fcee36ff..92f1e09dd9b 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -4096,7 +4096,7 @@ macro_rules! check_spendable_outputs { Event::SpendableOutputs { ref outputs } => { for outp in outputs { match *outp { - SpendableOutputDescriptor::DynamicOutputP2WPKH { ref outpoint, ref key, ref output } => { + SpendableOutputDescriptor::DynamicOutputP2WPKH { ref outpoint, ref per_commitment_point, ref output } => { let input = TxIn { previous_output: outpoint.clone(), script_sig: Script::new(), @@ -4114,13 +4114,7 @@ macro_rules! check_spendable_outputs { output: vec![outp], }; let secp_ctx = Secp256k1::new(); - let remotepubkey = PublicKey::from_secret_key(&secp_ctx, &key); - let witness_script = Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey(); - let sighash = Message::from_slice(&bip143::SighashComponents::new(&spend_tx).sighash_all(&spend_tx.input[0], &witness_script, output.value)[..]).unwrap(); - let remotesig = secp_ctx.sign(&sighash, key); - spend_tx.input[0].witness.push(remotesig.serialize_der().to_vec()); - spend_tx.input[0].witness[0].push(SigHashType::All as u8); - spend_tx.input[0].witness.push(remotepubkey.serialize().to_vec()); + $chan_signer.sign_payment_transaction(&mut spend_tx, 0, output.value, per_commitment_point, Network::Testnet, &secp_ctx); txn.push(spend_tx); }, SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref per_commitment_point, ref witness_script, ref to_self_delay, ref output } => { diff --git a/lightning/src/util/enforcing_trait_impls.rs b/lightning/src/util/enforcing_trait_impls.rs index 4b9ac375189..6b7dfcc5666 100644 --- a/lightning/src/util/enforcing_trait_impls.rs +++ b/lightning/src/util/enforcing_trait_impls.rs @@ -9,6 +9,8 @@ use std::sync::{Mutex, Arc}; use bitcoin::blockdata::transaction::Transaction; use bitcoin::blockdata::script::Script; use bitcoin::util::bip143; +use bitcoin::network::constants::Network; + use secp256k1; use secp256k1::key::{SecretKey, PublicKey}; @@ -119,6 +121,10 @@ impl ChannelKeys for EnforcingChannelKeys { self.inner.sign_delayed_transaction(spend_tx, input, witness_script, amount, per_commitment_point, secp_ctx); } + fn sign_payment_transaction(&self, spend_tx: &mut Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, network: Network, secp_ctx: &Secp256k1) { + self.inner.sign_payment_transaction(spend_tx, input, amount, per_commitment_point, network, secp_ctx); + } + fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result { Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap()) }