From efa579bc61eafaae509657e57c6442c0c9229915 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sun, 1 Aug 2021 02:34:08 +0000 Subject: [PATCH 01/11] Don't initialise Vecs being read with VecReadWrapper explicitly This simplifies the tlv serialization read macro somewhat by allowing callsites to simply read into an `Option` instead of needing to read into an `Option` when using `vec_type`. --- lightning/src/util/ser_macros.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs index 5178732c745..e8bd3326bab 100644 --- a/lightning/src/util/ser_macros.rs +++ b/lightning/src/util/ser_macros.rs @@ -154,7 +154,8 @@ macro_rules! decode_tlv { $field = ser::Readable::read(&mut $reader)?; }}; ($reader: expr, $field: ident, vec_type) => {{ - $field = Some(ser::Readable::read(&mut $reader)?); + let f: ::util::ser::VecReadWrapper<_> = ser::Readable::read(&mut $reader)?; + $field = Some(f.0); }}; ($reader: expr, $field: ident, option) => {{ $field = Some(ser::Readable::read(&mut $reader)?); @@ -399,7 +400,7 @@ macro_rules! init_tlv_based_struct_field { $field.0.unwrap() }; ($field: ident, vec_type) => { - $field.unwrap().0 + $field.unwrap() }; } @@ -411,7 +412,7 @@ macro_rules! init_tlv_field_var { let mut $field = ::util::ser::OptionDeserWrapper(None); }; ($field: ident, vec_type) => { - let mut $field = Some(::util::ser::VecReadWrapper(Vec::new())); + let mut $field = Some(Vec::new()); }; ($field: ident, option) => { let mut $field = None; From ce479b671a4bc218388ef88e4eba2c78f2b95711 Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Tue, 3 Aug 2021 19:20:06 -0400 Subject: [PATCH 02/11] Remove DynamicOutputP2WPKH ref in logger --- lightning/src/util/macro_logger.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/util/macro_logger.rs b/lightning/src/util/macro_logger.rs index 9644411bc14..d7849d77ae0 100644 --- a/lightning/src/util/macro_logger.rs +++ b/lightning/src/util/macro_logger.rs @@ -142,7 +142,7 @@ impl<'a> core::fmt::Display for DebugSpendable<'a> { write!(f, "DelayedPaymentOutput {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?; } &SpendableOutputDescriptor::StaticPaymentOutput(ref descriptor) => { - write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?; + write!(f, "StaticPaymentOutput {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?; } } Ok(()) From aaba0a2ec9ed310785a1e79d627c2530094ce208 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 27 Jul 2021 15:52:21 +0000 Subject: [PATCH 03/11] Store to-self value in the current commitment tx in ChannelMonitor --- lightning/src/chain/channelmonitor.rs | 48 ++++++++++++++++++++------- lightning/src/chain/onchaintx.rs | 8 +++++ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index dc009c4d52c..aa66603615f 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -272,11 +272,15 @@ struct HolderSignedTx { b_htlc_key: PublicKey, delayed_payment_key: PublicKey, per_commitment_point: PublicKey, - feerate_per_kw: u32, htlc_outputs: Vec<(HTLCOutputInCommitment, Option, Option)>, + to_self_value_sat: u64, + feerate_per_kw: u32, } impl_writeable_tlv_based!(HolderSignedTx, { (0, txid, required), + // Note that this is filled in with data from OnchainTxHandler if it's missing. + // For HolderSignedTx objects serialized with 0.0.100+, this should be filled in. + (1, to_self_value_sat, (default_value, u64::max_value())), (2, revocation_key, required), (4, a_htlc_key, required), (6, b_htlc_key, required), @@ -869,8 +873,9 @@ impl ChannelMonitor { b_htlc_key: tx_keys.countersignatory_htlc_key, delayed_payment_key: tx_keys.broadcaster_delayed_payment_key, per_commitment_point: tx_keys.per_commitment_point, - feerate_per_kw: trusted_tx.feerate_per_kw(), htlc_outputs: Vec::new(), // There are never any HTLCs in the initial commitment transactions + to_self_value_sat: initial_holder_commitment_tx.to_broadcaster_value_sat(), + feerate_per_kw: trusted_tx.feerate_per_kw(), }; (holder_commitment_tx, trusted_tx.commitment_number()) }; @@ -1424,8 +1429,9 @@ impl ChannelMonitorImpl { b_htlc_key: tx_keys.countersignatory_htlc_key, delayed_payment_key: tx_keys.broadcaster_delayed_payment_key, per_commitment_point: tx_keys.per_commitment_point, - feerate_per_kw: trusted_tx.feerate_per_kw(), htlc_outputs, + to_self_value_sat: holder_commitment_tx.to_broadcaster_value_sat(), + feerate_per_kw: trusted_tx.feerate_per_kw(), } }; self.onchain_tx_handler.provide_latest_holder_tx(holder_commitment_tx); @@ -2708,14 +2714,15 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> } } - let prev_holder_signed_commitment_tx = match ::read(reader)? { - 0 => None, - 1 => { - Some(Readable::read(reader)?) - }, - _ => return Err(DecodeError::InvalidValue), - }; - let current_holder_commitment_tx = Readable::read(reader)?; + let mut prev_holder_signed_commitment_tx: Option = + match ::read(reader)? { + 0 => None, + 1 => { + Some(Readable::read(reader)?) + }, + _ => return Err(DecodeError::InvalidValue), + }; + let mut current_holder_commitment_tx: HolderSignedTx = Readable::read(reader)?; let current_counterparty_commitment_number = ::read(reader)?.0; let current_holder_commitment_number = ::read(reader)?.0; @@ -2772,11 +2779,28 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> return Err(DecodeError::InvalidValue); } } - let onchain_tx_handler = ReadableArgs::read(reader, keys_manager)?; + let onchain_tx_handler: OnchainTxHandler = ReadableArgs::read(reader, keys_manager)?; let lockdown_from_offchain = Readable::read(reader)?; let holder_tx_signed = Readable::read(reader)?; + if let Some(prev_commitment_tx) = prev_holder_signed_commitment_tx.as_mut() { + let prev_holder_value = onchain_tx_handler.get_prev_holder_commitment_to_self_value(); + if prev_holder_value.is_none() { return Err(DecodeError::InvalidValue); } + if prev_commitment_tx.to_self_value_sat == u64::max_value() { + prev_commitment_tx.to_self_value_sat = prev_holder_value.unwrap(); + } else if prev_commitment_tx.to_self_value_sat != prev_holder_value.unwrap() { + return Err(DecodeError::InvalidValue); + } + } + + let cur_holder_value = onchain_tx_handler.get_cur_holder_commitment_to_self_value(); + if current_holder_commitment_tx.to_self_value_sat == u64::max_value() { + current_holder_commitment_tx.to_self_value_sat = cur_holder_value; + } else if current_holder_commitment_tx.to_self_value_sat != cur_holder_value { + return Err(DecodeError::InvalidValue); + } + read_tlv_fields!(reader, {}); let mut secp_ctx = Secp256k1::new(); diff --git a/lightning/src/chain/onchaintx.rs b/lightning/src/chain/onchaintx.rs index d6777cc5c8c..b4f5438adfb 100644 --- a/lightning/src/chain/onchaintx.rs +++ b/lightning/src/chain/onchaintx.rs @@ -365,6 +365,14 @@ impl OnchainTxHandler { } } + pub(crate) fn get_prev_holder_commitment_to_self_value(&self) -> Option { + self.prev_holder_commitment.as_ref().map(|commitment| commitment.to_broadcaster_value_sat()) + } + + pub(crate) fn get_cur_holder_commitment_to_self_value(&self) -> u64 { + self.holder_commitment.to_broadcaster_value_sat() + } + /// Lightning security model (i.e being able to redeem/timeout HTLC or penalize coutnerparty onchain) lays on the assumption of claim transactions getting confirmed before timelock expiration /// (CSV or CLTV following cases). In case of high-fee spikes, claim tx may stuck in the mempool, so you need to bump its feerate quickly using Replace-By-Fee or Child-Pay-For-Parent. /// Panics if there are signing errors, because signing operations in reaction to on-chain events From 44e48679fbac4240ee9347bbebbe2d3d023bc2f8 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 27 Jul 2021 19:27:43 +0000 Subject: [PATCH 04/11] Drop unused CounterpartyCommitmentTransaction::per_htlc HashMap --- lightning/src/chain/channelmonitor.rs | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index aa66603615f..8a994f6986d 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -297,19 +297,11 @@ struct CounterpartyCommitmentTransaction { counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, on_counterparty_tx_csv: u16, - per_htlc: HashMap> } impl Writeable for CounterpartyCommitmentTransaction { fn write(&self, w: &mut W) -> Result<(), io::Error> { - w.write_all(&byte_utils::be64_to_array(self.per_htlc.len() as u64))?; - for (ref txid, ref htlcs) in self.per_htlc.iter() { - w.write_all(&txid[..])?; - w.write_all(&byte_utils::be64_to_array(htlcs.len() as u64))?; - for &ref htlc in htlcs.iter() { - htlc.write(w)?; - } - } + w.write_all(&byte_utils::be64_to_array(0))?; write_tlv_fields!(w, { (0, self.counterparty_delayed_payment_base_key, required), (2, self.counterparty_htlc_base_key, required), @@ -321,20 +313,17 @@ impl Writeable for CounterpartyCommitmentTransaction { impl Readable for CounterpartyCommitmentTransaction { fn read(r: &mut R) -> Result { let counterparty_commitment_transaction = { + // Versions prior to 0.0.100 had some per-HTLC state stored here, which is no longer + // used. Read it for compatibility. let per_htlc_len: u64 = Readable::read(r)?; - 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: Txid = Readable::read(r)?; + let _txid: Txid = Readable::read(r)?; let htlcs_count: u64 = Readable::read(r)?; - 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(r)?; - htlcs.push(htlc); - } - if let Some(_) = per_htlc.insert(txid, htlcs) { - return Err(DecodeError::InvalidValue); + let _htlc: HTLCOutputInCommitment = Readable::read(r)?; } } + let mut counterparty_delayed_payment_base_key = OptionDeserWrapper(None); let mut counterparty_htlc_base_key = OptionDeserWrapper(None); let mut on_counterparty_tx_csv: u16 = 0; @@ -347,7 +336,6 @@ impl Readable for CounterpartyCommitmentTransaction { counterparty_delayed_payment_base_key: counterparty_delayed_payment_base_key.0.unwrap(), counterparty_htlc_base_key: counterparty_htlc_base_key.0.unwrap(), on_counterparty_tx_csv, - per_htlc, } }; Ok(counterparty_commitment_transaction) @@ -855,7 +843,7 @@ impl ChannelMonitor { let counterparty_channel_parameters = channel_parameters.counterparty_parameters.as_ref().unwrap(); let counterparty_delayed_payment_base_key = counterparty_channel_parameters.pubkeys.delayed_payment_basepoint; let counterparty_htlc_base_key = counterparty_channel_parameters.pubkeys.htlc_basepoint; - let counterparty_tx_cache = CounterpartyCommitmentTransaction { counterparty_delayed_payment_base_key, counterparty_htlc_base_key, on_counterparty_tx_csv, per_htlc: HashMap::new() }; + let counterparty_tx_cache = CounterpartyCommitmentTransaction { counterparty_delayed_payment_base_key, counterparty_htlc_base_key, on_counterparty_tx_csv }; let channel_keys_id = keys.channel_keys_id(); let holder_revocation_basepoint = keys.pubkeys().revocation_basepoint; @@ -1407,7 +1395,6 @@ impl ChannelMonitorImpl { htlcs.push(htlc.0); } } - self.counterparty_tx_cache.per_htlc.insert(txid, htlcs); } /// Informs this monitor of the latest holder (ie broadcastable) commitment transaction. The From cd578b55f3650af2296f064f0cf9753f4cf85e93 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 27 Jul 2021 19:30:27 +0000 Subject: [PATCH 05/11] Rename CounterpartyCommitmentTransaction to Params as it is static --- lightning/src/chain/channelmonitor.rs | 42 +++++++++++++-------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 8a994f6986d..761ad7bae83 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -290,16 +290,16 @@ impl_writeable_tlv_based!(HolderSignedTx, { (14, htlc_outputs, vec_type) }); -/// We use this to track counterparty commitment transactions and htlcs outputs and -/// use it to generate any justice or 2nd-stage preimage/timeout transactions. +/// We use this to track static counterparty commitment transaction data and to generate any +/// justice or 2nd-stage preimage/timeout transactions. #[derive(PartialEq)] -struct CounterpartyCommitmentTransaction { +struct CounterpartyCommitmentParameters { counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, on_counterparty_tx_csv: u16, } -impl Writeable for CounterpartyCommitmentTransaction { +impl Writeable for CounterpartyCommitmentParameters { fn write(&self, w: &mut W) -> Result<(), io::Error> { w.write_all(&byte_utils::be64_to_array(0))?; write_tlv_fields!(w, { @@ -310,7 +310,7 @@ impl Writeable for CounterpartyCommitmentTransaction { Ok(()) } } -impl Readable for CounterpartyCommitmentTransaction { +impl Readable for CounterpartyCommitmentParameters { fn read(r: &mut R) -> Result { let counterparty_commitment_transaction = { // Versions prior to 0.0.100 had some per-HTLC state stored here, which is no longer @@ -332,7 +332,7 @@ impl Readable for CounterpartyCommitmentTransaction { (2, counterparty_htlc_base_key, required), (4, on_counterparty_tx_csv, required), }); - CounterpartyCommitmentTransaction { + CounterpartyCommitmentParameters { counterparty_delayed_payment_base_key: counterparty_delayed_payment_base_key.0.unwrap(), counterparty_htlc_base_key: counterparty_htlc_base_key.0.unwrap(), on_counterparty_tx_csv, @@ -524,7 +524,7 @@ pub(crate) struct ChannelMonitorImpl { current_counterparty_commitment_txid: Option, prev_counterparty_commitment_txid: Option, - counterparty_tx_cache: CounterpartyCommitmentTransaction, + counterparty_commitment_params: CounterpartyCommitmentParameters, funding_redeemscript: Script, channel_value_satoshis: u64, // first is the idx of the first of the two revocation points @@ -637,7 +637,7 @@ impl PartialEq for ChannelMonitorImpl { self.funding_info != other.funding_info || self.current_counterparty_commitment_txid != other.current_counterparty_commitment_txid || self.prev_counterparty_commitment_txid != other.prev_counterparty_commitment_txid || - self.counterparty_tx_cache != other.counterparty_tx_cache || + self.counterparty_commitment_params != other.counterparty_commitment_params || self.funding_redeemscript != other.funding_redeemscript || self.channel_value_satoshis != other.channel_value_satoshis || self.their_cur_revocation_points != other.their_cur_revocation_points || @@ -708,7 +708,7 @@ impl Writeable for ChannelMonitorImpl { self.current_counterparty_commitment_txid.write(writer)?; self.prev_counterparty_commitment_txid.write(writer)?; - self.counterparty_tx_cache.write(writer)?; + self.counterparty_commitment_params.write(writer)?; self.funding_redeemscript.write(writer)?; self.channel_value_satoshis.write(writer)?; @@ -843,7 +843,7 @@ impl ChannelMonitor { let counterparty_channel_parameters = channel_parameters.counterparty_parameters.as_ref().unwrap(); let counterparty_delayed_payment_base_key = counterparty_channel_parameters.pubkeys.delayed_payment_basepoint; let counterparty_htlc_base_key = counterparty_channel_parameters.pubkeys.htlc_basepoint; - let counterparty_tx_cache = CounterpartyCommitmentTransaction { counterparty_delayed_payment_base_key, counterparty_htlc_base_key, on_counterparty_tx_csv }; + let counterparty_commitment_params = CounterpartyCommitmentParameters { counterparty_delayed_payment_base_key, counterparty_htlc_base_key, on_counterparty_tx_csv }; let channel_keys_id = keys.channel_keys_id(); let holder_revocation_basepoint = keys.pubkeys().revocation_basepoint; @@ -891,7 +891,7 @@ impl ChannelMonitor { current_counterparty_commitment_txid: None, prev_counterparty_commitment_txid: None, - counterparty_tx_cache, + counterparty_commitment_params, funding_redeemscript, channel_value_satoshis, their_cur_revocation_points: None, @@ -1629,16 +1629,16 @@ impl ChannelMonitorImpl { 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.holder_revocation_basepoint)); - let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.counterparty_tx_cache.counterparty_delayed_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.counterparty_commitment_params.counterparty_delayed_payment_base_key)); - let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_tx_cache.on_counterparty_tx_csv, &delayed_key); + let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key); let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh(); // First, process non-htlc outputs (to_holder & to_counterparty) for (idx, outp) in tx.output.iter().enumerate() { if outp.script_pubkey == revokeable_p2wsh { - let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_tx_cache.on_counterparty_tx_csv); - let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, true, height); + let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv); + let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height); claimable_outpoints.push(justice_package); } } @@ -1651,7 +1651,7 @@ impl ChannelMonitorImpl { 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 revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone()); + let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone()); let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height); claimable_outpoints.push(justice_package); } @@ -1719,7 +1719,7 @@ impl ChannelMonitorImpl { } let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None }; if preimage.is_some() || !htlc.offered { - let counterparty_htlc_outp = if htlc.offered { PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(*revocation_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, preimage.unwrap(), htlc.clone())) } else { PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(*revocation_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, htlc.clone())) }; + let counterparty_htlc_outp = if htlc.offered { PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(*revocation_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, preimage.unwrap(), htlc.clone())) } else { PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(*revocation_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, htlc.clone())) }; let aggregation = if !htlc.offered { false } else { true }; let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry,aggregation, 0); claimable_outpoints.push(counterparty_package); @@ -1753,8 +1753,8 @@ impl ChannelMonitorImpl { let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key); log_error!(logger, "Got broadcast of revoked counterparty HTLC transaction, spending {}:{}", htlc_txid, 0); - let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, tx.output[0].value, self.counterparty_tx_cache.on_counterparty_tx_csv); - let justice_package = PackageTemplate::build_package(htlc_txid, 0, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, true, height); + let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, tx.output[0].value, self.counterparty_commitment_params.on_counterparty_tx_csv); + let justice_package = PackageTemplate::build_package(htlc_txid, 0, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height); let claimable_outpoints = vec!(justice_package); let outputs = vec![(0, tx.output[0].clone())]; (claimable_outpoints, Some((htlc_txid, outputs))) @@ -2628,7 +2628,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> let current_counterparty_commitment_txid = Readable::read(reader)?; let prev_counterparty_commitment_txid = Readable::read(reader)?; - let counterparty_tx_cache = Readable::read(reader)?; + let counterparty_commitment_params = Readable::read(reader)?; let funding_redeemscript = Readable::read(reader)?; let channel_value_satoshis = Readable::read(reader)?; @@ -2809,7 +2809,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> current_counterparty_commitment_txid, prev_counterparty_commitment_txid, - counterparty_tx_cache, + counterparty_commitment_params, funding_redeemscript, channel_value_satoshis, their_cur_revocation_points, From 73ee30d9da29ebe9e87b00db27c03f8830a754b1 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 4 Aug 2021 15:14:56 +0000 Subject: [PATCH 06/11] Track the tx which spends our funding output in ChannelMonitor This allows us to easily look up how our channel was closed and track which balances may be spendable on-chain. --- lightning/src/chain/channelmonitor.rs | 85 +++++++++++++++++++++------ 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 761ad7bae83..22708f18b40 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -356,12 +356,20 @@ struct OnchainEventEntry { impl OnchainEventEntry { fn confirmation_threshold(&self) -> u32 { let mut conf_threshold = self.height + ANTI_REORG_DELAY - 1; - if let OnchainEvent::MaturingOutput { - descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) - } = self.event { - // A CSV'd transaction is confirmable in block (input height) + CSV delay, which means - // it's broadcastable when we see the previous block. - conf_threshold = cmp::max(conf_threshold, self.height + descriptor.to_self_delay as u32 - 1); + match self.event { + OnchainEvent::MaturingOutput { + descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) + } => { + // A CSV'd transaction is confirmable in block (input height) + CSV delay, which means + // it's broadcastable when we see the previous block. + conf_threshold = cmp::max(conf_threshold, self.height + descriptor.to_self_delay as u32 - 1); + }, + OnchainEvent::FundingSpendConfirmation { on_local_output_csv: Some(csv), .. } => { + // A CSV'd transaction is confirmable in block (input height) + CSV delay, which means + // it's broadcastable when we see the previous block. + conf_threshold = cmp::max(conf_threshold, self.height + csv as u32 - 1); + }, + _ => {}, } conf_threshold } @@ -386,6 +394,14 @@ enum OnchainEvent { MaturingOutput { descriptor: SpendableOutputDescriptor, }, + /// A spend of the funding output, either a commitment transaction or a cooperative closing + /// transaction. + FundingSpendConfirmation { + txid: Txid, + /// The CSV delay for the output of the funding spend transaction (implying it is a local + /// commitment transaction, and this is the delay on the to_self output). + on_local_output_csv: Option, + }, } impl Writeable for OnchainEventEntry { @@ -426,6 +442,10 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent, (1, MaturingOutput) => { (0, descriptor, required), }, + (3, FundingSpendConfirmation) => { + (0, txid, required), + (2, on_local_output_csv, option), + }, ); #[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))] @@ -598,6 +618,8 @@ pub(crate) struct ChannelMonitorImpl { // remote monitor out-of-order with regards to the block view. holder_tx_signed: bool, + funding_spend_confirmed: Option, + // We simply modify best_block in Channel's block_connected so that serialization is // consistent but hopefully the users' copy handles block_connected in a consistent way. // (we do *not*, however, update them in update_monitor to ensure any local user copies keep @@ -656,7 +678,8 @@ impl PartialEq for ChannelMonitorImpl { self.onchain_events_awaiting_threshold_conf != other.onchain_events_awaiting_threshold_conf || self.outputs_to_watch != other.outputs_to_watch || self.lockdown_from_offchain != other.lockdown_from_offchain || - self.holder_tx_signed != other.holder_tx_signed + self.holder_tx_signed != other.holder_tx_signed || + self.funding_spend_confirmed != other.funding_spend_confirmed { false } else { @@ -821,7 +844,9 @@ impl Writeable for ChannelMonitorImpl { self.lockdown_from_offchain.write(writer)?; self.holder_tx_signed.write(writer)?; - write_tlv_fields!(writer, {}); + write_tlv_fields!(writer, { + (1, self.funding_spend_confirmed, option), + }); Ok(()) } @@ -919,6 +944,7 @@ impl ChannelMonitor { lockdown_from_offchain: false, holder_tx_signed: false, + funding_spend_confirmed: None, best_block, @@ -1804,7 +1830,8 @@ impl ChannelMonitorImpl { /// Attempts to claim any claimable HTLCs in a commitment transaction which was not (yet) /// revoked using data in holder_claimable_outpoints. /// Should not be used if check_spend_revoked_transaction succeeds. - fn check_spend_holder_transaction(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec, TransactionOutputs) where L::Target: Logger { + /// Returns None unless the transaction is definitely one of our commitment transactions. + fn check_spend_holder_transaction(&mut self, tx: &Transaction, height: u32, logger: &L) -> Option<(Vec, TransactionOutputs)> where L::Target: Logger { let commitment_txid = tx.txid(); let mut claim_requests = Vec::new(); let mut watch_outputs = Vec::new(); @@ -1839,9 +1866,10 @@ impl ChannelMonitorImpl { } if is_holder_tx { + Some((claim_requests, (commitment_txid, watch_outputs))) + } else { + None } - - (claim_requests, (commitment_txid, watch_outputs)) } pub fn get_latest_holder_commitment_txn(&mut self, logger: &L) -> Vec where L::Target: Logger { @@ -1973,20 +2001,33 @@ impl ChannelMonitorImpl { // filters. let prevout = &tx.input[0].previous_output; if prevout.txid == self.funding_info.0.txid && prevout.vout == self.funding_info.0.index as u32 { + let mut balance_spendable_csv = None; + log_info!(logger, "Channel closed by funding output spend in txid {}.", log_bytes!(tx.txid())); if (tx.input[0].sequence >> 8*3) as u8 == 0x80 && (tx.lock_time >> 8*3) as u8 == 0x20 { let (mut new_outpoints, new_outputs) = self.check_spend_counterparty_transaction(&tx, height, &logger); if !new_outputs.1.is_empty() { watch_outputs.push(new_outputs); } + claimable_outpoints.append(&mut new_outpoints); if new_outpoints.is_empty() { - let (mut new_outpoints, new_outputs) = self.check_spend_holder_transaction(&tx, height, &logger); - if !new_outputs.1.is_empty() { - watch_outputs.push(new_outputs); + if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &logger) { + if !new_outputs.1.is_empty() { + watch_outputs.push(new_outputs); + } + claimable_outpoints.append(&mut new_outpoints); + balance_spendable_csv = Some(self.on_holder_tx_csv); } - claimable_outpoints.append(&mut new_outpoints); } - claimable_outpoints.append(&mut new_outpoints); } + let txid = tx.txid(); + self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { + txid, + height: height, + event: OnchainEvent::FundingSpendConfirmation { + txid, + on_local_output_csv: balance_spendable_csv, + }, + }); } else { if let Some(&commitment_number) = self.counterparty_commitment_txn_on_chain.get(&prevout.txid) { let (mut new_outpoints, new_outputs_option) = self.check_spend_counterparty_htlc(&tx, commitment_number, height, &logger); @@ -2075,6 +2116,7 @@ impl ChannelMonitorImpl { .filter_map(|entry| match &entry.event { OnchainEvent::HTLCUpdate { source, .. } => Some(source), OnchainEvent::MaturingOutput { .. } => None, + OnchainEvent::FundingSpendConfirmation { .. } => None, }) .collect(); #[cfg(debug_assertions)] @@ -2113,7 +2155,10 @@ impl ChannelMonitorImpl { self.pending_events.push(Event::SpendableOutputs { outputs: vec![descriptor] }); - } + }, + OnchainEvent::FundingSpendConfirmation { txid, .. } => { + self.funding_spend_confirmed = Some(txid); + }, } } @@ -2788,7 +2833,10 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> return Err(DecodeError::InvalidValue); } - read_tlv_fields!(reader, {}); + let mut funding_spend_confirmed = None; + read_tlv_fields!(reader, { + (1, funding_spend_confirmed, option), + }); let mut secp_ctx = Secp256k1::new(); secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes()); @@ -2837,6 +2885,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> lockdown_from_offchain, holder_tx_signed, + funding_spend_confirmed, best_block, From c02b6a3807488e1943d79792c5ac0ee52530b971 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 4 Aug 2021 15:16:43 +0000 Subject: [PATCH 07/11] Track how our HTLCs are resolved on-chain persistently This tracks how any HTLC outputs in broadcast commitment transactions are resolved on-chain, storing the result of the HTLC resolution persistently in the ChannelMonitor. This can be used to determine which outputs may still be available for claiming on-chain. --- lightning/src/chain/channelmonitor.rs | 158 ++++++++++++++++++++++---- 1 file changed, 136 insertions(+), 22 deletions(-) diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 22708f18b40..1b5a56d59e1 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -364,7 +364,8 @@ impl OnchainEventEntry { // it's broadcastable when we see the previous block. conf_threshold = cmp::max(conf_threshold, self.height + descriptor.to_self_delay as u32 - 1); }, - OnchainEvent::FundingSpendConfirmation { on_local_output_csv: Some(csv), .. } => { + OnchainEvent::FundingSpendConfirmation { on_local_output_csv: Some(csv), .. } | + OnchainEvent::HTLCSpendConfirmation { on_to_local_output_csv: Some(csv), .. } => { // A CSV'd transaction is confirmable in block (input height) + CSV delay, which means // it's broadcastable when we see the previous block. conf_threshold = cmp::max(conf_threshold, self.height + csv as u32 - 1); @@ -383,13 +384,19 @@ impl OnchainEventEntry { /// once they mature to enough confirmations (ANTI_REORG_DELAY) #[derive(PartialEq)] enum OnchainEvent { - /// HTLC output getting solved by a timeout, at maturation we pass upstream payment source information to solve - /// inbound HTLC in backward channel. Note, in case of preimage, we pass info to upstream without delay as we can - /// only win from it, so it's never an OnchainEvent + /// An outbound HTLC failing after a transaction is confirmed. Used + /// * when an outbound HTLC output is spent by us after the HTLC timed out + /// * an outbound HTLC which was not present in the commitment transaction which appeared + /// on-chain (either because it was not fully committed to or it was dust). + /// Note that this is *not* used for preimage claims, as those are passed upstream immediately, + /// appearing only as an `HTLCSpendConfirmation`, below. HTLCUpdate { source: HTLCSource, payment_hash: PaymentHash, onchain_value_satoshis: Option, + /// None in the second case, above, ie when there is no relevant output in the commitment + /// transaction which appeared on chain. + input_idx: Option, }, MaturingOutput { descriptor: SpendableOutputDescriptor, @@ -397,11 +404,27 @@ enum OnchainEvent { /// A spend of the funding output, either a commitment transaction or a cooperative closing /// transaction. FundingSpendConfirmation { - txid: Txid, /// The CSV delay for the output of the funding spend transaction (implying it is a local /// commitment transaction, and this is the delay on the to_self output). on_local_output_csv: Option, }, + /// A spend of a commitment transaction HTLC output, set in the cases where *no* `HTLCUpdate` + /// is constructed. This is used when + /// * an outbound HTLC is claimed by our counterparty with a preimage, causing us to + /// immediately claim the HTLC on the inbound edge and track the resolution here, + /// * an inbound HTLC is claimed by our counterparty (with a timeout), + /// * an inbound HTLC is claimed by us (with a preimage). + /// * a revoked-state HTLC transaction was broadcasted, which was claimed by the revocation + /// signature. + HTLCSpendConfirmation { + input_idx: u32, + /// If the claim was made by either party with a preimage, this is filled in + preimage: Option, + /// If the claim was made by us on an inbound HTLC against a local commitment transaction, + /// we set this to the output CSV value which we will have to wait until to spend the + /// output (and generate a SpendableOutput event). + on_to_local_output_csv: Option, + }, } impl Writeable for OnchainEventEntry { @@ -438,14 +461,20 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent, (0, source, required), (1, onchain_value_satoshis, option), (2, payment_hash, required), + (3, input_idx, option), }, (1, MaturingOutput) => { (0, descriptor, required), }, (3, FundingSpendConfirmation) => { - (0, txid, required), - (2, on_local_output_csv, option), + (0, on_local_output_csv, option), }, + (5, HTLCSpendConfirmation) => { + (0, input_idx, required), + (2, preimage, option), + (4, on_to_local_output_csv, option), + }, + ); #[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))] @@ -506,6 +535,19 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep, }, ); +/// An HTLC which has been irrevocably resolved on-chain, and has reached ANTI_REORG_DELAY. +#[derive(PartialEq)] +struct IrrevocablyResolvedHTLC { + input_idx: u32, + /// Only set if the HTLC claim was ours using a payment preimage + payment_preimage: Option, +} + +impl_writeable_tlv_based!(IrrevocablyResolvedHTLC, { + (0, input_idx, required), + (2, payment_preimage, option), +}); + /// A ChannelMonitor handles chain events (blocks connected and disconnected) and generates /// on-chain transactions to ensure no loss of funds occurs. /// @@ -619,6 +661,10 @@ pub(crate) struct ChannelMonitorImpl { holder_tx_signed: bool, funding_spend_confirmed: Option, + /// The set of HTLCs which have been either claimed or failed on chain and have reached + /// the requisite confirmations on the claim/fail transaction (either ANTI_REORG_DELAY or the + /// spending CSV for revocable outputs). + htlcs_resolved_on_chain: Vec, // We simply modify best_block in Channel's block_connected so that serialization is // consistent but hopefully the users' copy handles block_connected in a consistent way. @@ -679,7 +725,8 @@ impl PartialEq for ChannelMonitorImpl { self.outputs_to_watch != other.outputs_to_watch || self.lockdown_from_offchain != other.lockdown_from_offchain || self.holder_tx_signed != other.holder_tx_signed || - self.funding_spend_confirmed != other.funding_spend_confirmed + self.funding_spend_confirmed != other.funding_spend_confirmed || + self.htlcs_resolved_on_chain != other.htlcs_resolved_on_chain { false } else { @@ -846,6 +893,7 @@ impl Writeable for ChannelMonitorImpl { write_tlv_fields!(writer, { (1, self.funding_spend_confirmed, option), + (3, self.htlcs_resolved_on_chain, vec_type), }); Ok(()) @@ -945,6 +993,7 @@ impl ChannelMonitor { lockdown_from_offchain: false, holder_tx_signed: false, funding_spend_confirmed: None, + htlcs_resolved_on_chain: Vec::new(), best_block, @@ -1311,6 +1360,7 @@ macro_rules! fail_unbroadcast_htlcs { source: (**source).clone(), payment_hash: htlc.payment_hash.clone(), onchain_value_satoshis: Some(htlc.amount_msat / 1000), + input_idx: None, }, }; log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction, waiting for confirmation (at height {})", @@ -2024,7 +2074,6 @@ impl ChannelMonitorImpl { txid, height: height, event: OnchainEvent::FundingSpendConfirmation { - txid, on_local_output_csv: balance_spendable_csv, }, }); @@ -2115,8 +2164,7 @@ impl ChannelMonitorImpl { .iter() .filter_map(|entry| match &entry.event { OnchainEvent::HTLCUpdate { source, .. } => Some(source), - OnchainEvent::MaturingOutput { .. } => None, - OnchainEvent::FundingSpendConfirmation { .. } => None, + _ => None, }) .collect(); #[cfg(debug_assertions)] @@ -2125,7 +2173,7 @@ impl ChannelMonitorImpl { // Produce actionable events from on-chain events having reached their threshold. for entry in onchain_events_reaching_threshold_conf.drain(..) { match entry.event { - OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis } => { + OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis, input_idx } => { // Check for duplicate HTLC resolutions. #[cfg(debug_assertions)] { @@ -2149,6 +2197,9 @@ impl ChannelMonitorImpl { source: source.clone(), onchain_value_satoshis, })); + if let Some(idx) = input_idx { + self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { input_idx: idx, payment_preimage: None }); + } }, OnchainEvent::MaturingOutput { descriptor } => { log_debug!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor)); @@ -2156,8 +2207,11 @@ impl ChannelMonitorImpl { outputs: vec![descriptor] }); }, - OnchainEvent::FundingSpendConfirmation { txid, .. } => { - self.funding_spend_confirmed = Some(txid); + OnchainEvent::HTLCSpendConfirmation { input_idx, preimage, .. } => { + self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { input_idx, payment_preimage: preimage }); + }, + OnchainEvent::FundingSpendConfirmation { .. } => { + self.funding_spend_confirmed = Some(entry.txid); }, } } @@ -2336,15 +2390,32 @@ impl ChannelMonitorImpl { let revocation_sig_claim = (input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC) && input.witness[1].len() == 33) || (input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::AcceptedHTLC) && input.witness[1].len() == 33); let accepted_preimage_claim = input.witness.len() == 5 && HTLCType::scriptlen_to_htlctype(input.witness[4].len()) == Some(HTLCType::AcceptedHTLC); - let offered_preimage_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC); + let accepted_timeout_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::AcceptedHTLC) && !revocation_sig_claim; + let offered_preimage_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC) && !revocation_sig_claim; + let offered_timeout_claim = input.witness.len() == 5 && HTLCType::scriptlen_to_htlctype(input.witness[4].len()) == Some(HTLCType::OfferedHTLC); + + let mut payment_preimage = PaymentPreimage([0; 32]); + if accepted_preimage_claim { + payment_preimage.0.copy_from_slice(&input.witness[3]); + } else if offered_preimage_claim { + payment_preimage.0.copy_from_slice(&input.witness[1]); + } macro_rules! log_claim { ($tx_info: expr, $holder_tx: expr, $htlc: expr, $source_avail: expr) => { - // We found the output in question, but aren't failing it backwards - // as we have no corresponding source and no valid counterparty commitment txid - // to try a weak source binding with same-hash, same-value still-valid offered HTLC. - // This implies either it is an inbound HTLC or an outbound HTLC on a revoked transaction. let outbound_htlc = $holder_tx == $htlc.offered; + // HTLCs must either be claimed by a matching script type or through the + // revocation path: + #[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures" + debug_assert!(!$htlc.offered || offered_preimage_claim || offered_timeout_claim || revocation_sig_claim); + #[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures" + debug_assert!($htlc.offered || accepted_preimage_claim || accepted_timeout_claim || revocation_sig_claim); + // Further, only exactly one of the possible spend paths should have been + // matched by any HTLC spend: + #[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures" + debug_assert_eq!(accepted_preimage_claim as u8 + accepted_timeout_claim as u8 + + offered_preimage_claim as u8 + offered_timeout_claim as u8 + + revocation_sig_claim as u8, 1); if ($holder_tx && revocation_sig_claim) || (outbound_htlc && !$source_avail && (accepted_preimage_claim || offered_preimage_claim)) { log_error!(logger, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}!", @@ -2396,6 +2467,30 @@ impl ChannelMonitorImpl { } if payment_data.is_none() { log_claim!($tx_info, $holder_tx, htlc_output, false); + let outbound_htlc = $holder_tx == htlc_output.offered; + if !outbound_htlc || revocation_sig_claim { + self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { + txid: tx.txid(), height, + event: OnchainEvent::HTLCSpendConfirmation { + input_idx: input.previous_output.vout, + preimage: if accepted_preimage_claim || offered_preimage_claim { + Some(payment_preimage) } else { None }, + // If this is a payment to us (!outbound_htlc, above), + // wait for the CSV delay before dropping the HTLC from + // claimable balance if the claim was an HTLC-Success + // transaction. + on_to_local_output_csv: if accepted_preimage_claim { + Some(self.on_holder_tx_csv) } else { None }, + }, + }); + } else { + // Outbound claims should always have payment_data, unless + // we've already failed the HTLC as the commitment transaction + // which was broadcasted was revoked. In that case, we should + // spend the HTLC output here immediately, and expose that fact + // as a ClaimableBalance, something which we do not yet do. + // TODO: Track the above as claimable! + } continue 'outer_loop; } } @@ -2421,11 +2516,18 @@ impl ChannelMonitorImpl { // Check that scan_commitment, above, decided there is some source worth relaying an // HTLC resolution backwards to and figure out whether we learned a preimage from it. if let Some((source, payment_hash, amount_msat)) = payment_data { - let mut payment_preimage = PaymentPreimage([0; 32]); if accepted_preimage_claim { if !self.pending_monitor_events.iter().any( |update| if let &MonitorEvent::HTLCEvent(ref upd) = update { upd.source == source } else { false }) { - payment_preimage.0.copy_from_slice(&input.witness[3]); + self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { + txid: tx.txid(), + height, + event: OnchainEvent::HTLCSpendConfirmation { + input_idx: input.previous_output.vout, + preimage: Some(payment_preimage), + on_to_local_output_csv: None, + }, + }); self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate { source, payment_preimage: Some(payment_preimage), @@ -2438,7 +2540,15 @@ impl ChannelMonitorImpl { |update| if let &MonitorEvent::HTLCEvent(ref upd) = update { upd.source == source } else { false }) { - payment_preimage.0.copy_from_slice(&input.witness[1]); + self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { + txid: tx.txid(), + height, + event: OnchainEvent::HTLCSpendConfirmation { + input_idx: input.previous_output.vout, + preimage: Some(payment_preimage), + on_to_local_output_csv: None, + }, + }); self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate { source, payment_preimage: Some(payment_preimage), @@ -2462,6 +2572,7 @@ impl ChannelMonitorImpl { event: OnchainEvent::HTLCUpdate { source, payment_hash, onchain_value_satoshis: Some(amount_msat / 1000), + input_idx: Some(input.previous_output.vout), }, }; log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", log_bytes!(payment_hash.0), entry.confirmation_threshold()); @@ -2834,8 +2945,10 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> } let mut funding_spend_confirmed = None; + let mut htlcs_resolved_on_chain = Some(Vec::new()); read_tlv_fields!(reader, { (1, funding_spend_confirmed, option), + (3, htlcs_resolved_on_chain, vec_type), }); let mut secp_ctx = Secp256k1::new(); @@ -2886,6 +2999,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> lockdown_from_offchain, holder_tx_signed, funding_spend_confirmed, + htlcs_resolved_on_chain: htlcs_resolved_on_chain.unwrap(), best_block, From f9febb0351414222dc61e1bef647b140bf7a1e39 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 25 Aug 2021 19:36:43 +0000 Subject: [PATCH 08/11] Fix indentation in ChannelMonitor --- lightning/src/chain/channelmonitor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 1b5a56d59e1..b2ed3d99de7 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -2460,7 +2460,7 @@ impl ChannelMonitorImpl { // resolve the source HTLC with the original sender. payment_data = Some(((*source).clone(), htlc_output.payment_hash, htlc_output.amount_msat)); } else if !$holder_tx { - check_htlc_valid_counterparty!(self.current_counterparty_commitment_txid, htlc_output); + check_htlc_valid_counterparty!(self.current_counterparty_commitment_txid, htlc_output); if payment_data.is_none() { check_htlc_valid_counterparty!(self.prev_counterparty_commitment_txid, htlc_output); } From 00906418aabc482c0067b085e6f57db61df68bb2 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 4 Aug 2021 16:10:38 +0000 Subject: [PATCH 09/11] Expose the amount of funds available for claim in ChannelMonitor In general, we should always allow users to query for how much is currently in-flight being claimed on-chain at any time. This does so by examining the confirmed claims on-chain and breaking down what is left to be claimed into a new `ClaimableBalance` enum. Fixes #995. --- lightning/src/chain/channelmonitor.rs | 222 ++++++++++++- lightning/src/ln/monitor_tests.rs | 457 +++++++++++++++++++++++++- 2 files changed, 676 insertions(+), 3 deletions(-) diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index b2ed3d99de7..30e2793dcb9 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -535,6 +535,59 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep, }, ); +/// Details about the balance(s) available for spending once the channel appears on chain. +/// +/// See [`ChannelMonitor::get_claimable_balances`] for more details on when these will or will not +/// be provided. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(test, derive(PartialOrd, Ord))] +pub enum Balance { + /// The channel is not yet closed (or the commitment or closing transaction has not yet + /// appeared in a block). The given balance is claimable (less on-chain fees) if the channel is + /// force-closed now. + ClaimableOnChannelClose { + /// The amount available to claim, in satoshis, excluding the on-chain fees which will be + /// required to do so. + claimable_amount_satoshis: u64, + }, + /// The channel has been closed, and the given balance is ours but awaiting confirmations until + /// we consider it spendable. + ClaimableAwaitingConfirmations { + /// The amount available to claim, in satoshis, possibly excluding the on-chain fees which + /// were spent in broadcasting the transaction. + claimable_amount_satoshis: u64, + /// The height at which an [`Event::SpendableOutputs`] event will be generated for this + /// amount. + confirmation_height: u32, + }, + /// The channel has been closed, and the given balance should be ours but awaiting spending + /// transaction confirmation. If the spending transaction does not confirm in time, it is + /// possible our counterparty can take the funds by broadcasting an HTLC timeout on-chain. + /// + /// Once the spending transaction confirms, before it has reached enough confirmations to be + /// considered safe from chain reorganizations, the balance will instead be provided via + /// [`Balance::ClaimableAwaitingConfirmations`]. + ContentiousClaimable { + /// The amount available to claim, in satoshis, excluding the on-chain fees which will be + /// required to do so. + claimable_amount_satoshis: u64, + /// The height at which the counterparty may be able to claim the balance if we have not + /// done so. + timeout_height: u32, + }, + /// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain + /// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat + /// likely to be claimed by our counterparty before we do. + MaybeClaimableHTLCAwaitingTimeout { + /// The amount available to claim, in satoshis, excluding the on-chain fees which will be + /// required to do so. + claimable_amount_satoshis: u64, + /// The height at which we will be able to claim the balance if our counterparty has not + /// done so. + claimable_height: u32, + }, +} + /// An HTLC which has been irrevocably resolved on-chain, and has reached ANTI_REORG_DELAY. #[derive(PartialEq)] struct IrrevocablyResolvedHTLC { @@ -1302,6 +1355,173 @@ impl ChannelMonitor { pub fn current_best_block(&self) -> BestBlock { self.inner.lock().unwrap().best_block.clone() } + + /// Gets the balances in this channel which are either claimable by us if we were to + /// force-close the channel now or which are claimable on-chain (possibly awaiting + /// confirmation). + /// + /// Any balances in the channel which are available on-chain (excluding on-chain fees) are + /// included here until an [`Event::SpendableOutputs`] event has been generated for the + /// balance, or until our counterparty has claimed the balance and accrued several + /// confirmations on the claim transaction. + /// + /// Note that the balances available when you or your counterparty have broadcasted revoked + /// state(s) may not be fully captured here. + // TODO, fix that ^ + /// + /// See [`Balance`] for additional details on the types of claimable balances which + /// may be returned here and their meanings. + pub fn get_claimable_balances(&self) -> Vec { + let mut res = Vec::new(); + let us = self.inner.lock().unwrap(); + + let mut confirmed_txid = us.funding_spend_confirmed; + let mut pending_commitment_tx_conf_thresh = None; + let funding_spend_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| { + if let OnchainEvent::FundingSpendConfirmation { .. } = event.event { + Some((event.txid, event.confirmation_threshold())) + } else { None } + }); + if let Some((txid, conf_thresh)) = funding_spend_pending { + debug_assert!(us.funding_spend_confirmed.is_none(), + "We have a pending funding spend awaiting anti-reorg confirmation, we can't have confirmed it already!"); + confirmed_txid = Some(txid); + pending_commitment_tx_conf_thresh = Some(conf_thresh); + } + + macro_rules! walk_htlcs { + ($holder_commitment: expr, $htlc_iter: expr) => { + for htlc in $htlc_iter { + if let Some(htlc_input_idx) = htlc.transaction_output_index { + if us.htlcs_resolved_on_chain.iter().any(|v| v.input_idx == htlc_input_idx) { + assert!(us.funding_spend_confirmed.is_some()); + } else if htlc.offered == $holder_commitment { + // If the payment was outbound, check if there's an HTLCUpdate + // indicating we have spent this HTLC with a timeout, claiming it back + // and awaiting confirmations on it. + let htlc_update_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| { + if let OnchainEvent::HTLCUpdate { input_idx: Some(input_idx), .. } = event.event { + if input_idx == htlc_input_idx { Some(event.confirmation_threshold()) } else { None } + } else { None } + }); + if let Some(conf_thresh) = htlc_update_pending { + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: htlc.amount_msat / 1000, + confirmation_height: conf_thresh, + }); + } else { + res.push(Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: htlc.amount_msat / 1000, + claimable_height: htlc.cltv_expiry, + }); + } + } else if us.payment_preimages.get(&htlc.payment_hash).is_some() { + // Otherwise (the payment was inbound), only expose it as claimable if + // we know the preimage. + // Note that if there is a pending claim, but it did not use the + // preimage, we lost funds to our counterparty! We will then continue + // to show it as ContentiousClaimable until ANTI_REORG_DELAY. + let htlc_spend_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| { + if let OnchainEvent::HTLCSpendConfirmation { input_idx, preimage, .. } = event.event { + if input_idx == htlc_input_idx { + Some((event.confirmation_threshold(), preimage.is_some())) + } else { None } + } else { None } + }); + if let Some((conf_thresh, true)) = htlc_spend_pending { + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: htlc.amount_msat / 1000, + confirmation_height: conf_thresh, + }); + } else { + res.push(Balance::ContentiousClaimable { + claimable_amount_satoshis: htlc.amount_msat / 1000, + timeout_height: htlc.cltv_expiry, + }); + } + } + } + } + } + } + + if let Some(txid) = confirmed_txid { + let mut found_commitment_tx = false; + if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid { + walk_htlcs!(false, us.counterparty_claimable_outpoints.get(&txid).unwrap().iter().map(|(a, _)| a)); + if let Some(conf_thresh) = pending_commitment_tx_conf_thresh { + if let Some(value) = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| { + if let OnchainEvent::MaturingOutput { + descriptor: SpendableOutputDescriptor::StaticPaymentOutput(descriptor) + } = &event.event { + Some(descriptor.output.value) + } else { None } + }) { + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: value, + confirmation_height: conf_thresh, + }); + } else { + // If a counterparty commitment transaction is awaiting confirmation, we + // should either have a StaticPaymentOutput MaturingOutput event awaiting + // confirmation with the same height or have never met our dust amount. + } + } + found_commitment_tx = true; + } else if txid == us.current_holder_commitment_tx.txid { + walk_htlcs!(true, us.current_holder_commitment_tx.htlc_outputs.iter().map(|(a, _, _)| a)); + if let Some(conf_thresh) = pending_commitment_tx_conf_thresh { + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat, + confirmation_height: conf_thresh, + }); + } + found_commitment_tx = true; + } else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx { + if txid == prev_commitment.txid { + walk_htlcs!(true, prev_commitment.htlc_outputs.iter().map(|(a, _, _)| a)); + if let Some(conf_thresh) = pending_commitment_tx_conf_thresh { + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: prev_commitment.to_self_value_sat, + confirmation_height: conf_thresh, + }); + } + found_commitment_tx = true; + } + } + if !found_commitment_tx { + if let Some(conf_thresh) = pending_commitment_tx_conf_thresh { + // We blindly assume this is a cooperative close transaction here, and that + // neither us nor our counterparty misbehaved. At worst we've under-estimated + // the amount we can claim as we'll punish a misbehaving counterparty. + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat, + confirmation_height: conf_thresh, + }); + } + } + // TODO: Add logic to provide claimable balances for counterparty broadcasting revoked + // outputs. + } else { + let mut claimable_inbound_htlc_value_sat = 0; + for (htlc, _, _) in us.current_holder_commitment_tx.htlc_outputs.iter() { + if htlc.transaction_output_index.is_none() { continue; } + if htlc.offered { + res.push(Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: htlc.amount_msat / 1000, + claimable_height: htlc.cltv_expiry, + }); + } else if us.payment_preimages.get(&htlc.payment_hash).is_some() { + claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000; + } + } + res.push(Balance::ClaimableOnChannelClose { + claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat + claimable_inbound_htlc_value_sat, + }); + } + + res + } } /// Compares a broadcasted commitment transaction's HTLCs with those in the latest state, @@ -2488,7 +2708,7 @@ impl ChannelMonitorImpl { // we've already failed the HTLC as the commitment transaction // which was broadcasted was revoked. In that case, we should // spend the HTLC output here immediately, and expose that fact - // as a ClaimableBalance, something which we do not yet do. + // as a Balance, something which we do not yet do. // TODO: Track the above as claimable! } continue 'outer_loop; diff --git a/lightning/src/ln/monitor_tests.rs b/lightning/src/ln/monitor_tests.rs index 8fdf28e578b..1a332fbb7fd 100644 --- a/lightning/src/ln/monitor_tests.rs +++ b/lightning/src/ln/monitor_tests.rs @@ -9,8 +9,10 @@ //! Further functional tests which test blockchain reorganizations. -use chain::channelmonitor::ANTI_REORG_DELAY; -use ln::{PaymentPreimage, PaymentHash}; +use chain::channelmonitor::{ANTI_REORG_DELAY, Balance}; +use chain::transaction::OutPoint; +use ln::{channel, PaymentPreimage, PaymentHash}; +use ln::channelmanager::BREAKDOWN_TIMEOUT; use ln::features::InitFeatures; use ln::msgs::{ChannelMessageHandler, HTLCFailChannelUpdate, ErrorAction}; use util::events::{Event, MessageSendEvent, MessageSendEventsProvider}; @@ -19,6 +21,10 @@ use routing::router::get_route; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash; +use bitcoin::blockdata::script::Builder; +use bitcoin::blockdata::opcodes; +use bitcoin::secp256k1::Secp256k1; + use prelude::*; use ln::functional_test_utils::*; @@ -79,3 +85,450 @@ fn chanmon_fail_from_stale_commitment() { expect_payment_failed!(nodes[0], payment_hash, false); expect_payment_failure_chan_update!(nodes[0], update_a.contents.short_channel_id, true); } + +#[test] +fn chanmon_claim_value_coop_close() { + // Tests `get_claimable_balances` returns the correct values across a simple cooperative claim. + // Specifically, this tests that the channel non-HTLC balances show up in + // `get_claimable_balances` until the cooperative claims have confirmed and generated a + // `SpendableOutputs` event, and no longer. + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let (_, _, chan_id, funding_tx) = + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 1_000_000, InitFeatures::known(), InitFeatures::known()); + let funding_outpoint = OutPoint { txid: funding_tx.txid(), index: 0 }; + assert_eq!(funding_outpoint.to_channel_id(), chan_id); + + let chan_feerate = get_feerate!(nodes[0], chan_id) as u64; + + assert_eq!(vec![Balance::ClaimableOnChannelClose { + claimable_amount_satoshis: 1_000_000 - 1_000 - chan_feerate * channel::COMMITMENT_TX_BASE_WEIGHT / 1000 + }], + nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + assert_eq!(vec![Balance::ClaimableOnChannelClose { claimable_amount_satoshis: 1_000, }], + nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + + nodes[0].node.close_channel(&chan_id).unwrap(); + let node_0_shutdown = get_event_msg!(nodes[0], MessageSendEvent::SendShutdown, nodes[1].node.get_our_node_id()); + nodes[1].node.handle_shutdown(&nodes[0].node.get_our_node_id(), &InitFeatures::known(), &node_0_shutdown); + let node_1_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id()); + nodes[0].node.handle_shutdown(&nodes[1].node.get_our_node_id(), &InitFeatures::known(), &node_1_shutdown); + + let node_0_closing_signed = get_event_msg!(nodes[0], MessageSendEvent::SendClosingSigned, nodes[1].node.get_our_node_id()); + nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &node_0_closing_signed); + let node_1_closing_signed = get_event_msg!(nodes[1], MessageSendEvent::SendClosingSigned, nodes[0].node.get_our_node_id()); + nodes[0].node.handle_closing_signed(&nodes[1].node.get_our_node_id(), &node_1_closing_signed); + let (_, node_0_2nd_closing_signed) = get_closing_signed_broadcast!(nodes[0].node, nodes[1].node.get_our_node_id()); + nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &node_0_2nd_closing_signed.unwrap()); + let (_, node_1_none) = get_closing_signed_broadcast!(nodes[1].node, nodes[0].node.get_our_node_id()); + assert!(node_1_none.is_none()); + + let shutdown_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0); + assert_eq!(shutdown_tx, nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0)); + assert_eq!(shutdown_tx.len(), 1); + + mine_transaction(&nodes[0], &shutdown_tx[0]); + mine_transaction(&nodes[1], &shutdown_tx[0]); + + assert!(nodes[0].node.list_channels().is_empty()); + assert!(nodes[1].node.list_channels().is_empty()); + + assert!(nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty()); + assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty()); + + assert_eq!(vec![Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: 1_000_000 - 1_000 - chan_feerate * channel::COMMITMENT_TX_BASE_WEIGHT / 1000, + confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1, + }], + nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + assert_eq!(vec![Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: 1000, + confirmation_height: nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1, + }], + nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + + connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1); + connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1); + + assert_eq!(Vec::::new(), + nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + assert_eq!(Vec::::new(), + nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + + let mut node_a_spendable = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events(); + assert_eq!(node_a_spendable.len(), 1); + if let Event::SpendableOutputs { outputs } = node_a_spendable.pop().unwrap() { + assert_eq!(outputs.len(), 1); + let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(), + Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap(); + check_spends!(spend_tx, shutdown_tx[0]); + } + + let mut node_b_spendable = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events(); + assert_eq!(node_b_spendable.len(), 1); + if let Event::SpendableOutputs { outputs } = node_b_spendable.pop().unwrap() { + assert_eq!(outputs.len(), 1); + let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(), + Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap(); + check_spends!(spend_tx, shutdown_tx[0]); + } +} + +fn sorted_vec(mut v: Vec) -> Vec { + v.sort_unstable(); + v +} + +fn do_test_claim_value_force_close(prev_commitment_tx: bool) { + // Tests `get_claimable_balances` with an HTLC across a force-close. + // We build a channel with an HTLC pending, then force close the channel and check that the + // `get_claimable_balances` return value is correct as transactions confirm on-chain. + let mut chanmon_cfgs = create_chanmon_cfgs(2); + if prev_commitment_tx { + // We broadcast a second-to-latest commitment transaction, without providing the revocation + // secret to the counterparty. However, because we always immediately take the revocation + // secret from the keys_manager, we would panic at broadcast as we're trying to sign a + // transaction which, from the point of view of our keys_manager, is revoked. + chanmon_cfgs[1].keys_manager.disable_revocation_policy_check = true; + } + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let (_, _, chan_id, funding_tx) = + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 1_000_000, InitFeatures::known(), InitFeatures::known()); + let funding_outpoint = OutPoint { txid: funding_tx.txid(), index: 0 }; + assert_eq!(funding_outpoint.to_channel_id(), chan_id); + + // This HTLC is immediately claimed, giving node B the preimage + let payment_preimage = route_payment(&nodes[0], &[&nodes[1]], 3_000_000).0; + // This HTLC is allowed to time out, letting A claim it. However, in order to test claimable + // balances more fully we also give B the preimage for this HTLC. + let (timeout_payment_preimage, timeout_payment_hash, _) = route_payment(&nodes[0], &[&nodes[1]], 4_000_000); + // This HTLC will be dust, and not be claimable at all: + let (dust_payment_preimage, dust_payment_hash, _) = route_payment(&nodes[0], &[&nodes[1]], 3_000); + + let htlc_cltv_timeout = nodes[0].best_block_info().1 + TEST_FINAL_CLTV + 1; // Note ChannelManager adds one to CLTV timeouts for safety + + let chan_feerate = get_feerate!(nodes[0], chan_id) as u64; + + let remote_txn = get_local_commitment_txn!(nodes[1], chan_id); + // Before B receives the payment preimage, it only suggests the push_msat value of 1_000 sats + // as claimable. A lists both its to-self balance and the (possibly-claimable) HTLCs. + assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose { + claimable_amount_satoshis: 1_000_000 - 3_000 - 4_000 - 1_000 - 3 - chan_feerate * + (channel::COMMITMENT_TX_BASE_WEIGHT + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000, + }, Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 3_000, + claimable_height: htlc_cltv_timeout, + }, Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 4_000, + claimable_height: htlc_cltv_timeout, + }]), + sorted_vec(nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances())); + assert_eq!(vec![Balance::ClaimableOnChannelClose { + claimable_amount_satoshis: 1_000, + }], + nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + + nodes[1].node.claim_funds(payment_preimage); + check_added_monitors!(nodes[1], 1); + let b_htlc_msgs = get_htlc_update_msgs!(&nodes[1], nodes[0].node.get_our_node_id()); + // We claim the dust payment here as well, but it won't impact our claimable balances as its + // dust and thus doesn't appear on chain at all. + nodes[1].node.claim_funds(dust_payment_preimage); + check_added_monitors!(nodes[1], 1); + nodes[1].node.claim_funds(timeout_payment_preimage); + check_added_monitors!(nodes[1], 1); + + if prev_commitment_tx { + // To build a previous commitment transaction, deliver one round of commitment messages. + nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &b_htlc_msgs.update_fulfill_htlcs[0]); + expect_payment_sent!(nodes[0], payment_preimage); + nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &b_htlc_msgs.commitment_signed); + check_added_monitors!(nodes[0], 1); + let (as_raa, as_cs) = get_revoke_commit_msgs!(nodes[0], nodes[1].node.get_our_node_id()); + nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_raa); + let _htlc_updates = get_htlc_update_msgs!(&nodes[1], nodes[0].node.get_our_node_id()); + check_added_monitors!(nodes[1], 1); + nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &as_cs); + let _bs_raa = get_event_msg!(nodes[1], MessageSendEvent::SendRevokeAndACK, nodes[0].node.get_our_node_id()); + check_added_monitors!(nodes[1], 1); + } + + // Once B has received the payment preimage, it includes the value of the HTLC in its + // "claimable if you were to close the channel" balance. + let mut a_expected_balances = vec![Balance::ClaimableOnChannelClose { + claimable_amount_satoshis: 1_000_000 - // Channel funding value in satoshis + 4_000 - // The to-be-failed HTLC value in satoshis + 3_000 - // The claimed HTLC value in satoshis + 1_000 - // The push_msat value in satoshis + 3 - // The dust HTLC value in satoshis + // The commitment transaction fee with two HTLC outputs: + chan_feerate * (channel::COMMITMENT_TX_BASE_WEIGHT + + if prev_commitment_tx { 1 } else { 2 } * + channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000, + }, Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 4_000, + claimable_height: htlc_cltv_timeout, + }]; + if !prev_commitment_tx { + a_expected_balances.push(Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 3_000, + claimable_height: htlc_cltv_timeout, + }); + } + assert_eq!(sorted_vec(a_expected_balances), + sorted_vec(nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances())); + assert_eq!(vec![Balance::ClaimableOnChannelClose { + claimable_amount_satoshis: 1_000 + 3_000 + 4_000, + }], + nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + + // Broadcast the closing transaction (which has both pending HTLCs in it) and get B's + // broadcasted HTLC claim transaction with preimage. + let node_b_commitment_claimable = nodes[1].best_block_info().1 + BREAKDOWN_TIMEOUT as u32; + mine_transaction(&nodes[0], &remote_txn[0]); + mine_transaction(&nodes[1], &remote_txn[0]); + + let b_broadcast_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0); + assert_eq!(b_broadcast_txn.len(), if prev_commitment_tx { 4 } else { 5 }); + if prev_commitment_tx { + check_spends!(b_broadcast_txn[3], b_broadcast_txn[2]); + } else { + assert_eq!(b_broadcast_txn[0], b_broadcast_txn[3]); + assert_eq!(b_broadcast_txn[1], b_broadcast_txn[4]); + } + // b_broadcast_txn[0] should spend the HTLC output of the commitment tx for 3_000 sats + check_spends!(b_broadcast_txn[0], remote_txn[0]); + check_spends!(b_broadcast_txn[1], remote_txn[0]); + assert_eq!(b_broadcast_txn[0].input.len(), 1); + assert_eq!(b_broadcast_txn[1].input.len(), 1); + assert_eq!(remote_txn[0].output[b_broadcast_txn[0].input[0].previous_output.vout as usize].value, 3_000); + assert_eq!(remote_txn[0].output[b_broadcast_txn[1].input[0].previous_output.vout as usize].value, 4_000); + check_spends!(b_broadcast_txn[2], funding_tx); + + assert!(nodes[0].node.list_channels().is_empty()); + check_closed_broadcast!(nodes[0], true); + check_added_monitors!(nodes[0], 1); + assert!(nodes[1].node.list_channels().is_empty()); + check_closed_broadcast!(nodes[1], true); + check_added_monitors!(nodes[1], 1); + assert!(nodes[0].node.get_and_clear_pending_events().is_empty()); + assert!(nodes[1].node.get_and_clear_pending_events().is_empty()); + + // Once the commitment transaction confirms, we will wait until ANTI_REORG_DELAY until we + // generate any `SpendableOutputs` events. Thus, the same balances will still be listed + // available in `get_claimable_balances`. However, both will swap from `ClaimableOnClose` to + // other Balance variants, as close has already happened. + assert!(nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty()); + assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty()); + + assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: 1_000_000 - 3_000 - 4_000 - 1_000 - 3 - chan_feerate * + (channel::COMMITMENT_TX_BASE_WEIGHT + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000, + confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1, + }, Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 3_000, + claimable_height: htlc_cltv_timeout, + }, Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 4_000, + claimable_height: htlc_cltv_timeout, + }]), + sorted_vec(nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances())); + // The main non-HTLC balance is just awaiting confirmations, but the claimable height is the + // CSV delay, not ANTI_REORG_DELAY. + assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: 1_000, + confirmation_height: node_b_commitment_claimable, + }, + // Both HTLC balances are "contentious" as our counterparty could claim them if we wait too + // long. + Balance::ContentiousClaimable { + claimable_amount_satoshis: 3_000, + timeout_height: htlc_cltv_timeout, + }, Balance::ContentiousClaimable { + claimable_amount_satoshis: 4_000, + timeout_height: htlc_cltv_timeout, + }]), + sorted_vec(nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances())); + + connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1); + expect_payment_failed!(nodes[0], dust_payment_hash, true); + connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1); + + // After ANTI_REORG_DELAY, A will consider its balance fully spendable and generate a + // `SpendableOutputs` event. However, B still has to wait for the CSV delay. + assert_eq!(sorted_vec(vec![Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 3_000, + claimable_height: htlc_cltv_timeout, + }, Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 4_000, + claimable_height: htlc_cltv_timeout, + }]), + sorted_vec(nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances())); + assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: 1_000, + confirmation_height: node_b_commitment_claimable, + }, Balance::ContentiousClaimable { + claimable_amount_satoshis: 3_000, + timeout_height: htlc_cltv_timeout, + }, Balance::ContentiousClaimable { + claimable_amount_satoshis: 4_000, + timeout_height: htlc_cltv_timeout, + }]), + sorted_vec(nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances())); + + let mut node_a_spendable = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events(); + assert_eq!(node_a_spendable.len(), 1); + if let Event::SpendableOutputs { outputs } = node_a_spendable.pop().unwrap() { + assert_eq!(outputs.len(), 1); + let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(), + Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap(); + check_spends!(spend_tx, remote_txn[0]); + } + + assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty()); + + // After broadcasting the HTLC claim transaction, node A will still consider the HTLC + // possibly-claimable up to ANTI_REORG_DELAY, at which point it will drop it. + mine_transaction(&nodes[0], &b_broadcast_txn[0]); + if !prev_commitment_tx { + expect_payment_sent!(nodes[0], payment_preimage); + } + assert_eq!(sorted_vec(vec![Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 3_000, + claimable_height: htlc_cltv_timeout, + }, Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 4_000, + claimable_height: htlc_cltv_timeout, + }]), + sorted_vec(nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances())); + connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1); + assert_eq!(vec![Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: 4_000, + claimable_height: htlc_cltv_timeout, + }], + nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + + // When the HTLC timeout output is spendable in the next block, A should broadcast it + connect_blocks(&nodes[0], htlc_cltv_timeout - nodes[0].best_block_info().1 - 1); + let a_broadcast_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0); + assert_eq!(a_broadcast_txn.len(), 3); + check_spends!(a_broadcast_txn[0], funding_tx); + assert_eq!(a_broadcast_txn[1].input.len(), 1); + check_spends!(a_broadcast_txn[1], remote_txn[0]); + assert_eq!(a_broadcast_txn[2].input.len(), 1); + check_spends!(a_broadcast_txn[2], remote_txn[0]); + assert_ne!(a_broadcast_txn[1].input[0].previous_output.vout, + a_broadcast_txn[2].input[0].previous_output.vout); + // a_broadcast_txn [1] and [2] should spend the HTLC outputs of the commitment tx + assert_eq!(remote_txn[0].output[a_broadcast_txn[1].input[0].previous_output.vout as usize].value, 3_000); + assert_eq!(remote_txn[0].output[a_broadcast_txn[2].input[0].previous_output.vout as usize].value, 4_000); + + // Once the HTLC-Timeout transaction confirms, A will no longer consider the HTLC + // "MaybeClaimable", but instead move it to "AwaitingConfirmations". + mine_transaction(&nodes[0], &a_broadcast_txn[2]); + assert!(nodes[0].node.get_and_clear_pending_events().is_empty()); + assert_eq!(vec![Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: 4_000, + confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1, + }], + nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + // After ANTI_REORG_DELAY, A will generate a SpendableOutputs event and drop the claimable + // balance entry. + connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1); + assert_eq!(Vec::::new(), + nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + expect_payment_failed!(nodes[0], timeout_payment_hash, true); + + let mut node_a_spendable = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events(); + assert_eq!(node_a_spendable.len(), 1); + if let Event::SpendableOutputs { outputs } = node_a_spendable.pop().unwrap() { + assert_eq!(outputs.len(), 1); + let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(), + Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap(); + check_spends!(spend_tx, a_broadcast_txn[2]); + } else { panic!(); } + + // Node B will no longer consider the HTLC "contentious" after the HTLC claim transaction + // confirms, and consider it simply "awaiting confirmations". Note that it has to wait for the + // standard revocable transaction CSV delay before receiving a `SpendableOutputs`. + let node_b_htlc_claimable = nodes[1].best_block_info().1 + BREAKDOWN_TIMEOUT as u32; + mine_transaction(&nodes[1], &b_broadcast_txn[0]); + + assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: 1_000, + confirmation_height: node_b_commitment_claimable, + }, Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: 3_000, + confirmation_height: node_b_htlc_claimable, + }, Balance::ContentiousClaimable { + claimable_amount_satoshis: 4_000, + timeout_height: htlc_cltv_timeout, + }]), + sorted_vec(nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances())); + + // After reaching the commitment output CSV, we'll get a SpendableOutputs event for it and have + // only the HTLCs claimable on node B. + connect_blocks(&nodes[1], node_b_commitment_claimable - nodes[1].best_block_info().1); + + let mut node_b_spendable = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events(); + assert_eq!(node_b_spendable.len(), 1); + if let Event::SpendableOutputs { outputs } = node_b_spendable.pop().unwrap() { + assert_eq!(outputs.len(), 1); + let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(), + Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap(); + check_spends!(spend_tx, remote_txn[0]); + } + + assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: 3_000, + confirmation_height: node_b_htlc_claimable, + }, Balance::ContentiousClaimable { + claimable_amount_satoshis: 4_000, + timeout_height: htlc_cltv_timeout, + }]), + sorted_vec(nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances())); + + // After reaching the claimed HTLC output CSV, we'll get a SpendableOutptus event for it and + // have only one HTLC output left spendable. + connect_blocks(&nodes[1], node_b_htlc_claimable - nodes[1].best_block_info().1); + + let mut node_b_spendable = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events(); + assert_eq!(node_b_spendable.len(), 1); + if let Event::SpendableOutputs { outputs } = node_b_spendable.pop().unwrap() { + assert_eq!(outputs.len(), 1); + let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(), + Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap(); + check_spends!(spend_tx, b_broadcast_txn[0]); + } else { panic!(); } + + assert_eq!(vec![Balance::ContentiousClaimable { + claimable_amount_satoshis: 4_000, + timeout_height: htlc_cltv_timeout, + }], + nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + + // Finally, mine the HTLC timeout transaction that A broadcasted (even though B should be able + // to claim this HTLC with the preimage it knows!). It will remain listed as a claimable HTLC + // until ANTI_REORG_DELAY confirmations on the spend. + mine_transaction(&nodes[1], &a_broadcast_txn[2]); + assert_eq!(vec![Balance::ContentiousClaimable { + claimable_amount_satoshis: 4_000, + timeout_height: htlc_cltv_timeout, + }], + nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); + connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1); + assert_eq!(Vec::::new(), + nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()); +} + +#[test] +fn test_claim_value_force_close() { + do_test_claim_value_force_close(true); + do_test_claim_value_force_close(false); +} From a675886e101d1cfb79117a040af88e7f3f7727d0 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 25 Aug 2021 20:13:01 +0000 Subject: [PATCH 10/11] Add an accessor to `ChainMonitor` to get the claimable balances The common user desire is to get the set of claimable balances for all non-closed channels. In order to do so, they really want to just ask their `ChainMonitor` for the set of balances, which they can do here by passing the `ChannelManager::list_channels` output to `ChainMonitor::get_claimable_balances`. --- lightning/src/chain/chainmonitor.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index 8969427a0f9..f8fe6bf6d57 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -30,12 +30,13 @@ use chain; use chain::{Filter, WatchedOutput}; use chain::chaininterface::{BroadcasterInterface, FeeEstimator}; use chain::channelmonitor; -use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, MonitorEvent, Persist, TransactionOutputs}; +use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, Balance, MonitorEvent, Persist, TransactionOutputs}; use chain::transaction::{OutPoint, TransactionData}; use chain::keysinterface::Sign; use util::logger::Logger; use util::events; use util::events::EventHandler; +use ln::channelmanager::ChannelDetails; use prelude::*; use sync::RwLock; @@ -140,6 +141,31 @@ where C::Target: chain::Filter, } } + /// Gets the balances in the contained [`ChannelMonitor`]s which are claimable on-chain or + /// claims which are awaiting confirmation. + /// + /// Includes the balances from each [`ChannelMonitor`] *except* those included in + /// `ignored_channels`, allowing you to filter out balances from channels which are still open + /// (and whose balance should likely be pulled from the [`ChannelDetails`]). + /// + /// See [`ChannelMonitor::get_claimable_balances`] for more details on the exact criteria for + /// inclusion in the return value. + pub fn get_claimable_balances(&self, ignored_channels: &[ChannelDetails]) -> Vec { + let mut ret = Vec::new(); + let monitors = self.monitors.read().unwrap(); + for (_, monitor) in monitors.iter().filter(|(funding_outpoint, _)| { + for chan in ignored_channels { + if chan.funding_txo.as_ref() == Some(funding_outpoint) { + return false; + } + } + true + }) { + ret.append(&mut monitor.get_claimable_balances()); + } + ret + } + #[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))] pub fn get_and_clear_pending_events(&self) -> Vec { use util::events::EventsProvider; From cae212379371ff4d4eded0d576ea49095d7e00d3 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sun, 29 Aug 2021 19:01:05 +0000 Subject: [PATCH 11/11] Expand `ANTI_REORG_DELAY` docs to say its a library-wide assumption --- lightning/src/chain/channelmonitor.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 30e2793dcb9..413d184b598 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -232,8 +232,13 @@ pub(crate) const CLTV_CLAIM_BUFFER: u32 = 18; /// with at worst this delay, so we are not only using this value as a mercy for them but also /// us as a safeguard to delay with enough time. pub(crate) const LATENCY_GRACE_PERIOD_BLOCKS: u32 = 3; -/// Number of blocks we wait on seeing a HTLC output being solved before we fail corresponding inbound -/// HTLCs. This prevents us from failing backwards and then getting a reorg resulting in us losing money. +/// Number of blocks we wait on seeing a HTLC output being solved before we fail corresponding +/// inbound HTLCs. This prevents us from failing backwards and then getting a reorg resulting in us +/// losing money. +/// +/// Note that this is a library-wide security assumption. If a reorg deeper than this number of +/// blocks occurs, counterparties may be able to steal funds or claims made by and balances exposed +/// by a [`ChannelMonitor`] may be incorrect. // We also use this delay to be sure we can remove our in-flight claim txn from bump candidates buffer. // It may cause spurious generation of bumped claim txn but that's alright given the outpoint is already // solved by a previous claim tx. What we want to avoid is reorg evicting our claim tx and us not