From 73ee30d9da29ebe9e87b00db27c03f8830a754b1 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 4 Aug 2021 15:14:56 +0000 Subject: [PATCH] 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 761ad7bae..22708f18b 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, -- 2.39.5