diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index 09db795e13a..d18122d85de 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -1180,6 +1180,26 @@ where panic!("{}", err_str); }, } + + // We may need to start monitoring for any alternative funding transactions. + if let Some(ref chain_source) = self.chain_source { + for (funding_outpoint, funding_script) in + update.internal_renegotiated_funding_data() + { + log_trace!( + logger, + "Registering renegotiated funding outpoint {} with the filter to monitor confirmations and spends", + funding_outpoint + ); + chain_source.register_tx(&funding_outpoint.txid, &funding_script); + chain_source.register_output(WatchedOutput { + block_hash: None, + outpoint: funding_outpoint, + script_pubkey: funding_script, + }); + } + } + if update_res.is_err() { ChannelMonitorUpdateStatus::InProgress } else { diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 54f170bcbe3..5ca1bd86976 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -113,6 +113,37 @@ pub struct ChannelMonitorUpdate { pub channel_id: Option, } +impl ChannelMonitorUpdate { + pub(crate) fn internal_renegotiated_funding_data( + &self, + ) -> impl Iterator + '_ { + self.updates.iter().filter_map(|update| match update { + ChannelMonitorUpdateStep::RenegotiatedFunding { channel_parameters, .. } => { + let funding_outpoint = channel_parameters + .funding_outpoint + .expect("Renegotiated funding must always have known outpoint"); + let funding_script = channel_parameters.make_funding_redeemscript().to_p2wsh(); + Some((funding_outpoint, funding_script)) + }, + _ => None, + }) + } + + /// Returns a `Vec` of new (funding outpoint, funding script) to monitor the chain for as a + /// result of a renegotiated funding transaction. + #[cfg(c_bindings)] + pub fn renegotiated_funding_data(&self) -> Vec<(OutPoint, ScriptBuf)> { + self.internal_renegotiated_funding_data().collect() + } + + /// Returns an iterator of new (funding outpoint, funding script) to monitor the chain for as a + /// result of a renegotiated funding transaction. + #[cfg(not(c_bindings))] + pub fn renegotiated_funding_data(&self) -> impl Iterator + '_ { + self.internal_renegotiated_funding_data() + } +} + /// LDK prior to 0.1 used this constant as the [`ChannelMonitorUpdate::update_id`] for any /// [`ChannelMonitorUpdate`]s which were generated after the channel was closed. const LEGACY_CLOSED_CHANNEL_UPDATE_ID: u64 = u64::MAX; @@ -592,7 +623,6 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent, #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum ChannelMonitorUpdateStep { - // Update LatestHolderCommitmentTXInfo in channel.rs if adding new fields to this variant. LatestHolderCommitmentTXInfo { commitment_tx: HolderCommitmentTransaction, /// Note that LDK after 0.0.115 supports this only containing dust HTLCs (implying the @@ -605,6 +635,11 @@ pub(crate) enum ChannelMonitorUpdateStep { claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>, nondust_htlc_sources: Vec, }, + LatestHolderCommitment { + commitment_txs: Vec, + htlc_data: CommitmentHTLCData, + claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>, + }, LatestCounterpartyCommitmentTXInfo { commitment_txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option>)>, @@ -614,11 +649,9 @@ pub(crate) enum ChannelMonitorUpdateStep { to_broadcaster_value_sat: Option, to_countersignatory_value_sat: Option, }, - LatestCounterpartyCommitmentTX { - // The dust and non-dust htlcs for that commitment - htlc_outputs: Vec<(HTLCOutputInCommitment, Option>)>, - // Contains only the non-dust htlcs - commitment_tx: CommitmentTransaction, + LatestCounterpartyCommitment { + commitment_txs: Vec, + htlc_data: CommitmentHTLCData, }, PaymentPreimage { payment_preimage: PaymentPreimage, @@ -640,6 +673,11 @@ pub(crate) enum ChannelMonitorUpdateStep { ShutdownScript { scriptpubkey: ScriptBuf, }, + RenegotiatedFunding { + channel_parameters: ChannelTransactionParameters, + holder_commitment_tx: HolderCommitmentTransaction, + counterparty_commitment_tx: CommitmentTransaction, + }, } impl ChannelMonitorUpdateStep { @@ -647,12 +685,14 @@ impl ChannelMonitorUpdateStep { fn variant_name(&self) -> &'static str { match self { ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { .. } => "LatestHolderCommitmentTXInfo", + ChannelMonitorUpdateStep::LatestHolderCommitment { .. } => "LatestHolderCommitment", ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. } => "LatestCounterpartyCommitmentTXInfo", - ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { .. } => "LatestCounterpartyCommitmentTX", + ChannelMonitorUpdateStep::LatestCounterpartyCommitment { .. } => "LatestCounterpartyCommitment", ChannelMonitorUpdateStep::PaymentPreimage { .. } => "PaymentPreimage", ChannelMonitorUpdateStep::CommitmentSecret { .. } => "CommitmentSecret", ChannelMonitorUpdateStep::ChannelForceClosed { .. } => "ChannelForceClosed", ChannelMonitorUpdateStep::ShutdownScript { .. } => "ShutdownScript", + ChannelMonitorUpdateStep::RenegotiatedFunding { .. } => "RenegotiatedFunding", } } } @@ -687,9 +727,19 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep, (5, ShutdownScript) => { (0, scriptpubkey, required), }, - (6, LatestCounterpartyCommitmentTX) => { - (0, htlc_outputs, required_vec), - (2, commitment_tx, required), + (6, LatestCounterpartyCommitment) => { + (1, commitment_txs, required_vec), + (3, htlc_data, required), + }, + (8, LatestHolderCommitment) => { + (1, commitment_txs, required_vec), + (3, htlc_data, required), + (5, claimed_htlcs, required_vec), + }, + (10, RenegotiatedFunding) => { + (1, channel_parameters, (required: ReadableArgs, None)), + (3, holder_commitment_tx, required), + (5, counterparty_commitment_tx, required), }, ); @@ -946,12 +996,12 @@ where } } -#[derive(Clone, PartialEq)] -struct CommitmentHTLCData { +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) struct CommitmentHTLCData { // These must be sorted in increasing output index order to match the expected order of the // HTLCs in the `CommitmentTransaction`. - nondust_htlc_sources: Vec, - dust_htlcs: Vec<(HTLCOutputInCommitment, Option)>, + pub nondust_htlc_sources: Vec, + pub dust_htlcs: Vec<(HTLCOutputInCommitment, Option)>, } impl CommitmentHTLCData { @@ -960,6 +1010,11 @@ impl CommitmentHTLCData { } } +impl_writeable_tlv_based!(CommitmentHTLCData, { + (1, nondust_htlc_sources, required_vec), + (3, dust_htlcs, required_vec), +}); + impl TryFrom for CommitmentHTLCData { type Error = (); #[rustfmt::skip] @@ -1024,9 +1079,69 @@ struct FundingScope { prev_holder_commitment_tx: Option, } +impl FundingScope { + fn funding_outpoint(&self) -> OutPoint { + let funding_outpoint = self.channel_parameters.funding_outpoint.as_ref(); + *funding_outpoint.expect("Funding outpoint must be set for active monitor") + } + + fn funding_txid(&self) -> Txid { + self.funding_outpoint().txid + } +} + +impl Writeable for FundingScope { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + write_tlv_fields!(w, { + (1, self.channel_parameters, (required: ReadableArgs, None)), + (3, self.current_counterparty_commitment_txid, required), + (5, self.prev_counterparty_commitment_txid, option), + (7, self.current_holder_commitment_tx, required), + (9, self.prev_holder_commitment_tx, option), + (11, self.counterparty_claimable_outpoints, required), + }); + Ok(()) + } +} + +impl Readable for FundingScope { + fn read(r: &mut R) -> Result { + let mut channel_parameters = RequiredWrapper(None); + let mut current_counterparty_commitment_txid = RequiredWrapper(None); + let mut prev_counterparty_commitment_txid = None; + let mut current_holder_commitment_tx = RequiredWrapper(None); + let mut prev_holder_commitment_tx = None; + let mut counterparty_claimable_outpoints = RequiredWrapper(None); + + read_tlv_fields!(r, { + (1, channel_parameters, (required: ReadableArgs, None)), + (3, current_counterparty_commitment_txid, required), + (5, prev_counterparty_commitment_txid, option), + (7, current_holder_commitment_tx, required), + (9, prev_holder_commitment_tx, option), + (11, counterparty_claimable_outpoints, required), + }); + + let channel_parameters: ChannelTransactionParameters = channel_parameters.0.unwrap(); + let redeem_script = channel_parameters.make_funding_redeemscript(); + + Ok(Self { + script_pubkey: redeem_script.to_p2wsh(), + redeem_script, + channel_parameters, + current_counterparty_commitment_txid: current_counterparty_commitment_txid.0.unwrap(), + prev_counterparty_commitment_txid, + current_holder_commitment_tx: current_holder_commitment_tx.0.unwrap(), + prev_holder_commitment_tx, + counterparty_claimable_outpoints: counterparty_claimable_outpoints.0.unwrap(), + }) + } +} + #[derive(Clone, PartialEq)] pub(crate) struct ChannelMonitorImpl { funding: FundingScope, + pending_funding: Vec, latest_update_id: u64, commitment_transaction_number_obscure_factor: u64, @@ -1467,6 +1582,7 @@ impl Writeable for ChannelMonitorImpl { (27, self.first_confirmed_funding_txo, required), (29, self.initial_counterparty_commitment_tx, option), (31, self.funding.channel_parameters, required), + (32, self.pending_funding, optional_vec), }); Ok(()) @@ -1636,6 +1752,7 @@ impl ChannelMonitor { current_holder_commitment_tx: initial_holder_commitment_tx, prev_holder_commitment_tx: None, }, + pending_funding: vec![], latest_update_id: 0, commitment_transaction_number_obscure_factor, @@ -1728,14 +1845,11 @@ impl ChannelMonitor { /// /// This is used to provide the counterparty commitment transaction directly to the monitor /// before the initial persistence of a new channel. - pub(crate) fn provide_initial_counterparty_commitment_tx( - &self, commitment_tx: CommitmentTransaction, logger: &L, - ) where - L::Target: Logger, - { + pub(crate) fn provide_initial_counterparty_commitment_tx( + &self, commitment_tx: CommitmentTransaction, + ) { let mut inner = self.inner.lock().unwrap(); - let logger = WithChannelMonitor::from_impl(logger, &*inner, None); - inner.provide_initial_counterparty_commitment_tx(commitment_tx, &logger); + inner.provide_initial_counterparty_commitment_tx(commitment_tx); } /// Informs this monitor of the latest counterparty (ie non-broadcastable) commitment transaction. @@ -1743,19 +1857,17 @@ impl ChannelMonitor { /// possibly future revocation/preimage information) to claim outputs where possible. /// We cache also the mapping hash:commitment number to lighten pruning of old preimages by watchtowers. #[cfg(test)] - #[rustfmt::skip] - fn provide_latest_counterparty_commitment_tx( - &self, - txid: Txid, - htlc_outputs: Vec<(HTLCOutputInCommitment, Option>)>, - commitment_number: u64, - their_per_commitment_point: PublicKey, - logger: &L, - ) where L::Target: Logger { + fn provide_latest_counterparty_commitment_tx( + &self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option>)>, + commitment_number: u64, their_per_commitment_point: PublicKey, + ) { let mut inner = self.inner.lock().unwrap(); - let logger = WithChannelMonitor::from_impl(logger, &*inner, None); inner.provide_latest_counterparty_commitment_tx( - txid, htlc_outputs, commitment_number, their_per_commitment_point, &logger) + txid, + htlc_outputs, + commitment_number, + their_per_commitment_point, + ) } #[cfg(test)] @@ -1764,7 +1876,9 @@ impl ChannelMonitor { &self, holder_commitment_tx: HolderCommitmentTransaction, htlc_outputs: Vec<(HTLCOutputInCommitment, Option, Option)>, ) { - self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new()) + self.inner.lock().unwrap().provide_latest_holder_commitment_tx( + holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new() + ).unwrap() } /// This is used to provide payment preimage(s) out-of-band during startup without updating the @@ -1862,14 +1976,16 @@ impl ChannelMonitor { { let lock = self.inner.lock().unwrap(); let logger = WithChannelMonitor::from_impl(logger, &*lock, None); - log_trace!(&logger, "Registering funding outpoint {}", &lock.get_funding_txo()); - let funding_outpoint = lock.get_funding_txo(); - filter.register_tx(&funding_outpoint.txid, &lock.funding.script_pubkey); + for funding in core::iter::once(&lock.funding).chain(&lock.pending_funding) { + let funding_outpoint = funding.funding_outpoint(); + log_trace!(&logger, "Registering funding outpoint {} with the filter to monitor confirmations", &funding_outpoint); + filter.register_tx(&funding_outpoint.txid, &funding.script_pubkey); + } for (txid, outputs) in lock.get_outputs_to_watch().iter() { for (index, script_pubkey) in outputs.iter() { assert!(*index <= u16::MAX as u32); let outpoint = OutPoint { txid: *txid, index: *index as u16 }; - log_trace!(logger, "Registering outpoint {} with the filter for monitoring spends", outpoint); + log_trace!(logger, "Registering outpoint {} with the filter to monitor spend", outpoint); filter.register_output(WatchedOutput { block_hash: None, outpoint, @@ -3138,9 +3254,9 @@ impl ChannelMonitorImpl { } #[rustfmt::skip] - fn provide_initial_counterparty_commitment_tx( - &mut self, commitment_tx: CommitmentTransaction, logger: &WithChannelMonitor, - ) where L::Target: Logger { + fn provide_initial_counterparty_commitment_tx( + &mut self, commitment_tx: CommitmentTransaction, + ) { // We populate this field for downgrades self.initial_counterparty_commitment_info = Some((commitment_tx.per_commitment_point(), commitment_tx.feerate_per_kw(), commitment_tx.to_broadcaster_value_sat(), commitment_tx.to_countersignatory_value_sat())); @@ -3151,17 +3267,16 @@ impl ChannelMonitorImpl { } self.provide_latest_counterparty_commitment_tx(commitment_tx.trust().txid(), Vec::new(), commitment_tx.commitment_number(), - commitment_tx.per_commitment_point(), logger); + commitment_tx.per_commitment_point()); // Soon, we will only populate this field self.initial_counterparty_commitment_tx = Some(commitment_tx); } #[rustfmt::skip] - fn provide_latest_counterparty_commitment_tx( - &mut self, txid: Txid, - htlc_outputs: Vec<(HTLCOutputInCommitment, Option>)>, - commitment_number: u64, their_per_commitment_point: PublicKey, logger: &WithChannelMonitor, - ) where L::Target: Logger { + fn provide_latest_counterparty_commitment_tx( + &mut self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option>)>, + commitment_number: u64, their_per_commitment_point: PublicKey, + ) { // TODO: Encrypt the htlc_outputs data with the single-hash of the commitment transaction // so that a remote monitor doesn't learn anything unless there is a malicious close. // (only maybe, sadly we cant do the same for local info, as we need to be aware of @@ -3170,11 +3285,11 @@ impl ChannelMonitorImpl { self.counterparty_hash_commitment_number.insert(htlc.payment_hash, commitment_number); } - log_trace!(logger, "Tracking new counterparty commitment transaction with txid {} at commitment number {} with {} HTLC outputs", txid, commitment_number, htlc_outputs.len()); self.funding.prev_counterparty_commitment_txid = self.funding.current_counterparty_commitment_txid.take(); self.funding.current_counterparty_commitment_txid = Some(txid); - self.funding.counterparty_claimable_outpoints.insert(txid, htlc_outputs.clone()); + self.funding.counterparty_claimable_outpoints.insert(txid, htlc_outputs); self.current_counterparty_commitment_number = commitment_number; + //TODO: Merge this into the other per-counterparty-transaction output storage stuff match self.their_cur_per_commitment_points { Some(old_points) => { @@ -3196,6 +3311,58 @@ impl ChannelMonitorImpl { } } + fn update_counterparty_commitment_data( + &mut self, commitment_txs: &[CommitmentTransaction], htlc_data: &CommitmentHTLCData, + ) -> Result<(), &'static str> { + self.verify_matching_commitment_transactions(commitment_txs.iter())?; + + let htlcs_for_commitment = |commitment: &CommitmentTransaction| { + let mut nondust_htlcs = commitment.nondust_htlcs().iter(); + let mut sources = htlc_data.nondust_htlc_sources.iter(); + let nondust_htlcs = core::iter::from_fn(move || { + let htlc = nondust_htlcs.next()?.clone(); + let source = (!htlc.offered).then(|| { + let source = sources + .next() + .expect("Every offered non-dust HTLC should have a corresponding source") + .clone(); + Box::new(source) + }); + Some((htlc, source)) + }); + + let dust_htlcs = htlc_data + .dust_htlcs + .clone() + .into_iter() + .map(|(htlc, source)| (htlc, source.map(|source| Box::new(source)))); + + nondust_htlcs.chain(dust_htlcs).collect::>() + }; + + let current_funding_commitment_tx = commitment_txs.first().unwrap(); + self.provide_latest_counterparty_commitment_tx( + current_funding_commitment_tx.trust().txid(), + htlcs_for_commitment(current_funding_commitment_tx), + current_funding_commitment_tx.commitment_number(), + current_funding_commitment_tx.per_commitment_point(), + ); + + for (pending_funding, commitment_tx) in + self.pending_funding.iter_mut().zip(commitment_txs.iter().skip(1)) + { + let commitment_txid = commitment_tx.trust().txid(); + pending_funding.prev_counterparty_commitment_txid = + pending_funding.current_counterparty_commitment_txid.take(); + pending_funding.current_counterparty_commitment_txid = Some(commitment_txid); + pending_funding + .counterparty_claimable_outpoints + .insert(commitment_txid, htlcs_for_commitment(commitment_tx)); + } + + Ok(()) + } + /// Informs this monitor of the latest holder (ie broadcastable) commitment transaction. The /// monitor watches for timeouts and may broadcast it if we approach such a timeout. Thus, it /// is important that any clones of this channel monitor (including remote clones) by kept @@ -3203,11 +3370,11 @@ impl ChannelMonitorImpl { /// Panics if set_on_holder_tx_csv has never been called. #[rustfmt::skip] fn provide_latest_holder_commitment_tx( - &mut self, mut holder_commitment_tx: HolderCommitmentTransaction, - htlc_outputs: Vec<(HTLCOutputInCommitment, Option, Option)>, + &mut self, holder_commitment_tx: HolderCommitmentTransaction, + htlc_outputs: &[(HTLCOutputInCommitment, Option, Option)], claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], mut nondust_htlc_sources: Vec, - ) { - let dust_htlcs: Vec<_> = if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) { + ) -> Result<(), &'static str> { + let dust_htlcs = if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) { // If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the // `holder_commitment_tx`. In the future, we'll no longer provide the redundant data // and just pass in source data via `nondust_htlc_sources`. @@ -3223,19 +3390,17 @@ impl ChannelMonitorImpl { // Backfill the non-dust HTLC sources. debug_assert!(nondust_htlc_sources.is_empty()); nondust_htlc_sources.reserve_exact(holder_commitment_tx.nondust_htlcs().len()); - let dust_htlcs = htlc_outputs.into_iter().filter_map(|(htlc, _, source)| { + htlc_outputs.iter().filter_map(|(htlc, _, source)| { // Filter our non-dust HTLCs, while at the same time pushing their sources into // `nondust_htlc_sources`. if htlc.transaction_output_index.is_none() { - return Some((htlc, source)); + return Some((htlc.clone(), source.clone())); } if htlc.offered { - nondust_htlc_sources.push(source.expect("Outbound HTLCs should have a source")); + nondust_htlc_sources.push(source.clone().expect("Outbound HTLCs should have a source")); } None - }).collect(); - - dust_htlcs + }).collect() } else { // If we don't have any non-dust HTLCs in htlc_outputs, assume they were all passed via // `nondust_htlc_sources`, building up the final htlc_outputs by combining @@ -3262,30 +3427,112 @@ impl ChannelMonitorImpl { assert!(sources.next().is_none(), "All HTLC sources should have been exhausted"); // This only includes dust HTLCs as checked above. - htlc_outputs.into_iter().map(|(htlc, _, source)| (htlc, source)).collect() + htlc_outputs.iter().map(|(htlc, _, source)| (htlc.clone(), source.clone())).collect() }; - self.current_holder_commitment_number = holder_commitment_tx.trust().commitment_number(); - self.onchain_tx_handler.provide_latest_holder_tx(holder_commitment_tx.clone()); + let htlc_data = CommitmentHTLCData { nondust_htlc_sources, dust_htlcs }; + self.update_holder_commitment_data( + vec![holder_commitment_tx], htlc_data, claimed_htlcs.to_vec(), + ) + } - mem::swap(&mut holder_commitment_tx, &mut self.funding.current_holder_commitment_tx); - self.funding.prev_holder_commitment_tx = Some(holder_commitment_tx); - let mut holder_htlc_data = CommitmentHTLCData { nondust_htlc_sources, dust_htlcs }; - mem::swap(&mut holder_htlc_data, &mut self.current_holder_htlc_data); - self.prev_holder_htlc_data = Some(holder_htlc_data); + fn verify_matching_commitment_transactions< + 'a, + I: Iterator + Clone, + >( + &self, commitment_txs: I, + ) -> Result<(), &'static str> { + if self.pending_funding.len() + 1 != commitment_txs.clone().count() { + return Err("Commitment transaction count mismatch"); + } + + let mut other_commitment_tx = None::<&CommitmentTransaction>; + for (funding, commitment_tx) in + core::iter::once(&self.funding).chain(self.pending_funding.iter()).zip(commitment_txs) + { + let trusted_tx = &commitment_tx.trust().built_transaction().transaction; + if trusted_tx.input.len() != 1 { + return Err("Commitment transactions must only spend one input"); + } + let funding_outpoint_spent = trusted_tx.input[0].previous_output; + if funding_outpoint_spent != funding.funding_outpoint().into_bitcoin_outpoint() { + return Err("Commitment transaction spends invalid funding outpoint"); + } + + if let Some(other_commitment_tx) = other_commitment_tx { + if commitment_tx.commitment_number() != other_commitment_tx.commitment_number() { + return Err("Commitment number mismatch"); + } + if commitment_tx.per_commitment_point() + != other_commitment_tx.per_commitment_point() + { + return Err("Per-commitment-point mismatch"); + } + if commitment_tx.feerate_per_kw() != other_commitment_tx.feerate_per_kw() { + return Err("Commitment fee rate mismatch"); + } + let nondust_htlcs = commitment_tx.nondust_htlcs(); + let other_nondust_htlcs = other_commitment_tx.nondust_htlcs(); + if nondust_htlcs.len() != other_nondust_htlcs.len() { + return Err("Non-dust HTLC count mismatch"); + } + for (nondust_htlc, other_nondust_htlc) in + nondust_htlcs.iter().zip(other_nondust_htlcs.iter()) + { + if !nondust_htlc.is_data_equal(other_nondust_htlc) { + return Err("Non-dust HTLC mismatch"); + } + } + } + + other_commitment_tx = Some(commitment_tx); + } + + Ok(()) + } + + fn update_holder_commitment_data( + &mut self, commitment_txs: Vec, + mut htlc_data: CommitmentHTLCData, claimed_htlcs: Vec<(SentHTLCId, PaymentPreimage)>, + ) -> Result<(), &'static str> { + self.verify_matching_commitment_transactions( + commitment_txs.iter().map(|holder_commitment_tx| holder_commitment_tx.deref()), + )?; + + let current_funding_commitment_tx = commitment_txs.first().unwrap(); + self.current_holder_commitment_number = current_funding_commitment_tx.commitment_number(); + self.onchain_tx_handler.provide_latest_holder_tx(current_funding_commitment_tx.clone()); + for (funding, mut commitment_tx) in core::iter::once(&mut self.funding) + .chain(self.pending_funding.iter_mut()) + .zip(commitment_txs.into_iter()) + { + mem::swap(&mut commitment_tx, &mut funding.current_holder_commitment_tx); + funding.prev_holder_commitment_tx = Some(commitment_tx); + } + + mem::swap(&mut htlc_data, &mut self.current_holder_htlc_data); + self.prev_holder_htlc_data = Some(htlc_data); for (claimed_htlc_id, claimed_preimage) in claimed_htlcs { - #[cfg(debug_assertions)] { - let cur_counterparty_htlcs = self.funding.counterparty_claimable_outpoints.get( - &self.funding.current_counterparty_commitment_txid.unwrap()).unwrap(); + #[cfg(debug_assertions)] + { + let cur_counterparty_htlcs = self + .funding + .counterparty_claimable_outpoints + .get(&self.funding.current_counterparty_commitment_txid.unwrap()) + .unwrap(); assert!(cur_counterparty_htlcs.iter().any(|(_, source_opt)| { if let Some(source) = source_opt { - SentHTLCId::from_source(source) == *claimed_htlc_id - } else { false } + SentHTLCId::from_source(source) == claimed_htlc_id + } else { + false + } })); } - self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage); + self.counterparty_fulfilled_htlcs.insert(claimed_htlc_id, claimed_preimage); } + + Ok(()) } /// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all @@ -3453,6 +3700,128 @@ impl ChannelMonitorImpl { ); } + fn renegotiated_funding( + &mut self, logger: &WithChannelMonitor, + channel_parameters: &ChannelTransactionParameters, + alternative_holder_commitment_tx: &HolderCommitmentTransaction, + alternative_counterparty_commitment_tx: &CommitmentTransaction, + ) -> Result<(), ()> + where + L::Target: Logger, + { + let redeem_script = channel_parameters.make_funding_redeemscript(); + let script_pubkey = redeem_script.to_p2wsh(); + let alternative_counterparty_commitment_txid = + alternative_counterparty_commitment_tx.trust().txid(); + + // Both the current counterparty commitment and the alternative one share the same set of + // non-dust and dust HTLCs in the same order, though the index of each non-dust HTLC may be + // different. + // + // We clone all HTLCs and their sources to use in the alternative funding scope, and update + // each non-dust HTLC with their corresponding index in the alternative counterparty + // commitment. + let current_counterparty_commitment_htlcs = + if let Some(txid) = &self.funding.current_counterparty_commitment_txid { + self.funding.counterparty_claimable_outpoints.get(txid).unwrap() + } else { + debug_assert!(false); + log_error!( + logger, + "Received funding renegotiation while initial funding negotiation is still pending" + ); + return Err(()); + }; + let mut htlcs_with_sources = current_counterparty_commitment_htlcs.clone(); + let alternative_htlcs = alternative_counterparty_commitment_tx.nondust_htlcs(); + + let expected_non_dust_htlc_count = htlcs_with_sources + .iter() + // Non-dust HTLCs always come first, so the position of the first dust HTLC is equal to + // our non-dust HTLC count. + .position(|(htlc, _)| htlc.transaction_output_index.is_none()) + .unwrap_or(htlcs_with_sources.len()); + if alternative_htlcs.len() != expected_non_dust_htlc_count { + log_error!( + logger, + "Received alternative counterparty commitment with HTLC count mismatch" + ); + return Err(()); + } + + for (alternative_htlc, (htlc, _)) in + alternative_htlcs.iter().zip(htlcs_with_sources.iter_mut()) + { + debug_assert!(htlc.transaction_output_index.is_some()); + debug_assert!(alternative_htlc.transaction_output_index.is_some()); + if !alternative_htlc.is_data_equal(htlc) { + log_error!( + logger, + "Received alternative counterparty commitment with non-dust HTLC mismatch" + ); + return Err(()); + } + htlc.transaction_output_index = alternative_htlc.transaction_output_index; + } + + let mut counterparty_claimable_outpoints = new_hash_map(); + counterparty_claimable_outpoints + .insert(alternative_counterparty_commitment_txid, htlcs_with_sources); + + // TODO(splicing): Enforce any necessary RBF validity checks. + let alternative_funding = FundingScope { + script_pubkey: script_pubkey.clone(), + redeem_script, + channel_parameters: channel_parameters.clone(), + current_counterparty_commitment_txid: Some(alternative_counterparty_commitment_txid), + prev_counterparty_commitment_txid: None, + counterparty_claimable_outpoints, + current_holder_commitment_tx: alternative_holder_commitment_tx.clone(), + prev_holder_commitment_tx: None, + }; + let alternative_funding_outpoint = alternative_funding.funding_outpoint(); + + if self + .pending_funding + .iter() + .any(|funding| funding.funding_txid() == alternative_funding_outpoint.txid) + { + log_error!( + logger, + "Renegotiated funding transaction with a duplicate funding txid {}", + alternative_funding_outpoint.txid + ); + return Err(()); + } + + if let Some(parent_funding_txid) = channel_parameters.splice_parent_funding_txid.as_ref() { + // Only one splice can be negotiated at a time after we've exchanged `channel_ready` + // (implying our funding is confirmed) that spends our currently locked funding. + if !self.pending_funding.is_empty() { + log_error!( + logger, + "Negotiated splice while channel is pending channel_ready/splice_locked" + ); + return Err(()); + } + if *parent_funding_txid != self.funding.funding_txid() { + log_error!( + logger, + "Negotiated splice that does not spend currently locked funding transaction" + ); + return Err(()); + } + } + + self.outputs_to_watch.insert( + alternative_funding_outpoint.txid, + vec![(alternative_funding_outpoint.index as u32, script_pubkey)], + ); + self.pending_funding.push(alternative_funding); + + Ok(()) + } + #[rustfmt::skip] fn update_monitor( &mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &WithChannelMonitor @@ -3506,18 +3875,46 @@ impl ChannelMonitorImpl { ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => { log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info"); if self.lockdown_from_offchain { panic!(); } - self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone()); + if let Err(e) = self.provide_latest_holder_commitment_tx( + commitment_tx.clone(), htlc_outputs, &claimed_htlcs, + nondust_htlc_sources.clone() + ) { + log_error!(logger, "Failed updating latest holder commitment transaction info: {}", e); + ret = Err(()); + } } - // Soon we will drop the `LatestCounterpartyCommitmentTXInfo` variant in favor of `LatestCounterpartyCommitmentTX`. + ChannelMonitorUpdateStep::LatestHolderCommitment { + commitment_txs, htlc_data, claimed_htlcs, + } => { + log_trace!(logger, "Updating ChannelMonitor with latest holder commitment"); + assert!(!self.lockdown_from_offchain); + if let Err(e) = self.update_holder_commitment_data( + commitment_txs.clone(), htlc_data.clone(), claimed_htlcs.clone(), + ) { + log_error!(logger, "Failed updating latest holder commitment state: {}", e); + ret = Err(()); + } + }, + // Soon we will drop the `LatestCounterpartyCommitmentTXInfo` variant in favor of `LatestCounterpartyCommitment`. // For now we just add the code to handle the new updates. - // Next step: in channel, switch channel monitor updates to use the `LatestCounterpartyCommitmentTX` variant. + // Next step: in channel, switch channel monitor updates to use the `LatestCounterpartyCommitment` variant. ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => { log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info"); - self.provide_latest_counterparty_commitment_tx(*commitment_txid, htlc_outputs.clone(), *commitment_number, *their_per_commitment_point, logger) + if self.pending_funding.is_empty() { + self.provide_latest_counterparty_commitment_tx(*commitment_txid, htlc_outputs.clone(), *commitment_number, *their_per_commitment_point) + } else { + log_error!(logger, "Received unexpected non-splice counterparty commitment monitor update"); + ret = Err(()); + } }, - ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { htlc_outputs, commitment_tx } => { - log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info"); - self.provide_latest_counterparty_commitment_tx(commitment_tx.trust().txid(), htlc_outputs.clone(), commitment_tx.commitment_number(), commitment_tx.per_commitment_point(), logger) + ChannelMonitorUpdateStep::LatestCounterpartyCommitment { + commitment_txs, htlc_data, + } => { + log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment"); + if let Err(e) = self.update_counterparty_commitment_data(commitment_txs, htlc_data) { + log_error!(logger, "Failed updating latest counterparty commitment state: {}", e); + ret = Err(()); + } }, ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage, payment_info } => { log_trace!(logger, "Updating ChannelMonitor with payment preimage"); @@ -3532,6 +3929,17 @@ impl ChannelMonitorImpl { ret = Err(()); } }, + ChannelMonitorUpdateStep::RenegotiatedFunding { + channel_parameters, holder_commitment_tx, counterparty_commitment_tx, + } => { + log_trace!(logger, "Updating ChannelMonitor with alternative holder and counterparty commitment transactions for funding txid {}", + channel_parameters.funding_outpoint.unwrap().txid); + if let Err(_) = self.renegotiated_funding( + logger, channel_parameters, holder_commitment_tx, counterparty_commitment_tx, + ) { + ret = Err(()); + } + }, ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } => { log_trace!(logger, "Updating ChannelMonitor: channel force closed, should broadcast: {}", should_broadcast); self.lockdown_from_offchain = true; @@ -3580,10 +3988,12 @@ impl ChannelMonitorImpl { for update in updates.updates.iter() { match update { ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { .. } + |ChannelMonitorUpdateStep::LatestHolderCommitment { .. } |ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. } - |ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { .. } + |ChannelMonitorUpdateStep::LatestCounterpartyCommitment { .. } |ChannelMonitorUpdateStep::ShutdownScript { .. } - |ChannelMonitorUpdateStep::CommitmentSecret { .. } => + |ChannelMonitorUpdateStep::CommitmentSecret { .. } + |ChannelMonitorUpdateStep::RenegotiatedFunding { .. } => is_pre_close_update = true, // After a channel is closed, we don't communicate with our peer about it, so the // only things we will update is getting a new preimage (from a different channel) @@ -3740,7 +4150,7 @@ impl ChannelMonitorImpl { update.updates.iter().filter_map(|update| { // Soon we will drop the first branch here in favor of the second. // In preparation, we just add the second branch without deleting the first. - // Next step: in channel, switch channel monitor updates to use the `LatestCounterpartyCommitmentTX` variant. + // Next step: in channel, switch channel monitor updates to use the `LatestCounterpartyCommitment` variant. match update { &ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, ref htlc_outputs, commitment_number, their_per_commitment_point, @@ -3758,16 +4168,17 @@ impl ChannelMonitorImpl { debug_assert_eq!(commitment_tx.trust().txid(), commitment_txid); - Some(commitment_tx) + Some(vec![commitment_tx]) }, - &ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { - htlc_outputs: _, ref commitment_tx, - } => { - Some(commitment_tx.clone()) + &ChannelMonitorUpdateStep::LatestCounterpartyCommitment { ref commitment_txs, .. } => { + Some(commitment_txs.clone()) + }, + &ChannelMonitorUpdateStep::RenegotiatedFunding { ref counterparty_commitment_tx, .. } => { + Some(vec![counterparty_commitment_tx.clone()]) }, _ => None, } - }).collect() + }).flatten().collect() } #[rustfmt::skip] @@ -5438,6 +5849,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP let mut payment_preimages_with_info: Option> = None; let mut first_confirmed_funding_txo = RequiredWrapper(None); let mut channel_parameters = None; + let mut pending_funding = None; read_tlv_fields!(reader, { (1, funding_spend_confirmed, option), (3, htlcs_resolved_on_chain, optional_vec), @@ -5455,6 +5867,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP (27, first_confirmed_funding_txo, (default_value, outpoint)), (29, initial_counterparty_commitment_tx, option), (31, channel_parameters, (option: ReadableArgs, None)), + (32, pending_funding, optional_vec), }); if let Some(payment_preimages_with_info) = payment_preimages_with_info { if payment_preimages_with_info.len() != payment_preimages.len() { @@ -5570,6 +5983,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP current_holder_commitment_tx, prev_holder_commitment_tx, }, + pending_funding: pending_funding.unwrap_or(vec![]), latest_update_id, commitment_transaction_number_obscure_factor, @@ -5888,9 +6302,9 @@ mod tests { monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(), nondust_htlcs.iter().map(|htlc| (htlc.clone(), Some(dummy_sig), Some(dummy_source.clone()))).collect()); monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"1").to_byte_array()), - preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger); + preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key); monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"2").to_byte_array()), - preimages_slice_to_htlc_outputs!(preimages[15..20]), 281474976710654, dummy_key, &logger); + preimages_slice_to_htlc_outputs!(preimages[15..20]), 281474976710654, dummy_key); for &(ref preimage, ref hash) in preimages.iter() { let bounded_fee_estimator = LowerBoundedFeeEstimator::new(&fee_estimator); monitor.provide_payment_preimage_unsafe_legacy( @@ -5907,7 +6321,7 @@ mod tests { test_preimages_exist!(&preimages[15..20], monitor); monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"3").to_byte_array()), - preimages_slice_to_htlc_outputs!(preimages[17..20]), 281474976710653, dummy_key, &logger); + preimages_slice_to_htlc_outputs!(preimages[17..20]), 281474976710653, dummy_key); // Now provide a further secret, pruning preimages 15-17 secret[0..32].clone_from_slice(&>::from_hex("c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964").unwrap()); @@ -5917,7 +6331,7 @@ mod tests { test_preimages_exist!(&preimages[17..20], monitor); monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"4").to_byte_array()), - preimages_slice_to_htlc_outputs!(preimages[18..20]), 281474976710652, dummy_key, &logger); + preimages_slice_to_htlc_outputs!(preimages[18..20]), 281474976710652, dummy_key); // Now update holder commitment tx info, pruning only element 18 as we still care about the // previous commitment tx's preimages too diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index fb58b51d4dc..cacc783a39e 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -30,7 +30,8 @@ use crate::chain::chaininterface::{ fee_for_weight, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator, }; use crate::chain::channelmonitor::{ - ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS, + ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, CommitmentHTLCData, + LATENCY_GRACE_PERIOD_BLOCKS, }; use crate::chain::transaction::{OutPoint, TransactionData}; use crate::chain::BestBlock; @@ -1821,7 +1822,32 @@ where res }, ChannelPhase::Funded(mut funded_channel) => { - let res = funded_channel.commitment_signed(msg, logger).map(|monitor_update_opt| (None, monitor_update_opt)); + #[cfg(splicing)] + let has_negotiated_pending_splice = funded_channel.pending_splice.as_ref() + .map(|pending_splice| pending_splice.funding.is_some()) + .unwrap_or(false); + #[cfg(splicing)] + let session_received_commitment_signed = funded_channel + .interactive_tx_signing_session + .as_ref() + .map(|session| session.has_received_commitment_signed()) + // Not having a signing session implies they've already sent `splice_locked`, + // which must always come after the initial commitment signed is sent. + .unwrap_or(true); + #[cfg(splicing)] + let res = if has_negotiated_pending_splice && !session_received_commitment_signed { + funded_channel + .splice_initial_commitment_signed(msg, logger) + .map(|monitor_update_opt| (None, monitor_update_opt)) + } else { + funded_channel.commitment_signed(msg, logger) + .map(|monitor_update_opt| (None, monitor_update_opt)) + }; + + #[cfg(not(splicing))] + let res = funded_channel.commitment_signed(msg, logger) + .map(|monitor_update_opt| (None, monitor_update_opt)); + self.phase = ChannelPhase::Funded(funded_channel); res }, @@ -2148,6 +2174,7 @@ impl FundingScope { #[cfg(splicing)] struct PendingSplice { pub our_funding_contribution: i64, + funding: Option, /// The funding txid used in the `splice_locked` sent to the counterparty. sent_funding_txid: Option, @@ -2584,7 +2611,7 @@ where holder_commitment_tx, best_block, context.counterparty_node_id, context.channel_id(), ); channel_monitor.provide_initial_counterparty_commitment_tx( - counterparty_initial_commitment_tx.clone(), logger, + counterparty_initial_commitment_tx.clone(), ); self.context_mut().cur_counterparty_commitment_transaction_number -= 1; @@ -4156,7 +4183,7 @@ where fn validate_commitment_signed( &self, funding: &FundingScope, holder_commitment_point: &HolderCommitmentPoint, msg: &msgs::CommitmentSigned, logger: &L, - ) -> Result + ) -> Result<(HolderCommitmentTransaction, Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)>), ChannelError> where L::Target: Logger, { @@ -4215,9 +4242,7 @@ where } let holder_keys = commitment_data.tx.trust().keys(); - let mut nondust_htlc_sources = Vec::with_capacity(commitment_data.tx.nondust_htlcs().len()); - let mut dust_htlcs = Vec::with_capacity(commitment_data.htlcs_included.len() - commitment_data.tx.nondust_htlcs().len()); - for (idx, (htlc, mut source_opt)) in commitment_data.htlcs_included.into_iter().enumerate() { + for (idx, (htlc, _)) in commitment_data.htlcs_included.iter().enumerate() { if let Some(_) = htlc.transaction_output_index { let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_data.tx.feerate_per_kw(), funding.get_counterparty_selected_contest_delay().unwrap(), &htlc, funding.get_channel_type(), @@ -4232,17 +4257,7 @@ where if let Err(_) = self.secp_ctx.verify_ecdsa(&htlc_sighash, &msg.htlc_signatures[idx], &holder_keys.countersignatory_htlc_key.to_public_key()) { return Err(ChannelError::close("Invalid HTLC tx signature from peer".to_owned())); } - if htlc.offered { - if let Some(source) = source_opt.take() { - nondust_htlc_sources.push(source.clone()); - } else { - panic!("Missing outbound HTLC source"); - } - } - } else { - dust_htlcs.push((htlc, None, source_opt.take().cloned())); } - debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere"); } let holder_commitment_tx = HolderCommitmentTransaction::new( @@ -4256,11 +4271,7 @@ where self.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, commitment_data.outbound_htlc_preimages) .map_err(|_| ChannelError::close("Failed to validate our commitment".to_owned()))?; - Ok(LatestHolderCommitmentTXInfo { - commitment_tx: holder_commitment_tx, - htlc_outputs: dust_htlcs, - nondust_htlc_sources, - }) + Ok((holder_commitment_tx, commitment_data.htlcs_included)) } #[rustfmt::skip] @@ -5851,14 +5862,6 @@ struct CommitmentTxInfoCached { feerate: u32, } -/// Partial data from ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo used to simplify the -/// return type of `ChannelContext::validate_commitment_signed`. -struct LatestHolderCommitmentTXInfo { - pub commitment_tx: HolderCommitmentTransaction, - pub htlc_outputs: Vec<(HTLCOutputInCommitment, Option, Option)>, - pub nondust_htlc_sources: Vec, -} - /// Contents of a wire message that fails an HTLC backwards. Useful for [`FundedChannel::fail_htlc`] to /// fail with either [`msgs::UpdateFailMalformedHTLC`] or [`msgs::UpdateFailHTLC`] as needed. trait FailHTLCContents { @@ -6603,6 +6606,114 @@ where Ok(channel_monitor) } + /// Handles an incoming `commitment_signed` message for the first commitment transaction of the + /// channel's new funding transaction. This assumes our `commitment_signed` was already sent + /// when the [`InteractiveTxSigningSession`] was initialized, so we do not need to send one in + /// response. As a result, a single [`ChannelMonitorUpdate`] will get queued that tracks the new + /// set of channel parameters, as well as the initial holder and counterparty commitment + /// transactions. We hold back sending our `tx_signatures` until the monitor update is + /// persisted, such that we're able to enforce the holder commitment transaction onchain once + /// the new funding transaction is signed and broadcast. + /// + /// Note that our `commitment_signed` send did not include a monitor update. This is due to: + /// 1. Updates cannot be made since the state machine is paused until `tx_signatures`. + /// 2. We're still able to abort negotiation until `tx_signatures`. + #[cfg(splicing)] + pub fn splice_initial_commitment_signed( + &mut self, msg: &msgs::CommitmentSigned, logger: &L, + ) -> Result, ChannelError> + where + L::Target: Logger, + { + if !self.context.channel_state.is_interactive_signing() + || self.context.channel_state.is_their_tx_signatures_sent() + { + return Err(ChannelError::close( + "Received splice initial commitment_signed during invalid state".to_owned(), + )); + } + + let pending_splice_funding = self + .pending_splice + .as_ref() + .and_then(|pending_splice| pending_splice.funding.as_ref()) + .expect("Funding must exist for negotiated pending splice"); + let (holder_commitment_tx, _) = self.context.validate_commitment_signed( + pending_splice_funding, + &self.holder_commitment_point, + msg, + logger, + )?; + // This corresponds to the same `commitment_signed` we sent earlier, which we know to be the + // same since the state machine is paused until `tx_signatures` are exchanged. + let counterparty_commitment_tx = self + .context + .build_commitment_transaction( + pending_splice_funding, + self.context.cur_counterparty_commitment_transaction_number, + &self.context.counterparty_cur_commitment_point.unwrap(), + false, + false, + logger, + ) + .tx; + + { + let counterparty_trusted_tx = counterparty_commitment_tx.trust(); + let counterparty_bitcoin_tx = counterparty_trusted_tx.built_transaction(); + log_trace!( + logger, + "Splice initial counterparty tx for channel {} is: txid {} tx {}", + &self.context.channel_id(), + counterparty_bitcoin_tx.txid, + encode::serialize_hex(&counterparty_bitcoin_tx.transaction) + ); + } + + log_info!(logger, "Received splice initial commitment_signed from peer for channel {} with funding txid {}", + &self.context.channel_id(), pending_splice_funding.get_funding_txo().unwrap().txid); + + self.context.latest_monitor_update_id += 1; + let monitor_update = ChannelMonitorUpdate { + update_id: self.context.latest_monitor_update_id, + updates: vec![ChannelMonitorUpdateStep::RenegotiatedFunding { + channel_parameters: pending_splice_funding.channel_transaction_parameters.clone(), + holder_commitment_tx, + counterparty_commitment_tx, + }], + channel_id: Some(self.context.channel_id()), + }; + + let tx_signatures = self + .interactive_tx_signing_session + .as_mut() + .expect("Signing session must exist for negotiated pending splice") + .received_commitment_signed(); + self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new()); + self.context.monitor_pending_tx_signatures = tx_signatures; + + Ok(self.push_ret_blockable_mon_update(monitor_update)) + } + + fn get_commitment_htlc_data<'a>( + htlcs_included: &'a [(HTLCOutputInCommitment, Option<&HTLCSource>)], + ) -> ( + impl Iterator + 'a, + impl Iterator)> + 'a, + ) { + let nondust_htlc_sources = htlcs_included + .iter() + .filter(|(htlc, _)| htlc.transaction_output_index.is_some() && htlc.offered) + .map(|(_, mut source_opt)| { + source_opt.take().expect("Missing outbound HTLC source").clone() + }); + let dust_htlcs = htlcs_included + .iter() + .filter(|(htlc, _)| htlc.transaction_output_index.is_none()) + .map(|(htlc, mut source_opt)| (htlc.clone(), source_opt.take().cloned())); + (nondust_htlc_sources, dust_htlcs) + } + pub fn commitment_signed( &mut self, msg: &msgs::CommitmentSigned, logger: &L, ) -> Result, ChannelError> @@ -6617,25 +6728,23 @@ where )); } - let updates = self + let update = self .context .validate_commitment_signed(&self.funding, &self.holder_commitment_point, msg, logger) - .map( - |LatestHolderCommitmentTXInfo { - commitment_tx, - htlc_outputs, - nondust_htlc_sources, - }| { - vec![ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { - commitment_tx, - htlc_outputs, - claimed_htlcs: vec![], - nondust_htlc_sources, - }] - }, - )?; + .map(|(commitment_tx, htlcs_included)| { + let (nondust_htlc_sources, dust_htlcs) = + Self::get_commitment_htlc_data(&htlcs_included); + let htlc_outputs = + dust_htlcs.into_iter().map(|(htlc, source)| (htlc, None, source)).collect(); + ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { + commitment_tx, + htlc_outputs, + claimed_htlcs: vec![], + nondust_htlc_sources: nondust_htlc_sources.collect(), + } + })?; - self.commitment_signed_update_monitor(updates, logger) + self.commitment_signed_update_monitor(update, logger) } pub fn commitment_signed_batch( @@ -6672,34 +6781,40 @@ where // Any commitment_signed not associated with a FundingScope is ignored below if a // pending splice transaction has confirmed since receiving the batch. - let updates = core::iter::once(&self.funding) - .chain(self.pending_funding.iter()) - .map(|funding| { - let funding_txid = funding.get_funding_txo().unwrap().txid; - let msg = messages.get(&funding_txid).ok_or_else(|| { - ChannelError::close(format!( - "Peer did not send a commitment_signed for pending splice transaction: {}", - funding_txid - )) - })?; - self.context - .validate_commitment_signed(funding, &self.holder_commitment_point, msg, logger) - .map( - |LatestHolderCommitmentTXInfo { - commitment_tx, - htlc_outputs, - nondust_htlc_sources, - }| ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { - commitment_tx, - htlc_outputs, - claimed_htlcs: vec![], - nondust_htlc_sources, - }, - ) - }) - .collect::, ChannelError>>()?; + let mut commitment_txs = Vec::with_capacity(self.pending_funding.len() + 1); + let mut htlc_data = None; + for funding in core::iter::once(&self.funding).chain(self.pending_funding.iter()) { + let funding_txid = + funding.get_funding_txid().expect("Funding txid must be known for pending scope"); + let msg = messages.get(&funding_txid).ok_or_else(|| { + ChannelError::close(format!( + "Peer did not send a commitment_signed for pending splice transaction: {}", + funding_txid + )) + })?; + let (commitment_tx, htlcs_included) = self.context.validate_commitment_signed( + funding, + &self.holder_commitment_point, + msg, + logger, + )?; + commitment_txs.push(commitment_tx); + if htlc_data.is_none() { + let (nondust_htlc_sources, dust_htlcs) = + Self::get_commitment_htlc_data(&htlcs_included); + htlc_data = Some(CommitmentHTLCData { + nondust_htlc_sources: nondust_htlc_sources.collect(), + dust_htlcs: dust_htlcs.collect(), + }); + } + } - self.commitment_signed_update_monitor(updates, logger) + let update = ChannelMonitorUpdateStep::LatestHolderCommitment { + commitment_txs, + htlc_data: htlc_data.expect("At least one funding scope must have been considered"), + claimed_htlcs: Vec::new(), + }; + self.commitment_signed_update_monitor(update, logger) } fn commitment_signed_check_state(&self) -> Result<(), ChannelError> { @@ -6732,7 +6847,7 @@ where } #[rustfmt::skip] - fn commitment_signed_update_monitor(&mut self, mut updates: Vec, logger: &L) -> Result, ChannelError> + fn commitment_signed_update_monitor(&mut self, mut update: ChannelMonitorUpdateStep, logger: &L) -> Result, ChannelError> where L::Target: Logger { if self.holder_commitment_point.advance(&self.context.holder_signer, &self.context.secp_ctx, logger).is_err() { @@ -6788,21 +6903,26 @@ where } } - for mut update in updates.iter_mut() { - if let ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { + match &mut update { + ChannelMonitorUpdateStep::LatestHolderCommitment { claimed_htlcs: ref mut update_claimed_htlcs, .. - } = &mut update { + } => { debug_assert!(update_claimed_htlcs.is_empty()); *update_claimed_htlcs = claimed_htlcs.clone(); - } else { - debug_assert!(false); - } + }, + ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { + claimed_htlcs: ref mut update_claimed_htlcs, .. + } => { + debug_assert!(update_claimed_htlcs.is_empty()); + *update_claimed_htlcs = claimed_htlcs.clone(); + }, + _ => debug_assert!(false), } self.context.latest_monitor_update_id += 1; let mut monitor_update = ChannelMonitorUpdate { update_id: self.context.latest_monitor_update_id, - updates, + updates: vec![update], channel_id: Some(self.context.channel_id()), }; @@ -9830,6 +9950,7 @@ where self.pending_splice = Some(PendingSplice { our_funding_contribution: our_funding_contribution_satoshis, + funding: None, sent_funding_txid: None, received_funding_txid: None, }); @@ -10187,32 +10308,54 @@ where } self.context.resend_order = RAACommitmentOrder::RevokeAndACKFirst; - let mut updates = Vec::with_capacity(self.pending_funding.len() + 1); - for funding in core::iter::once(&self.funding).chain(self.pending_funding.iter()) { + let update = if self.pending_funding.is_empty() { let (htlcs_ref, counterparty_commitment_tx) = - self.build_commitment_no_state_update(funding, logger); - let htlc_outputs: Vec<(HTLCOutputInCommitment, Option>)> = - htlcs_ref.into_iter().map(|(htlc, htlc_source)| (htlc, htlc_source.map(|source_ref| Box::new(source_ref.clone())))).collect(); - - if self.pending_funding.is_empty() { - // Soon, we will switch this to `LatestCounterpartyCommitmentTX`, - // and provide the full commit tx instead of the information needed to rebuild it. - updates.push(ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { - commitment_txid: counterparty_commitment_tx.trust().txid(), - htlc_outputs, - commitment_number: self.context.cur_counterparty_commitment_transaction_number, - their_per_commitment_point: self.context.counterparty_cur_commitment_point.unwrap(), - feerate_per_kw: Some(counterparty_commitment_tx.feerate_per_kw()), - to_broadcaster_value_sat: Some(counterparty_commitment_tx.to_broadcaster_value_sat()), - to_countersignatory_value_sat: Some(counterparty_commitment_tx.to_countersignatory_value_sat()), - }); - } else { - updates.push(ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { - htlc_outputs, - commitment_tx: counterparty_commitment_tx, - }); + self.build_commitment_no_state_update(&self.funding, logger); + let htlc_outputs = htlcs_ref.into_iter() + .map(|(htlc, htlc_source)| ( + htlc, htlc_source.map(|source_ref| Box::new(source_ref.clone())) + )) + .collect(); + + // Soon, we will switch this to `LatestCounterpartyCommitment`, + // and provide the full commit tx instead of the information needed to rebuild it. + ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { + commitment_txid: counterparty_commitment_tx.trust().txid(), + htlc_outputs, + commitment_number: self.context.cur_counterparty_commitment_transaction_number, + their_per_commitment_point: self.context.counterparty_cur_commitment_point.unwrap(), + feerate_per_kw: Some(counterparty_commitment_tx.feerate_per_kw()), + to_broadcaster_value_sat: Some(counterparty_commitment_tx.to_broadcaster_value_sat()), + to_countersignatory_value_sat: Some(counterparty_commitment_tx.to_countersignatory_value_sat()), } - } + } else { + let mut htlc_data = None; + let commitment_txs = core::iter::once(&self.funding) + .chain(self.pending_funding.iter()) + .map(|funding| { + let (htlcs_ref, counterparty_commitment_tx) = + self.build_commitment_no_state_update(funding, logger); + if htlc_data.is_none() { + let nondust_htlc_sources = htlcs_ref.iter() + // We check !offered as this is the HTLC from the counterparty's point of view. + .filter(|(htlc, _)| !htlc.offered && htlc.transaction_output_index.is_some()) + .map(|(_, source)| source.expect("Outbound HTLC must have a source").clone()) + .collect(); + let dust_htlcs = htlcs_ref.into_iter() + .filter(|(htlc, _)| htlc.transaction_output_index.is_none()) + .map(|(htlc, source)| (htlc, source.cloned())) + .collect(); + htlc_data = Some(CommitmentHTLCData { + nondust_htlc_sources, + dust_htlcs, + }); + } + counterparty_commitment_tx + }) + .collect(); + let htlc_data = htlc_data.unwrap(); + ChannelMonitorUpdateStep::LatestCounterpartyCommitment { commitment_txs, htlc_data } + }; if self.context.announcement_sigs_state == AnnouncementSigsState::MessageSent { self.context.announcement_sigs_state = AnnouncementSigsState::Committed; @@ -10221,7 +10364,7 @@ where self.context.latest_monitor_update_id += 1; let monitor_update = ChannelMonitorUpdate { update_id: self.context.latest_monitor_update_id, - updates, + updates: vec![update], channel_id: Some(self.context.channel_id()), }; self.context.channel_state.set_awaiting_remote_revoke();