X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fchannelmonitor.rs;h=92f1a8df45d8e88bb75e9db80c7974fa89a430ec;hb=refs%2Fheads%2F2022-12-fix-missing-data;hp=0bfd944456ab6851749505350c781e8ec267d27e;hpb=15b79f8fb5040d56b21683bc906c63fb8bb3c92c;p=rust-lightning diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 0bfd9444..92f1a8df 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -826,6 +826,13 @@ pub(crate) struct ChannelMonitorImpl { /// spending CSV for revocable outputs). htlcs_resolved_on_chain: Vec, + /// The set of `SpendableOutput` events which we have already passed upstream to be claimed. + /// These are tracked explicitly to ensure that we don't generate the same events redundantly + /// if users duplicatively confirm old transactions. Specifically for transactions claiming a + /// revoked remote outpoint we otherwise have no tracking at all once they've reached + /// [`ANTI_REORG_DELAY`], so we have to track them here. + spendable_txids_confirmed: 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. // (we do *not*, however, update them in update_monitor to ensure any local user copies keep @@ -1071,6 +1078,7 @@ impl Writeable for ChannelMonitorImpl { (7, self.funding_spend_seen, required), (9, self.counterparty_node_id, option), (11, self.confirmed_commitment_tx_counterparty_output, option), + (13, self.spendable_txids_confirmed, vec_type), }); Ok(()) @@ -1179,6 +1187,7 @@ impl ChannelMonitor { funding_spend_confirmed: None, confirmed_commitment_tx_counterparty_output: None, htlcs_resolved_on_chain: Vec::new(), + spendable_txids_confirmed: Vec::new(), best_block, counterparty_node_id: Some(counterparty_node_id), @@ -2860,7 +2869,37 @@ impl ChannelMonitorImpl { let mut watch_outputs = Vec::new(); let mut claimable_outpoints = Vec::new(); - for tx in &txn_matched { + 'tx_iter: for tx in &txn_matched { + let txid = tx.txid(); + // If a transaction has already been confirmed, ensure we don't bother processing it duplicatively. + if Some(txid) == self.funding_spend_confirmed { + log_debug!(logger, "Skipping redundant processing of funding-spend tx {} as it was previously confirmed", txid); + continue 'tx_iter; + } + for ev in self.onchain_events_awaiting_threshold_conf.iter() { + if ev.txid == txid { + if let Some(conf_hash) = ev.block_hash { + assert_eq!(header.block_hash(), conf_hash, + "Transaction {} was already confirmed and is being re-confirmed in a different block.\n\ + This indicates a severe bug in the transaction connection logic - a reorg should have been processed first!", ev.txid); + } + log_debug!(logger, "Skipping redundant processing of confirming tx {} as it was previously confirmed", txid); + continue 'tx_iter; + } + } + for htlc in self.htlcs_resolved_on_chain.iter() { + if Some(txid) == htlc.resolving_txid { + log_debug!(logger, "Skipping redundant processing of HTLC resolution tx {} as it was previously confirmed", txid); + continue 'tx_iter; + } + } + for spendable_txid in self.spendable_txids_confirmed.iter() { + if txid == *spendable_txid { + log_debug!(logger, "Skipping redundant processing of spendable tx {} as it was previously confirmed", txid); + continue 'tx_iter; + } + } + if tx.input.len() == 1 { // Assuming our keys were not leaked (in which case we're screwed no matter what), // commitment transactions and HTLC transactions will all only ever have one input, @@ -2870,7 +2909,7 @@ impl ChannelMonitorImpl { 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!(self.funding_info.0.to_channel_id()), tx.txid()); + log_bytes!(self.funding_info.0.to_channel_id()), txid); self.funding_spend_seen = true; let mut commitment_tx_to_counterparty_output = None; if (tx.input[0].sequence.0 >> 8*3) as u8 == 0x80 && (tx.lock_time.0 >> 8*3) as u8 == 0x20 { @@ -2893,7 +2932,6 @@ impl ChannelMonitorImpl { } } } - let txid = tx.txid(); self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { txid, transaction: Some((*tx).clone()), @@ -3042,6 +3080,7 @@ impl ChannelMonitorImpl { self.pending_events.push(Event::SpendableOutputs { outputs: vec![descriptor] }); + self.spendable_txids_confirmed.push(entry.txid); }, OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. } => { self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { @@ -3111,10 +3150,24 @@ impl ChannelMonitorImpl { F::Target: FeeEstimator, L::Target: Logger, { - self.onchain_events_awaiting_threshold_conf.retain(|ref entry| if entry.txid == *txid { - log_info!(logger, "Removing onchain event with txid {}", txid); - false - } else { true }); + let mut removed_height = None; + for entry in self.onchain_events_awaiting_threshold_conf.iter() { + if entry.txid == *txid { + removed_height = Some(entry.height); + break; + } + } + + if let Some(removed_height) = removed_height { + log_info!(logger, "transaction_unconfirmed of txid {} implies height {} was reorg'd out", txid, removed_height); + self.onchain_events_awaiting_threshold_conf.retain(|ref entry| if entry.height >= removed_height { + log_info!(logger, "Transaction {} reorg'd out", entry.txid); + false + } else { true }); + } + + debug_assert!(!self.onchain_events_awaiting_threshold_conf.iter().any(|ref entry| entry.txid == *txid)); + self.onchain_tx_handler.transaction_unconfirmed(txid, broadcaster, fee_estimator, logger); } @@ -3551,8 +3604,8 @@ where const MAX_ALLOC_SIZE: usize = 64*1024; -impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> - for (BlockHash, ChannelMonitor) { +impl<'a, K: KeysInterface> ReadableArgs<&'a K> + for (BlockHash, ChannelMonitor) { fn read(reader: &mut R, keys_manager: &'a K) -> Result { macro_rules! unwrap_obj { ($key: expr) => { @@ -3736,7 +3789,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> return Err(DecodeError::InvalidValue); } } - let onchain_tx_handler: OnchainTxHandler = 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)?; @@ -3763,6 +3816,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> let mut funding_spend_seen = Some(false); let mut counterparty_node_id = None; let mut confirmed_commitment_tx_counterparty_output = None; + let mut spendable_txids_confirmed = Some(Vec::new()); read_tlv_fields!(reader, { (1, funding_spend_confirmed, option), (3, htlcs_resolved_on_chain, vec_type), @@ -3770,6 +3824,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> (7, funding_spend_seen, option), (9, counterparty_node_id, option), (11, confirmed_commitment_tx_counterparty_output, option), + (13, spendable_txids_confirmed, vec_type), }); let mut secp_ctx = Secp256k1::new(); @@ -3822,6 +3877,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> funding_spend_confirmed, confirmed_commitment_tx_counterparty_output, htlcs_resolved_on_chain: htlcs_resolved_on_chain.unwrap(), + spendable_txids_confirmed: spendable_txids_confirmed.unwrap(), best_block, counterparty_node_id, @@ -4030,7 +4086,7 @@ mod tests { SecretKey::from_slice(&[41; 32]).unwrap(), [41; 32], 0, - [0; 32] + [0; 32], ); let counterparty_pubkeys = ChannelPublicKeys { @@ -4051,6 +4107,7 @@ mod tests { }), funding_outpoint: Some(funding_outpoint), opt_anchors: None, + opt_non_zero_fee_anchors: None, }; // Prune with one old state and a holder commitment tx holding a few overlaps with the // old state.