units: stop using MtpAndHeight in locktime::relative is_satisfied_by methods

Keep using it in primitives; will remove it there in the next commit.
This commit is contained in:
Andrew Poelstra 2025-05-05 19:58:58 +00:00
parent d933c754f5
commit 72d5fbad73
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
2 changed files with 52 additions and 42 deletions

View File

@ -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()),
}
}

View File

@ -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 relativeheight 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 blockheight delay (`h`) required after confirmation.
/// - `chain_tip` The current chain state (contains the tip height).
/// - `utxo_mined_at` The chain state at the UTXOs confirmation block (contains that height).
/// - `self` the relative blockheight delay (`h`) required after confirmation.
/// - `chain_tip` the height of the current chain tip
/// - `utxo_mined_at` the height of the UTXOs 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 relativetime lock has matured, taking into account both
/// the UTXOs 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 512second intervals.
/// - `chain_tip` The current chain state, providing the tips MTP.
/// - `utxo_mined_at` The chain state at the UTXOs confirmation, providing its MTP.
/// - `self` the relative time delay (`t`) in 512second intervals.
/// - `chain_tip` the MTP of the current chain tip
/// - `utxo_mined_at` the MTP of the UTXOs confirmation block
///
/// # Returns
/// - `true` if the relativetime lock has expired by the tips MTP
/// - `false` if the lock has not yet expired by the tips 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));
}
}