diff --git a/crates/chain/src/canonical.rs b/crates/chain/src/canonical.rs index c2aecb756..ae1104c26 100644 --- a/crates/chain/src/canonical.rs +++ b/crates/chain/src/canonical.rs @@ -107,14 +107,12 @@ impl PartialOrd for CanonicalTxOut

{ impl CanonicalTxOut> { /// Whether the `txout` is considered mature. /// - /// Depending on the implementation of [`confirmation_height_upper_bound`] in [`Anchor`], this - /// method may return false-negatives. In other words, interpreted confirmation count may be - /// less than the actual value. - /// - /// [`confirmation_height_upper_bound`]: Anchor::confirmation_height_upper_bound + /// A coinbase output is mature once [`COINBASE_MATURITY`] blocks (including the block that + /// confirmed it) have been mined up to and including `tip`. Non-coinbase outputs are always + /// mature. pub fn is_mature(&self, tip: u32) -> bool { if self.is_on_coinbase { - let conf_height = match self.pos.confirmation_height_upper_bound() { + let conf_height = match self.pos.confirmation_height() { Some(height) => height, None => { debug_assert!(false, "coinbase tx can never be unconfirmed"); @@ -133,18 +131,12 @@ impl CanonicalTxOut> { /// Whether the utxo is/was/will be spendable with chain `tip`. /// /// This method does not take into account the lock time. - /// - /// Depending on the implementation of [`confirmation_height_upper_bound`] in [`Anchor`], this - /// method may return false-negatives. In other words, interpreted confirmation count may be - /// less than the actual value. - /// - /// [`confirmation_height_upper_bound`]: Anchor::confirmation_height_upper_bound pub fn is_confirmed_and_spendable(&self, tip: u32) -> bool { if !self.is_mature(tip) { return false; } - let conf_height = match self.pos.confirmation_height_upper_bound() { + let conf_height = match self.pos.confirmation_height() { Some(height) => height, None => return false, }; @@ -156,7 +148,7 @@ impl CanonicalTxOut> { if let Some(spend_height) = self .spent_by .as_ref() - .and_then(|(pos, _)| pos.confirmation_height_upper_bound()) + .and_then(|(pos, _)| pos.confirmation_height()) { if spend_height <= tip { return false; @@ -449,7 +441,7 @@ impl CanonicalView { for (spk_i, txout) in self.filter_unspent_outpoints(outpoints) { match &txout.pos { ChainPosition::Confirmed { anchor, .. } => { - let confirmation_height = anchor.confirmation_height_upper_bound(); + let confirmation_height = anchor.confirmation_height(); let confirmations = self .tip .height diff --git a/crates/chain/src/canonical_task.rs b/crates/chain/src/canonical_task.rs index a7cb9230a..e36df4d2a 100644 --- a/crates/chain/src/canonical_task.rs +++ b/crates/chain/src/canonical_task.rs @@ -178,7 +178,7 @@ impl<'g, A: Anchor> ChainQuery for CanonicalTask<'g, A> { .expect( "tx taken from `unprocessed_anchored_txs` so it must have at least one anchor", ) - .confirmation_height_upper_bound(), + .confirmation_height(), )) } } diff --git a/crates/chain/src/chain_data.rs b/crates/chain/src/chain_data.rs index b03a96823..ddc5a11ec 100644 --- a/crates/chain/src/chain_data.rs +++ b/crates/chain/src/chain_data.rs @@ -74,12 +74,10 @@ impl ChainPosition<&A> { } impl ChainPosition { - /// Determines the upper bound of the confirmation height. - pub fn confirmation_height_upper_bound(&self) -> Option { + /// Height of the block that confirmed this position, if it is confirmed. + pub fn confirmation_height(&self) -> Option { match self { - ChainPosition::Confirmed { anchor, .. } => { - Some(anchor.confirmation_height_upper_bound()) - } + ChainPosition::Confirmed { anchor, .. } => Some(anchor.confirmation_height()), ChainPosition::Unconfirmed { .. } => None, } } @@ -87,8 +85,8 @@ impl ChainPosition { /// Number of confirmations of this position, given `tip`. /// /// Returns `0` if unconfirmed, or if the confirmation height is above `tip`. - pub fn confirmations_lower_bound(&self, tip: u32) -> u32 { - let Some(height) = self.confirmation_height_upper_bound() else { + pub fn confirmations(&self, tip: u32) -> u32 { + let Some(height) = self.confirmation_height() else { return 0; }; @@ -352,7 +350,7 @@ mod test { } #[test] - fn test_confirmations_lower_bound() { + fn test_confirmations() { let confirmed_at = |height: u32| ChainPosition::Confirmed { anchor: ConfirmationBlockTime { confirmation_time: 0, @@ -364,15 +362,15 @@ mod test { transitively: None, }; - assert_eq!(confirmed_at(100).confirmations_lower_bound(100), 1); - assert_eq!(confirmed_at(99).confirmations_lower_bound(100), 2); - assert_eq!(confirmed_at(90).confirmations_lower_bound(100), 11); - assert_eq!(confirmed_at(101).confirmations_lower_bound(100), 0); + assert_eq!(confirmed_at(100).confirmations(100), 1); + assert_eq!(confirmed_at(99).confirmations(100), 2); + assert_eq!(confirmed_at(90).confirmations(100), 11); + assert_eq!(confirmed_at(101).confirmations(100), 0); let unconfirmed = ChainPosition::::Unconfirmed { first_seen: Some(1), last_seen: Some(2), }; - assert_eq!(unconfirmed.confirmations_lower_bound(100), 0); + assert_eq!(unconfirmed.confirmations(100), 0); } } diff --git a/crates/chain/src/tx_data_traits.rs b/crates/chain/src/tx_data_traits.rs index 7ab8963f4..998937434 100644 --- a/crates/chain/src/tx_data_traits.rs +++ b/crates/chain/src/tx_data_traits.rs @@ -2,10 +2,8 @@ use crate::{BlockId, ConfirmationBlockTime}; /// Trait that "anchors" blockchain data to a specific block of height and hash. /// -/// If transaction A is anchored in block B, and block B is in the best chain, we can -/// assume that transaction A is also confirmed in the best chain. This does not necessarily mean -/// that transaction A is confirmed in block B. It could also mean transaction A is confirmed in a -/// parent block of B. +/// If transaction A is anchored in block B, then block B is the block that confirmed transaction +/// A. If block B is in the best chain, transaction A is confirmed in the best chain. /// /// Every [`Anchor`] implementation must contain a [`BlockId`] parameter, and must implement /// [`Ord`]. When implementing [`Ord`], the anchors' [`BlockId`]s should take precedence @@ -68,11 +66,10 @@ pub trait Anchor: core::fmt::Debug + Clone + Eq + PartialOrd + Ord + core::hash: /// Returns the [`BlockId`] that the associated blockchain data is "anchored" in. fn anchor_block(&self) -> BlockId; - /// Get the upper bound of the chain data's confirmation height. + /// Get the height of the block that confirmed the associated chain data. /// - /// The default definition gives a pessimistic answer. This can be overridden by the `Anchor` - /// implementation for a more accurate value. - fn confirmation_height_upper_bound(&self) -> u32 { + /// This is the height of [`anchor_block`](Anchor::anchor_block). + fn confirmation_height(&self) -> u32 { self.anchor_block().height } } @@ -82,8 +79,8 @@ impl Anchor for &A { ::anchor_block(self) } - fn confirmation_height_upper_bound(&self) -> u32 { - ::confirmation_height_upper_bound(self) + fn confirmation_height(&self) -> u32 { + ::confirmation_height(self) } } @@ -97,10 +94,6 @@ impl Anchor for ConfirmationBlockTime { fn anchor_block(&self) -> BlockId { self.block_id } - - fn confirmation_height_upper_bound(&self) -> u32 { - self.block_id.height - } } /// Set of parameters sufficient to construct an [`Anchor`]. diff --git a/crates/chain/src/tx_graph.rs b/crates/chain/src/tx_graph.rs index df0fb01d7..44a2f81af 100644 --- a/crates/chain/src/tx_graph.rs +++ b/crates/chain/src/tx_graph.rs @@ -766,15 +766,11 @@ impl TxGraph { // `txs_by_highest_conf_heights`. // We want to remove `(old_top_h?, txid)` and insert `(new_top_h?, txid)`. let mut old_top_h = None; - let mut new_top_h = anchor.confirmation_height_upper_bound(); + let mut new_top_h = anchor.confirmation_height(); let is_changed = match self.anchors.entry(txid) { hash_map::Entry::Occupied(mut e) => { - old_top_h = e - .get() - .iter() - .last() - .map(Anchor::confirmation_height_upper_bound); + old_top_h = e.get().iter().last().map(Anchor::confirmation_height); if let Some(old_top_h) = old_top_h { if old_top_h > new_top_h { new_top_h = old_top_h;