From 72d5fbad737bf02c0bdc843bf4361bfc558c7520 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 5 May 2025 19:58:58 +0000 Subject: [PATCH] units: stop using MtpAndHeight in locktime::relative is_satisfied_by methods Keep using it in primitives; will remove it there in the next commit. --- primitives/src/locktime/relative.rs | 6 +- units/src/locktime/relative.rs | 88 ++++++++++++++++------------- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/primitives/src/locktime/relative.rs b/primitives/src/locktime/relative.rs index 053e883cf..db0a40552 100644 --- a/primitives/src/locktime/relative.rs +++ b/primitives/src/locktime/relative.rs @@ -255,8 +255,10 @@ impl LockTime { /// ``` pub fn is_satisfied_by(self, chain_tip: MtpAndHeight, utxo_mined_at: MtpAndHeight) -> bool { match self { - LockTime::Blocks(blocks) => blocks.is_satisfied_by(chain_tip, utxo_mined_at), - LockTime::Time(time) => time.is_satisfied_by(chain_tip, utxo_mined_at), + LockTime::Blocks(blocks) => + blocks.is_satisfied_by(chain_tip.to_height(), utxo_mined_at.to_height()), + LockTime::Time(time) => + time.is_satisfied_by(chain_tip.to_mtp(), utxo_mined_at.to_mtp()), } } diff --git a/units/src/locktime/relative.rs b/units/src/locktime/relative.rs index a98b11fb7..af3136d09 100644 --- a/units/src/locktime/relative.rs +++ b/units/src/locktime/relative.rs @@ -9,8 +9,6 @@ use arbitrary::{Arbitrary, Unstructured}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::mtp_height::MtpAndHeight; - #[deprecated(since = "TBD", note = "use `HeightIterval` instead")] #[doc(hidden)] pub type Height = HeightInterval; @@ -59,21 +57,26 @@ impl HeightInterval { /// Determines whether a relative‐height locktime has matured, taking into account /// both the chain tip and the height at which the UTXO was confirmed. /// + /// If you have two height intervals `x` and `y`, and want to know whether `x` + /// is satisfied by `y`, use `x >= y`. + /// /// # Parameters - /// - `self` – The relative block‐height delay (`h`) required after confirmation. - /// - `chain_tip` – The current chain state (contains the tip height). - /// - `utxo_mined_at` – The chain state at the UTXO’s confirmation block (contains that height). + /// - `self` – the relative block‐height delay (`h`) required after confirmation. + /// - `chain_tip` – the height of the current chain tip + /// - `utxo_mined_at` – the height of the UTXO’s confirmation block /// /// # Returns /// - `true` if a UTXO locked by `self` can be spent in a block after `chain_tip`. /// - `false` if the UTXO is still locked at `chain_tip`. - pub fn is_satisfied_by(self, chain_tip: MtpAndHeight, utxo_mined_at: MtpAndHeight) -> bool { - // let chain_tip_height = BlockHeight::from(chain_tip); - // let utxo_mined_at_height = BlockHeight::from(utxo_mined_at); - match u32::from(self.to_height()).checked_add(utxo_mined_at.to_height().to_u32()) { - Some(target_height) => chain_tip.to_height().to_u32() >= target_height, - None => false, - } + pub fn is_satisfied_by( + self, + chain_tip: crate::BlockHeight, + utxo_mined_at: crate::BlockHeight, + ) -> bool { + chain_tip + .checked_sub(utxo_mined_at) + .and_then(|diff: crate::BlockHeightInterval| diff.try_into().ok()) + .map_or(false, |diff: Self| diff >= self) } } @@ -180,22 +183,26 @@ impl MtpInterval { /// Determines whether a relative‑time lock has matured, taking into account both /// the UTXO’s Median Time Past at confirmation and the required delay. /// + /// If you have two MTP intervals `x` and `y`, and want to know whether `x` + /// is satisfied by `y`, use `x >= y`. + /// /// # Parameters - /// - `self` – The relative time delay (`t`) in 512‑second intervals. - /// - `chain_tip` – The current chain state, providing the tip’s MTP. - /// - `utxo_mined_at` – The chain state at the UTXO’s confirmation, providing its MTP. + /// - `self` – the relative time delay (`t`) in 512‑second intervals. + /// - `chain_tip` – the MTP of the current chain tip + /// - `utxo_mined_at` – the MTP of the UTXO’s confirmation block /// /// # Returns /// - `true` if the relative‐time lock has expired by the tip’s MTP /// - `false` if the lock has not yet expired by the tip’s MTP - pub fn is_satisfied_by(self, chain_tip: MtpAndHeight, utxo_mined_at: MtpAndHeight) -> bool { - match u32::from(self.to_512_second_intervals()).checked_mul(512) { - Some(seconds) => match seconds.checked_add(utxo_mined_at.to_mtp().to_u32()) { - Some(required_seconds) => chain_tip.to_mtp().to_u32() >= required_seconds, - None => false, - }, - None => false, - } + pub fn is_satisfied_by( + self, + chain_tip: crate::BlockMtp, + utxo_mined_at: crate::BlockMtp, + ) -> bool { + chain_tip + .checked_sub(utxo_mined_at) + .and_then(|diff: crate::BlockMtpInterval| diff.to_relative_mtp_interval_floor().ok()) + .map_or(false, |diff: Self| diff >= self) } } @@ -270,7 +277,7 @@ mod tests { use internals::serde_round_trip; use super::*; - use crate::{BlockHeight, BlockTime}; + use crate::BlockTime; const MAXIMUM_ENCODABLE_SECONDS: u32 = u16::MAX as u32 * 512; @@ -355,6 +362,8 @@ mod tests { #[test] fn test_time_chain_state() { + use crate::BlockMtp; + let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200); let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200); @@ -367,49 +376,48 @@ mod tests { // Test case 1: Satisfaction (current_mtp >= utxo_mtp + required_seconds) // 10 intervals × 512 seconds = 5120 seconds let time_lock = MtpInterval::from_512_second_intervals(10); - let chain_state1 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps); - let utxo_state1 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps); + let chain_state1 = BlockMtp::new(timestamps); + let utxo_state1 = BlockMtp::new(utxo_timestamps); assert!(time_lock.is_satisfied_by(chain_state1, utxo_state1)); // Test case 2: Not satisfied (current_mtp < utxo_mtp + required_seconds) - let chain_state2 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps2); - let utxo_state2 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps2); + let chain_state2 = BlockMtp::new(timestamps2); + let utxo_state2 = BlockMtp::new(utxo_timestamps2); assert!(!time_lock.is_satisfied_by(chain_state2, utxo_state2)); // Test case 3: Test with a larger value (100 intervals = 51200 seconds) let larger_lock = MtpInterval::from_512_second_intervals(100); - let chain_state3 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps3); - let utxo_state3 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps3); + let chain_state3 = BlockMtp::new(timestamps3); + let utxo_state3 = BlockMtp::new(utxo_timestamps3); assert!(larger_lock.is_satisfied_by(chain_state3, utxo_state3)); // Test case 4: Overflow handling - tests that is_satisfied_by handles overflow gracefully let max_time_lock = MtpInterval::MAX; - let chain_state4 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps); - let utxo_state4 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps); + let chain_state4 = BlockMtp::new(timestamps); + let utxo_state4 = BlockMtp::new(utxo_timestamps); assert!(!max_time_lock.is_satisfied_by(chain_state4, utxo_state4)); } #[test] fn test_height_chain_state() { - let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200); - let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200); + use crate::BlockHeight; let height_lock = HeightInterval(10); // Test case 1: Satisfaction (current_height >= utxo_height + required) - let chain_state1 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps); - let utxo_state1 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps); + let chain_state1 = BlockHeight::from_u32(100); + let utxo_state1 = BlockHeight::from_u32(80); assert!(height_lock.is_satisfied_by(chain_state1, utxo_state1)); // Test case 2: Not satisfied (current_height < utxo_height + required) - let chain_state2 = MtpAndHeight::new(BlockHeight::from_u32(89), timestamps); - let utxo_state2 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps); + let chain_state2 = BlockHeight::from_u32(89); + let utxo_state2 = BlockHeight::from_u32(80); assert!(!height_lock.is_satisfied_by(chain_state2, utxo_state2)); // Test case 3: Overflow handling - tests that is_satisfied_by handles overflow gracefully let max_height_lock = HeightInterval::MAX; - let chain_state3 = MtpAndHeight::new(BlockHeight::from_u32(1000), timestamps); - let utxo_state3 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps); + let chain_state3 = BlockHeight::from_u32(1000); + let utxo_state3 = BlockHeight::from_u32(80); assert!(!max_height_lock.is_satisfied_by(chain_state3, utxo_state3)); } }