From defff8ee22360358a8f6f9b0ed3e7c0c49508c54 Mon Sep 17 00:00:00 2001 From: Dmenec Date: Thu, 20 Aug 2026 12:37:29 +0200 Subject: [PATCH] feat(chain): add ChainPosition::blocks_since_conf --- crates/chain/src/chain_data.rs | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crates/chain/src/chain_data.rs b/crates/chain/src/chain_data.rs index 7ec88fba8f..2370066876 100644 --- a/crates/chain/src/chain_data.rs +++ b/crates/chain/src/chain_data.rs @@ -83,6 +83,19 @@ impl ChainPosition { ChainPosition::Unconfirmed { .. } => None, } } + + /// Number of blocks mined on top of this position's confirmation block, given `tip`. + /// + /// Returns `None` if unconfirmed, or if the confirmation height is above `tip`. + pub fn blocks_since_conf(&self, tip: u32) -> Option { + let height = self.confirmation_height_upper_bound()?; + + if height > tip { + return None; + } + + Some(tip - height) + } } /// Ordering for `ChainPosition`: @@ -335,4 +348,29 @@ mod test { ] ); } + + #[test] + fn test_blocks_since_conf() { + let confirmed_at = |height: u32| ChainPosition::Confirmed { + anchor: ConfirmationBlockTime { + confirmation_time: 0, + block_id: BlockId { + height, + ..Default::default() + }, + }, + transitively: None, + }; + + assert_eq!(confirmed_at(100).blocks_since_conf(100), Some(0)); + assert_eq!(confirmed_at(99).blocks_since_conf(100), Some(1)); + assert_eq!(confirmed_at(90).blocks_since_conf(100), Some(10)); + assert_eq!(confirmed_at(101).blocks_since_conf(100), None); + + let unconfirmed = ChainPosition::::Unconfirmed { + first_seen: Some(1), + last_seen: Some(2), + }; + assert_eq!(unconfirmed.blocks_since_conf(100), None); + } }