X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fchannelmonitor.rs;h=cc88a0119fc95cf972680e7fcb4e5eb63495130c;hb=de2acc0ee0166e9d6cbd3a89459e08ee4ac6a4d5;hp=c0d0affcce5d2fad616ab1ae48e49feaea2a76d7;hpb=49c9f1885dd7a564c0c78ad5f73ea4792c0171a8;p=rust-lightning diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index c0d0affc..cc88a011 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), @@ -2434,8 +2443,8 @@ impl ChannelMonitorImpl { let secret = self.get_secret(commitment_number).unwrap(); 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_commitment_params.counterparty_delayed_payment_base_key)); + let revocation_pubkey = chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint); + let delayed_key = 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_commitment_params.on_counterparty_tx_csv, &delayed_key); let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh(); @@ -2547,31 +2556,18 @@ impl ChannelMonitorImpl { } else { return (claimable_outpoints, to_counterparty_output_info); }; if let Some(transaction) = tx { - let revokeable_p2wsh_opt = - if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key( - &self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint) - { - if let Ok(delayed_key) = chan_utils::derive_public_key(&self.secp_ctx, - &per_commitment_point, - &self.counterparty_commitment_params.counterparty_delayed_payment_base_key) - { - Some(chan_utils::get_revokeable_redeemscript(&revocation_pubkey, - self.counterparty_commitment_params.on_counterparty_tx_csv, - &delayed_key).to_v0_p2wsh()) - } else { - debug_assert!(false, "Failed to derive a delayed payment key for a commitment state we accepted"); - None - } - } else { - debug_assert!(false, "Failed to derive a revocation pubkey key for a commitment state we accepted"); - None - }; - if let Some(revokeable_p2wsh) = revokeable_p2wsh_opt { - for (idx, outp) in transaction.output.iter().enumerate() { - if outp.script_pubkey == revokeable_p2wsh { - to_counterparty_output_info = - Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value)); - } + let revocation_pubkey = chan_utils::derive_public_revocation_key( + &self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint); + let delayed_key = chan_utils::derive_public_key(&self.secp_ctx, + &per_commitment_point, + &self.counterparty_commitment_params.counterparty_delayed_payment_base_key); + let revokeable_p2wsh = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, + self.counterparty_commitment_params.on_counterparty_tx_csv, + &delayed_key).to_v0_p2wsh(); + for (idx, outp) in transaction.output.iter().enumerate() { + if outp.script_pubkey == revokeable_p2wsh { + to_counterparty_output_info = + Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value)); } } } @@ -2860,7 +2856,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 +2896,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 +2919,6 @@ impl ChannelMonitorImpl { } } } - let txid = tx.txid(); self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { txid, transaction: Some((*tx).clone()), @@ -3042,6 +3067,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 +3137,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); } @@ -3763,6 +3803,7 @@ impl<'a, 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 +3811,7 @@ impl<'a, 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 +3864,7 @@ impl<'a, 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 +4073,7 @@ mod tests { SecretKey::from_slice(&[41; 32]).unwrap(), [41; 32], 0, - [0; 32] + [0; 32], ); let counterparty_pubkeys = ChannelPublicKeys { @@ -4051,6 +4094,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.