X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fonchaintx.rs;h=2ce2ed41ba1fcf0e00325aec5005e8757d10c76b;hb=384c4dc7753e4b7ac53ea380e52809babd8f0f9b;hp=18679f0aacd5cd34fead7952fc91be997ad4e57b;hpb=f4f1093edc506314484c8d52a40dd155e692394b;p=rust-lightning diff --git a/lightning/src/chain/onchaintx.rs b/lightning/src/chain/onchaintx.rs index 18679f0a..2ce2ed41 100644 --- a/lightning/src/chain/onchaintx.rs +++ b/lightning/src/chain/onchaintx.rs @@ -16,7 +16,7 @@ use bitcoin::blockdata::transaction::Transaction; use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint; use bitcoin::blockdata::script::Script; -use bitcoin::hash_types::Txid; +use bitcoin::hash_types::{Txid, BlockHash}; use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature}; use bitcoin::secp256k1; @@ -58,6 +58,7 @@ const MAX_ALLOC_SIZE: usize = 64*1024; struct OnchainEventEntry { txid: Txid, height: u32, + block_hash: Option, // Added as optional, will be filled in for any entry generated on 0.0.113 or after event: OnchainEvent, } @@ -92,6 +93,7 @@ impl Writeable for OnchainEventEntry { fn write(&self, writer: &mut W) -> Result<(), io::Error> { write_tlv_fields!(writer, { (0, self.txid, required), + (1, self.block_hash, option), (2, self.height, required), (4, self.event, required), }); @@ -103,14 +105,16 @@ impl MaybeReadable for OnchainEventEntry { fn read(reader: &mut R) -> Result, DecodeError> { let mut txid = Txid::all_zeros(); let mut height = 0; + let mut block_hash = None; let mut event = None; read_tlv_fields!(reader, { (0, txid, required), + (1, block_hash, option), (2, height, required), (4, event, ignorable), }); if let Some(ev) = event { - Ok(Some(Self { txid, height, event: ev })) + Ok(Some(Self { txid, height, block_hash, event: ev })) } else { Ok(None) } @@ -430,7 +434,43 @@ impl OnchainTxHandler { where F::Target: FeeEstimator, L::Target: Logger, { - if cached_request.outpoints().len() == 0 { return None } // But don't prune pending claiming request yet, we may have to resurrect HTLCs + let request_outpoints = cached_request.outpoints(); + if request_outpoints.is_empty() { + // Don't prune pending claiming request yet, we may have to resurrect HTLCs. Untractable + // packages cannot be aggregated and will never be split, so we cannot end up with an + // empty claim. + debug_assert!(cached_request.is_malleable()); + return None; + } + // If we've seen transaction inclusion in the chain for all outpoints in our request, we + // don't need to continue generating more claims. We'll keep tracking the request to fully + // remove it once it reaches the confirmation threshold, or to generate a new claim if the + // transaction is reorged out. + let mut all_inputs_have_confirmed_spend = true; + for outpoint in &request_outpoints { + if let Some(first_claim_txid_height) = self.claimable_outpoints.get(outpoint) { + // We check for outpoint spends within claims individually rather than as a set + // since requests can have outpoints split off. + if !self.onchain_events_awaiting_threshold_conf.iter() + .any(|event_entry| if let OnchainEvent::Claim { claim_request } = event_entry.event { + first_claim_txid_height.0 == claim_request + } else { + // The onchain event is not a claim, keep seeking until we find one. + false + }) + { + // Either we had no `OnchainEvent::Claim`, or we did but none matched the + // outpoint's registered spend. + all_inputs_have_confirmed_spend = false; + } + } else { + // The request's outpoint spend does not exist yet. + all_inputs_have_confirmed_spend = false; + } + } + if all_inputs_have_confirmed_spend { + return None; + } // Compute new height timer to decide when we need to regenerate a new bumped version of the claim tx (if we // didn't receive confirmation of it before, or not enough reorg-safe depth on top of it). @@ -507,17 +547,22 @@ impl OnchainTxHandler { /// Upon channelmonitor.block_connected(..) or upon provision of a preimage on the forward link /// for this channel, provide new relevant on-chain transactions and/or new claim requests. - /// Formerly this was named `block_connected`, but it is now also used for claiming an HTLC output - /// if we receive a preimage after force-close. - /// `conf_height` represents the height at which the transactions in `txn_matched` were - /// confirmed. This does not need to equal the current blockchain tip height, which should be - /// provided via `cur_height`, however it must never be higher than `cur_height`. - pub(crate) fn update_claims_view(&mut self, txn_matched: &[&Transaction], requests: Vec, conf_height: u32, cur_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator, logger: &L) - where B::Target: BroadcasterInterface, - F::Target: FeeEstimator, - L::Target: Logger, + /// Together with `update_claims_view_from_matched_txn` this used to be named + /// `block_connected`, but it is now also used for claiming an HTLC output if we receive a + /// preimage after force-close. + /// + /// `conf_height` represents the height at which the request was generated. This + /// does not need to equal the current blockchain tip height, which should be provided via + /// `cur_height`, however it must never be higher than `cur_height`. + pub(crate) fn update_claims_view_from_requests( + &mut self, requests: Vec, conf_height: u32, cur_height: u32, + broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator, logger: &L + ) where + B::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, { - log_debug!(logger, "Updating claims view at height {} with {} matched transactions in block {} and {} claim requests", cur_height, txn_matched.len(), conf_height, requests.len()); + log_debug!(logger, "Updating claims view at height {} with {} claim requests", cur_height, requests.len()); let mut preprocessed_requests = Vec::with_capacity(requests.len()); let mut aggregated_request = None; @@ -597,7 +642,25 @@ impl OnchainTxHandler { self.pending_claim_requests.insert(txid, req); } } + } + /// Upon channelmonitor.block_connected(..) or upon provision of a preimage on the forward link + /// for this channel, provide new relevant on-chain transactions and/or new claim requests. + /// Together with `update_claims_view_from_requests` this used to be named `block_connected`, + /// but it is now also used for claiming an HTLC output if we receive a preimage after force-close. + /// + /// `conf_height` represents the height at which the transactions in `txn_matched` were + /// confirmed. This does not need to equal the current blockchain tip height, which should be + /// provided via `cur_height`, however it must never be higher than `cur_height`. + pub(crate) fn update_claims_view_from_matched_txn( + &mut self, txn_matched: &[&Transaction], conf_height: u32, conf_hash: BlockHash, + cur_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator, logger: &L + ) where + B::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + { + log_debug!(logger, "Updating claims view at height {} with {} matched transactions in block {}", cur_height, txn_matched.len(), conf_height); let mut bump_candidates = HashMap::new(); for tx in txn_matched { // Scan all input to verify is one of the outpoint spent is of interest for us @@ -625,6 +688,7 @@ impl OnchainTxHandler { let entry = OnchainEventEntry { txid: tx.txid(), height: conf_height, + block_hash: Some(conf_hash), event: OnchainEvent::Claim { claim_request: first_claim_txid_height.0.clone() } }; if !self.onchain_events_awaiting_threshold_conf.contains(&entry) { @@ -665,6 +729,7 @@ impl OnchainTxHandler { let entry = OnchainEventEntry { txid: tx.txid(), height: conf_height, + block_hash: Some(conf_hash), event: OnchainEvent::ContentiousOutpoint { package }, }; if !self.onchain_events_awaiting_threshold_conf.contains(&entry) { @@ -824,12 +889,12 @@ impl OnchainTxHandler { self.claimable_outpoints.get(outpoint).is_some() } - pub(crate) fn get_relevant_txids(&self) -> Vec { - let mut txids: Vec = self.onchain_events_awaiting_threshold_conf + pub(crate) fn get_relevant_txids(&self) -> Vec<(Txid, Option)> { + let mut txids: Vec<(Txid, Option)> = self.onchain_events_awaiting_threshold_conf .iter() - .map(|entry| entry.txid) + .map(|entry| (entry.txid, entry.block_hash)) .collect(); - txids.sort_unstable(); + txids.sort_unstable_by_key(|(txid, _)| *txid); txids.dedup(); txids }