Skip to main content

bitcoin_units/locktime/relative/
mod.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Provides type [`LockTime`] that implements the logic around `nSequence`/`OP_CHECKSEQUENCEVERIFY`.
4//!
5//! There are two types of lock time: lock-by-block-count and lock-by-time, distinguished by whether
6//! bit 22 of the `u32` consensus value is set. To support these we provide the [`NumberOfBlocks`]
7//! and [`NumberOf512Seconds`] types.
8//!
9//! Note: bitcoin-units uses "block count" rather than "block height" because a relative lock is an
10//! age, a number of blocks elapsed since the UTXO was mined, not a position in the chain.
11
12pub mod error;
13
14use core::{convert, fmt};
15
16#[cfg(feature = "arbitrary")]
17use arbitrary::{Arbitrary, Unstructured};
18use internals::const_casts;
19#[cfg(feature = "serde")]
20use serde::{Deserialize, Deserializer, Serialize, Serializer};
21
22use crate::parse_int::{self, PrefixedHexError, UnprefixedHexError};
23#[cfg(doc)]
24use crate::relative;
25use crate::{BlockHeight, BlockMtp, Sequence};
26
27#[rustfmt::skip]                // Keep public re-exports separate.
28#[doc(no_inline)]
29pub use self::error::{
30    DisabledLockTimeError, IncompatibleHeightError, IncompatibleTimeError, InvalidHeightError,
31    InvalidTimeError, IsSatisfiedByError, IsSatisfiedByHeightError, IsSatisfiedByTimeError,
32    TimeOverflowError,
33};
34
35/// A relative lock time value, representing either a count of blocks or time (512 second intervals).
36///
37/// Used for sequence numbers (`nSequence` in Bitcoin Core and `TxIn::sequence`
38/// in `rust-bitcoin`) and also for the argument to opcode `OP_CHECKSEQUENCEVERIFY`.
39///
40/// # Note on ordering
41///
42/// Locktimes may be block- or time-based, and these metrics are incommensurate; there is no total
43/// ordering on locktimes. In order to compare locktimes, instead of using `<` or `>` we provide the
44/// [`LockTime::is_satisfied_by`] API.
45///
46/// # Relevant BIPs
47///
48/// * [BIP-0068 Relative lock-time using consensus-enforced sequence numbers](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki)
49/// * [BIP-0112 CHECKSEQUENCEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki)
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub enum LockTime {
52    /// A lock time measured in number of blocks.
53    Blocks(NumberOfBlocks),
54    /// A 512 second time interval value.
55    Time(NumberOf512Seconds),
56}
57
58impl LockTime {
59    /// A relative locktime of 0 is always valid, and is assumed valid for inputs that
60    /// are not yet confirmed.
61    pub const ZERO: Self = Self::Blocks(NumberOfBlocks::ZERO);
62
63    /// The number of bytes that the locktime contributes to the size of a transaction.
64    pub const SIZE: usize = 4; // Serialized length of a u32.
65
66    /// Constructs a new [`LockTime`] from an `nSequence` value or the argument to
67    /// `OP_CHECKSEQUENCEVERIFY`.
68    ///
69    /// This method will **not** round-trip with [`Self::to_consensus_u32`], because relative
70    /// locktimes only use some bits of the underlying `u32` value and discard the rest. If
71    /// you want to preserve the full value, you should use the [`Sequence`] type instead.
72    ///
73    /// # Errors
74    ///
75    /// If `n`, interpreted as a [`Sequence`] number does not encode a relative lock time.
76    ///
77    /// # Examples
78    ///
79    /// ```rust
80    /// # use bitcoin_units::relative;
81    ///
82    /// // Values with bit 22 set to 0 will be interpreted as block-based lock times.
83    /// let blocks: u32 = 144; // 144 blocks, approx 24h.
84    /// let lock_time = relative::LockTime::from_consensus(blocks)?;
85    /// assert!(lock_time.is_block_count());
86    /// assert_eq!(lock_time.to_consensus_u32(), blocks);
87    ///
88    /// // Values with bit 22 set to 1 will be interpreted as time-based lock times.
89    /// let time: u32 = 168 | (1 << 22) ; // Bit 22 is 1 with time approx 24h.
90    /// let lock_time = relative::LockTime::from_consensus(time)?;
91    /// assert!(lock_time.is_block_time());
92    /// assert_eq!(lock_time.to_consensus_u32(), time);
93    ///
94    /// # Ok::<_, relative::error::DisabledLockTimeError>(())
95    /// ```
96    #[inline]
97    pub fn from_consensus(n: u32) -> Result<Self, DisabledLockTimeError> {
98        let sequence = crate::Sequence::from_consensus(n);
99        sequence.to_relative_lock_time().ok_or(DisabledLockTimeError(n))
100    }
101
102    /// Returns the `u32` value used to encode this locktime in an `nSequence` field or
103    /// argument to `OP_CHECKSEQUENCEVERIFY`.
104    ///
105    /// # Warning
106    ///
107    /// Locktimes are not ordered by the natural ordering on `u32`. If you want to
108    /// compare locktimes, use [`Self::is_implied_by`] or similar methods.
109    #[inline]
110    pub fn to_consensus_u32(self) -> u32 {
111        match self {
112            Self::Blocks(ref h) => u32::from(h.to_count()),
113            Self::Time(ref t) => Sequence::LOCK_TYPE_MASK | u32::from(t.to_512_second_intervals()),
114        }
115    }
116
117    /// Constructs a new [`LockTime`] from the sequence number of a Bitcoin input.
118    ///
119    /// This method will **not** round-trip with [`Self::to_sequence`]. See the
120    /// docs for [`Self::from_consensus`] for more information.
121    ///
122    /// # Errors
123    ///
124    /// If `n` does not encode a relative lock time.
125    ///
126    /// # Examples
127    ///
128    /// ```rust
129    /// # use bitcoin_units::{Sequence, relative};
130    ///
131    /// // Interpret a sequence number from a Bitcoin transaction input as a relative lock time
132    /// let sequence_number = Sequence::from_consensus(144); // 144 blocks, approx 24h.
133    /// let lock_time = relative::LockTime::from_sequence(sequence_number)?;
134    /// assert!(lock_time.is_block_count());
135    ///
136    /// # Ok::<_, relative::error::DisabledLockTimeError>(())
137    /// ```
138    #[inline]
139    pub fn from_sequence(n: Sequence) -> Result<Self, DisabledLockTimeError> {
140        Self::from_consensus(n.to_consensus_u32())
141    }
142
143    /// Encodes the locktime as a sequence number.
144    #[inline]
145    pub fn to_sequence(self) -> Sequence { Sequence::from_consensus(self.to_consensus_u32()) }
146
147    /// Constructs a new [`LockTime`] from `n`, expecting `n` to be a 16-bit count of blocks.
148    #[inline]
149    pub const fn from_block_count(n: u16) -> Self { Self::Blocks(NumberOfBlocks::from_count(n)) }
150
151    /// Constructs a new [`LockTime`] from `n`, expecting `n` to be a count of 512-second
152    /// intervals.
153    ///
154    /// This function is a little awkward to use, and users may wish to instead use
155    /// [`Self::from_seconds_floor`] or [`Self::from_seconds_ceil`].
156    #[inline]
157    pub const fn from_512_second_intervals(intervals: u16) -> Self {
158        Self::Time(NumberOf512Seconds::from_512_second_intervals(intervals))
159    }
160
161    /// Constructs a new [`LockTime`] from seconds, converting the seconds into 512 second interval
162    /// with truncating division.
163    ///
164    /// # Errors
165    ///
166    /// Will return an error if the input cannot be encoded in 16 bits.
167    #[inline]
168    pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
169        match NumberOf512Seconds::from_seconds_floor(seconds) {
170            Ok(time) => Ok(Self::Time(time)),
171            Err(e) => Err(e),
172        }
173    }
174
175    /// Constructs a new [`LockTime`] from seconds, converting the seconds into 512 second interval
176    /// with ceiling division.
177    ///
178    /// # Errors
179    ///
180    /// Will return an error if the input cannot be encoded in 16 bits.
181    #[inline]
182    pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
183        match NumberOf512Seconds::from_seconds_ceil(seconds) {
184            Ok(time) => Ok(Self::Time(time)),
185            Err(e) => Err(e),
186        }
187    }
188
189    /// Returns `true` if both lock times use the same unit i.e., both height based or both time
190    /// based.
191    #[inline]
192    pub const fn is_same_unit(self, other: Self) -> bool {
193        matches!((self, other), (Self::Blocks(_), Self::Blocks(_)) | (Self::Time(_), Self::Time(_)))
194    }
195
196    /// Returns `true` if this lock time value is in units of blocks.
197    #[inline]
198    pub const fn is_block_count(self) -> bool { matches!(self, Self::Blocks(_)) }
199
200    /// Returns `true` if this lock time value is in units of time.
201    #[inline]
202    pub const fn is_block_time(self) -> bool { !self.is_block_count() }
203
204    /// Returns `true` if this [`relative::LockTime`] is satisfied by the given chain state.
205    ///
206    /// If this function returns true then an output with this locktime can be spent in the next
207    /// block.
208    ///
209    /// # Arguments
210    ///
211    /// * `chain_tip_height`: The current height of the blockchain.
212    /// * `chain_tip_mtp`: The current MTP (i.e. median of the 11 latest blocks).
213    /// * `utxo_mined_at_height`: `Some` height if mined, `None` if UTXO may go in the next block.
214    /// * `utxo_mined_at_mtp`: `Some` MTP if mined, `None` if UTXO may go in the next block.
215    ///
216    /// # Errors
217    ///
218    /// If `chain_tip` is not _after_ `utxo_mined_at` i.e., if you get the args mixed up.
219    #[inline]
220    pub fn is_satisfied_by(
221        self,
222        chain_tip_height: BlockHeight,
223        chain_tip_mtp: BlockMtp,
224        utxo_mined_at_height: Option<BlockHeight>,
225        utxo_mined_at_mtp: Option<BlockMtp>,
226    ) -> Result<bool, IsSatisfiedByError> {
227        match self {
228            Self::Blocks(blocks) => blocks
229                .is_satisfied_by(chain_tip_height, utxo_mined_at_height)
230                .map_err(IsSatisfiedByError::Blocks),
231            Self::Time(time) => time
232                .is_satisfied_by(chain_tip_mtp, utxo_mined_at_mtp)
233                .map_err(IsSatisfiedByError::Time),
234        }
235    }
236
237    /// Returns `true` if an output with this locktime can be spent in the next block.
238    ///
239    /// # Arguments
240    ///
241    /// * `chain_tip`: The current height of the blockchain.
242    /// * `utxo_mined_at`: `Some` height if mined, `None` if UTXO may go in the next block.
243    ///
244    /// # Errors
245    ///
246    /// Returns an error if this lock is not lock-by-block-count.
247    #[inline]
248    pub fn is_satisfied_by_height(
249        self,
250        chain_tip: BlockHeight,
251        utxo_mined_at: Option<BlockHeight>,
252    ) -> Result<bool, IsSatisfiedByHeightError> {
253        match self {
254            Self::Blocks(blocks) => blocks
255                .is_satisfied_by(chain_tip, utxo_mined_at)
256                .map_err(IsSatisfiedByHeightError::Satisfaction),
257            Self::Time(time) =>
258                Err(IncompatibleHeightError(time)).map_err(IsSatisfiedByHeightError::Incompatible),
259        }
260    }
261
262    /// Returns `true` if an output with this locktime can be spent in the next block.
263    ///
264    /// # Arguments
265    ///
266    /// * `chain_tip`: The current MTP (i.e. median of the 11 latest blocks).
267    /// * `utxo_mined_at`: `Some` MTP if mined, `None` if UTXO may go in the next block.
268    ///
269    /// # Errors
270    ///
271    /// Returns an error if this lock is not lock-by-time.
272    #[inline]
273    pub fn is_satisfied_by_time(
274        self,
275        chain_tip: BlockMtp,
276        utxo_mined_at: Option<BlockMtp>,
277    ) -> Result<bool, IsSatisfiedByTimeError> {
278        match self {
279            Self::Time(time) => time
280                .is_satisfied_by(chain_tip, utxo_mined_at)
281                .map_err(IsSatisfiedByTimeError::Satisfaction),
282            Self::Blocks(blocks) =>
283                Err(IncompatibleTimeError(blocks)).map_err(IsSatisfiedByTimeError::Incompatible),
284        }
285    }
286
287    /// Returns `true` if satisfaction of `other` lock time implies satisfaction of this
288    /// [`relative::LockTime`].
289    ///
290    /// A lock time can only be satisfied by n blocks being mined or n seconds passing. If you have
291    /// two lock times (same unit) then the larger lock time being satisfied implies (in a
292    /// mathematical sense) the smaller one being satisfied.
293    ///
294    /// This function is useful when checking sequence values against a lock, first one checks the
295    /// sequence represents a relative lock time by converting to [`LockTime`] then use this
296    /// function to see if satisfaction of the newly created lock time would imply satisfaction of
297    /// `self`.
298    ///
299    /// Can also be used to remove the smaller value of two `OP_CHECKSEQUENCEVERIFY` operations
300    /// within one branch of the script.
301    ///
302    /// # Examples
303    ///
304    /// ```rust
305    /// # use bitcoin_units::Sequence;
306    ///
307    /// # let required_height = 100;       // 100 blocks.
308    /// # let lock = Sequence::from_height(required_height).to_relative_lock_time().expect("valid height");
309    /// # let test_sequence = Sequence::from_height(required_height + 10);
310    ///
311    /// let satisfied = match test_sequence.to_relative_lock_time() {
312    ///     None => false, // Handle non-lock-time case.
313    ///     Some(test_lock) => lock.is_implied_by(test_lock),
314    /// };
315    /// assert!(satisfied);
316    /// ```
317    #[inline]
318    pub fn is_implied_by(self, other: Self) -> bool {
319        match (self, other) {
320            (Self::Blocks(this), Self::Blocks(other)) => this <= other,
321            (Self::Time(this), Self::Time(other)) => this <= other,
322            _ => false, // Not the same units.
323        }
324    }
325
326    /// Returns `true` if satisfaction of the sequence number implies satisfaction of this lock
327    /// time.
328    ///
329    /// When deciding whether an instance of `<n> CHECKSEQUENCEVERIFY` will pass, this
330    /// method can be used by parsing `n` as a [`LockTime`] and calling this method
331    /// with the sequence number of the input which spends the script.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// # use bitcoin_units::{Sequence, relative};
337    ///
338    /// let sequence = Sequence::from_consensus(1 << 22 | 168); // Bit 22 is 1 with time approx 24h.
339    /// let lock_time = relative::LockTime::from_sequence(sequence)?;
340    /// let input_sequence = Sequence::from_consensus(1 << 22 | 336); // Approx 48h.
341    /// assert!(lock_time.is_block_time());
342    ///
343    /// assert!(lock_time.is_implied_by_sequence(input_sequence));
344    ///
345    /// # Ok::<_, relative::error::DisabledLockTimeError>(())
346    /// ```
347    #[inline]
348    pub fn is_implied_by_sequence(self, other: Sequence) -> bool {
349        Self::from_sequence(other).is_ok_and(|other| self.is_implied_by(other))
350    }
351}
352
353impl From<NumberOfBlocks> for LockTime {
354    #[inline]
355    fn from(h: NumberOfBlocks) -> Self { Self::Blocks(h) }
356}
357
358impl From<NumberOf512Seconds> for LockTime {
359    #[inline]
360    fn from(t: NumberOf512Seconds) -> Self { Self::Time(t) }
361}
362
363impl fmt::Display for LockTime {
364    #[inline]
365    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
366        if f.alternate() {
367            match *self {
368                Self::Blocks(ref h) => write!(f, "block-count {}", h),
369                Self::Time(ref t) => write!(f, "block-time {} (512 second intervals)", t),
370            }
371        } else {
372            match *self {
373                Self::Blocks(ref h) => fmt::Display::fmt(h, f),
374                Self::Time(ref t) => fmt::Display::fmt(t, f),
375            }
376        }
377    }
378}
379
380impl convert::TryFrom<Sequence> for LockTime {
381    type Error = DisabledLockTimeError;
382    #[inline]
383    fn try_from(seq: Sequence) -> Result<Self, DisabledLockTimeError> { Self::from_sequence(seq) }
384}
385
386impl From<LockTime> for Sequence {
387    #[inline]
388    fn from(lt: LockTime) -> Self { lt.to_sequence() }
389}
390
391#[cfg(feature = "serde")]
392impl serde::Serialize for LockTime {
393    #[inline]
394    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
395    where
396        S: serde::Serializer,
397    {
398        self.to_consensus_u32().serialize(serializer)
399    }
400}
401
402#[cfg(feature = "serde")]
403impl<'de> serde::Deserialize<'de> for LockTime {
404    #[inline]
405    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
406    where
407        D: serde::Deserializer<'de>,
408    {
409        u32::deserialize(deserializer)
410            .and_then(|n| Self::from_consensus(n).map_err(serde::de::Error::custom))
411    }
412}
413
414/// A relative lock time lock-by-block-count value.
415#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
416pub struct NumberOfBlocks(u16);
417
418impl NumberOfBlocks {
419    /// A block count of zero, can be included in any block.
420    pub const ZERO: Self = Self(0);
421
422    /// The minimum block count (0), can be included in any block.
423    pub const MIN: Self = Self::ZERO;
424
425    /// The maximum block count.
426    pub const MAX: Self = Self(u16::MAX);
427
428    /// Constructs a new [`NumberOfBlocks`] using a count of blocks.
429    #[inline]
430    pub const fn from_count(blocks: u16) -> Self { Self(blocks) }
431
432    /// Expresses the [`NumberOfBlocks`] as a count of blocks.
433    #[inline]
434    #[must_use]
435    pub const fn to_count(self) -> u16 { self.0 }
436
437    /// Constructs a new [`NumberOfBlocks`] from a prefixed hex string.
438    ///
439    /// # Errors
440    ///
441    /// If the input string is not a valid hex representation of a block count or it does not
442    /// include the `0x` prefix.
443    #[inline]
444    pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
445        let block_count = parse_int::hex_u16_prefixed(s)?;
446        Ok(Self::from_count(block_count))
447    }
448
449    /// Constructs a new [`NumberOfBlocks`] from an unprefixed hex string.
450    ///
451    /// # Errors
452    ///
453    /// If the input string is not a valid hex representation of a block count or if it includes
454    /// the `0x` prefix.
455    #[inline]
456    pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
457        let block_count = parse_int::hex_u16_unprefixed(s)?;
458        Ok(Self::from_count(block_count))
459    }
460
461    /// Returns `true` if an output locked by a block count can be spent in the next block.
462    ///
463    /// `chain_tip` can be any height from the block prior to `utxo_mined_at` onwards i.e., we
464    /// support a pair of parent/child transactions going into the mempool at the same time.
465    ///
466    /// # Arguments
467    ///
468    /// * `chain_tip`: The current height of the blockchain.
469    /// * `utxo_mined_at`: `Some` height if mined, `None` if UTXO may go in the next block.
470    ///
471    /// # Errors
472    ///
473    /// If `chain_tip` is not valid for `utxo_mined_at` i.e., if you get the args mixed up.
474    #[inline]
475    pub fn is_satisfied_by(
476        self,
477        chain_tip: crate::BlockHeight,
478        utxo_mined_at: Option<crate::BlockHeight>,
479    ) -> Result<bool, InvalidHeightError> {
480        utxo_mined_at.map_or(
481            // We want 0-value relative timelocks to be able to go into transactions in the
482            // mempool if their parent is also in the mempool.
483            Ok(self == Self::ZERO),
484            |mined_at| {
485                chain_tip
486                    .checked_sub(mined_at)
487                    .ok_or(InvalidHeightError { chain_tip, utxo_mined_at: mined_at })
488                    .map(|diff| u32::from(self.to_count()).saturating_sub(1) <= diff.to_u32())
489            },
490        )
491    }
492}
493
494crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(NumberOfBlocks);
495
496parse_int::impl_parse_str_from_int_infallible!(NumberOfBlocks, u16, from_count);
497
498impl fmt::Display for NumberOfBlocks {
499    #[inline]
500    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
501}
502#[cfg(feature = "serde")]
503impl Serialize for NumberOfBlocks {
504    #[inline]
505    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
506    where
507        S: Serializer,
508    {
509        u16::serialize(&self.to_count(), s)
510    }
511}
512
513#[cfg(feature = "serde")]
514impl<'de> Deserialize<'de> for NumberOfBlocks {
515    #[inline]
516    fn deserialize<D>(d: D) -> Result<Self, D::Error>
517    where
518        D: Deserializer<'de>,
519    {
520        Ok(Self::from_count(u16::deserialize(d)?))
521    }
522}
523
524/// A relative lock time lock-by-time value.
525///
526/// For BIP-0068 relative lock-by-time locks, time is measured in 512 second intervals.
527#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
528pub struct NumberOf512Seconds(u16);
529
530impl NumberOf512Seconds {
531    /// Relative block time 0, can be included in any block.
532    pub const ZERO: Self = Self(0);
533
534    /// The minimum relative block time (0), can be included in any block.
535    pub const MIN: Self = Self::ZERO;
536
537    /// The maximum relative block time (33,553,920 seconds or approx 388 days).
538    pub const MAX: Self = Self(u16::MAX);
539
540    /// Constructs a new [`NumberOf512Seconds`] using time intervals where each interval is
541    /// equivalent to 512 seconds.
542    ///
543    /// Encoding finer granularity of time for relative lock-times is not supported in Bitcoin.
544    #[inline]
545    pub const fn from_512_second_intervals(intervals: u16) -> Self { Self(intervals) }
546
547    /// Expresses the [`NumberOf512Seconds`] as an integer number of 512-second intervals.
548    #[inline]
549    #[must_use]
550    pub const fn to_512_second_intervals(self) -> u16 { self.0 }
551
552    /// Constructs a new [`NumberOf512Seconds`] from seconds, converting the seconds into a 512
553    /// second interval using truncating division.
554    ///
555    /// # Errors
556    ///
557    /// Will return an error if the input cannot be encoded in 16 bits.
558    #[inline]
559    #[rustfmt::skip] // moves comments to unrelated code
560    pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
561        let interval = seconds / 512;
562        if interval <= u16::MAX as u32 { // infallible cast, needed by const code
563            Ok(Self::from_512_second_intervals(interval as u16)) // Cast checked above, needed by const code.
564        } else {
565            Err(TimeOverflowError { seconds })
566        }
567    }
568
569    /// Constructs a new [`NumberOf512Seconds`] from seconds, converting the seconds into a 512
570    /// second interval using ceiling division.
571    ///
572    /// # Errors
573    ///
574    /// Will return an error if the input cannot be encoded in 16 bits.
575    #[inline]
576    #[rustfmt::skip] // moves comments to unrelated code
577    pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
578        if seconds <= u16::MAX as u32 * 512 {
579            let interval = seconds.div_ceil(512);
580            Ok(Self::from_512_second_intervals(interval as u16)) // Cast checked above, needed by const code.
581        } else {
582            Err(TimeOverflowError { seconds })
583        }
584    }
585
586    /// Represents the [`NumberOf512Seconds`] as an integer number of seconds.
587    #[inline]
588    pub const fn to_seconds(self) -> u32 { const_casts::u16_to_u32(self.0) * 512 }
589
590    /// Constructs a new [`NumberOf512Seconds`] from a prefixed hex string.
591    ///
592    /// # Errors
593    ///
594    /// If the input string is not a valid hex representation of a number of 512 second intervals
595    /// or it does not include the `0x` prefix.
596    #[inline]
597    pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
598        let block_count = parse_int::hex_u16_prefixed(s)?;
599        Ok(Self::from_512_second_intervals(block_count))
600    }
601
602    /// Constructs a new [`NumberOf512Seconds`] from an unprefixed hex string.
603    ///
604    /// # Errors
605    ///
606    /// If the input string is not a valid hex representation of a number of 512 second intervals
607    /// or if it includes the `0x` prefix.
608    #[inline]
609    pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
610        let block_count = parse_int::hex_u16_unprefixed(s)?;
611        Ok(Self::from_512_second_intervals(block_count))
612    }
613
614    /// Returns `true` if an output locked by time can be spent in the next block.
615    ///
616    /// # Arguments
617    ///
618    /// * `chain_tip_mtp`: The current MTP (i.e. median of the 11 latest blocks).
619    /// * `utxo_mined_at`: `Some` MTP if mined, `None` if UTXO may go in the next block.
620    ///
621    /// # Errors
622    ///
623    /// If `chain_tip` is not _after_ `utxo_mined_at` i.e., if you get the args mixed up.
624    #[inline]
625    pub fn is_satisfied_by(
626        self,
627        chain_tip: crate::BlockMtp,
628        utxo_mined_at: Option<crate::BlockMtp>,
629    ) -> Result<bool, InvalidTimeError> {
630        utxo_mined_at.map_or(
631            // We want 0-value relative timelocks to be able to go into transactions in the
632            // mempool if their parent is also in the mempool.
633            Ok(self == Self::ZERO),
634            |mined_at| {
635                chain_tip
636                    .checked_sub(mined_at)
637                    .ok_or(InvalidTimeError { chain_tip, utxo_mined_at: mined_at })
638                    .map(|diff| self.to_seconds() <= diff.to_u32())
639            },
640        )
641    }
642}
643
644crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(NumberOf512Seconds);
645
646parse_int::impl_parse_str_from_int_infallible!(NumberOf512Seconds, u16, from_512_second_intervals);
647
648impl fmt::Display for NumberOf512Seconds {
649    #[inline]
650    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
651}
652
653#[cfg(feature = "serde")]
654impl Serialize for NumberOf512Seconds {
655    #[inline]
656    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
657    where
658        S: Serializer,
659    {
660        u16::serialize(&self.to_512_second_intervals(), s)
661    }
662}
663
664#[cfg(feature = "serde")]
665impl<'de> Deserialize<'de> for NumberOf512Seconds {
666    #[inline]
667    fn deserialize<D>(d: D) -> Result<Self, D::Error>
668    where
669        D: Deserializer<'de>,
670    {
671        Ok(Self::from_512_second_intervals(u16::deserialize(d)?))
672    }
673}
674
675#[cfg(feature = "arbitrary")]
676impl<'a> Arbitrary<'a> for LockTime {
677    #[inline]
678    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
679        let choice = u.int_in_range(0..=1)?;
680
681        match choice {
682            0 => Ok(Self::Blocks(NumberOfBlocks::arbitrary(u)?)),
683            _ => Ok(Self::Time(NumberOf512Seconds::arbitrary(u)?)),
684        }
685    }
686}
687
688#[cfg(feature = "arbitrary")]
689impl<'a> Arbitrary<'a> for NumberOfBlocks {
690    #[inline]
691    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
692        let choice = u.int_in_range(0..=2)?;
693
694        match choice {
695            0 => Ok(Self::MIN),
696            1 => Ok(Self::MAX),
697            _ => Ok(Self::from_count(u16::arbitrary(u)?)),
698        }
699    }
700}
701
702#[cfg(feature = "arbitrary")]
703impl<'a> Arbitrary<'a> for NumberOf512Seconds {
704    #[inline]
705    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
706        let choice = u.int_in_range(0..=2)?;
707
708        match choice {
709            0 => Ok(Self::MIN),
710            1 => Ok(Self::MAX),
711            _ => Ok(Self::from_512_second_intervals(u16::arbitrary(u)?)),
712        }
713    }
714}
715
716#[cfg(test)]
717mod tests {
718    #[cfg(feature = "alloc")]
719    use alloc::format;
720
721    use super::*;
722    use crate::{BlockHeight, BlockTime};
723
724    const MAXIMUM_ENCODABLE_SECONDS: u32 = u16::MAX as u32 * 512;
725
726    #[test]
727    #[cfg(feature = "alloc")]
728    fn display_and_alternate() {
729        let lock_by_height = LockTime::from_block_count(10);
730        let lock_by_time = LockTime::from_512_second_intervals(70);
731
732        assert_eq!(format!("{}", lock_by_height), "10");
733        assert_eq!(format!("{:#}", lock_by_height), "block-count 10");
734        assert!(!format!("{:?}", lock_by_height).is_empty());
735
736        assert_eq!(format!("{}", lock_by_time), "70");
737        assert_eq!(format!("{:#}", lock_by_time), "block-time 70 (512 second intervals)");
738        assert!(!format!("{:?}", lock_by_time).is_empty());
739    }
740
741    #[test]
742    fn from_seconds_ceil_and_floor() {
743        let time = 70 * 512 + 1;
744        let lock_by_time = LockTime::from_seconds_ceil(time).unwrap();
745        assert_eq!(lock_by_time, LockTime::from_512_second_intervals(71));
746
747        let lock_by_time = LockTime::from_seconds_floor(time).unwrap();
748        assert_eq!(lock_by_time, LockTime::from_512_second_intervals(70));
749
750        let mut max_time = 0xffff * 512;
751        assert_eq!(LockTime::from_seconds_ceil(max_time), LockTime::from_seconds_floor(max_time));
752        max_time += 512;
753        assert!(LockTime::from_seconds_ceil(max_time).is_err());
754        assert!(LockTime::from_seconds_floor(max_time).is_err());
755    }
756
757    #[test]
758    fn parses_correctly_to_height_or_time() {
759        let height1 = NumberOfBlocks::from_count(10);
760        let height2 = NumberOfBlocks::from_count(11);
761        let time1 = NumberOf512Seconds::from_512_second_intervals(70);
762        let time2 = NumberOf512Seconds::from_512_second_intervals(71);
763
764        let lock_by_height1 = LockTime::from(height1);
765        let lock_by_height2 = LockTime::from(height2);
766        let lock_by_time1 = LockTime::from(time1);
767        let lock_by_time2 = LockTime::from(time2);
768
769        assert!(lock_by_height1.is_block_count());
770        assert!(!lock_by_height1.is_block_time());
771
772        assert!(!lock_by_time1.is_block_count());
773        assert!(lock_by_time1.is_block_time());
774
775        // Test is_same_unit() logic
776        assert!(lock_by_height1.is_same_unit(lock_by_height2));
777        assert!(!lock_by_height1.is_same_unit(lock_by_time1));
778        assert!(lock_by_time1.is_same_unit(lock_by_time2));
779        assert!(!lock_by_time1.is_same_unit(lock_by_height1));
780    }
781
782    #[test]
783    fn height_correctly_implies() {
784        let height = NumberOfBlocks::from_count(10);
785        let lock_by_height = LockTime::from(height);
786
787        assert!(!lock_by_height.is_implied_by(LockTime::from(NumberOfBlocks::from_count(9))));
788        assert!(lock_by_height.is_implied_by(LockTime::from(NumberOfBlocks::from_count(10))));
789        assert!(lock_by_height.is_implied_by(LockTime::from(NumberOfBlocks::from_count(11))));
790    }
791
792    #[test]
793    fn time_correctly_implies() {
794        let time = NumberOf512Seconds::from_512_second_intervals(70);
795        let lock_by_time = LockTime::from(time);
796
797        assert!(!lock_by_time
798            .is_implied_by(LockTime::from(NumberOf512Seconds::from_512_second_intervals(69))));
799        assert!(lock_by_time
800            .is_implied_by(LockTime::from(NumberOf512Seconds::from_512_second_intervals(70))));
801        assert!(lock_by_time
802            .is_implied_by(LockTime::from(NumberOf512Seconds::from_512_second_intervals(71))));
803    }
804
805    #[test]
806    fn sequence_correctly_implies() {
807        let height = NumberOfBlocks::from_count(10);
808        let time = NumberOf512Seconds::from_512_second_intervals(70);
809
810        let lock_by_height = LockTime::from(height);
811        let lock_by_time = LockTime::from(time);
812
813        let seq_height = Sequence::from(lock_by_height);
814        let seq_time = Sequence::from(lock_by_time);
815
816        assert!(lock_by_height.is_implied_by_sequence(seq_height));
817        assert!(!lock_by_height.is_implied_by_sequence(seq_time));
818
819        assert!(lock_by_time.is_implied_by_sequence(seq_time));
820        assert!(!lock_by_time.is_implied_by_sequence(seq_height));
821
822        let disabled_sequence = Sequence::from_consensus(1 << 31);
823        assert!(!lock_by_height.is_implied_by_sequence(disabled_sequence));
824        assert!(!lock_by_time.is_implied_by_sequence(disabled_sequence));
825    }
826
827    #[test]
828    fn incorrect_units_do_not_imply() {
829        let time = NumberOf512Seconds::from_512_second_intervals(70);
830        let height = NumberOfBlocks::from_count(10);
831
832        let lock_by_time = LockTime::from(time);
833        assert!(!lock_by_time.is_implied_by(LockTime::from(height)));
834    }
835
836    #[test]
837    fn consensus_round_trip() {
838        assert!(LockTime::from_consensus(1 << 31).is_err());
839        assert!(LockTime::from_consensus(1 << 30).is_ok());
840        // Relative locktimes do not care about bits 17 through 21.
841        assert_eq!(LockTime::from_consensus(65536), LockTime::from_consensus(0));
842
843        for val in [0u32, 1, 1000, 65535] {
844            let seq = Sequence::from_consensus(val);
845            let lt = LockTime::from_consensus(val).unwrap();
846            assert_eq!(lt.to_consensus_u32(), val);
847            assert_eq!(lt.to_sequence(), seq);
848            assert_eq!(LockTime::from_sequence(seq).unwrap().to_sequence(), seq);
849
850            let seq = Sequence::from_consensus(val + (1 << 22));
851            let lt = LockTime::from_consensus(val + (1 << 22)).unwrap();
852            assert_eq!(lt.to_consensus_u32(), val + (1 << 22));
853            assert_eq!(lt.to_sequence(), seq);
854            assert_eq!(LockTime::from_sequence(seq).unwrap().to_sequence(), seq);
855        }
856    }
857
858    #[test]
859    #[cfg(feature = "alloc")]
860    fn disabled_locktime_error() {
861        let disabled_sequence = Sequence::from_consensus(1 << 31);
862        let err = LockTime::try_from(disabled_sequence).unwrap_err();
863
864        assert_eq!(err.disabled_locktime_value(), 1 << 31);
865        assert!(!format!("{}", err).is_empty());
866    }
867
868    #[test]
869    #[cfg(feature = "alloc")]
870    fn incompatible_height_error() {
871        // This is an error test these values are not used in the error path.
872        let mined_at = BlockHeight::from_u32(700_000);
873        let chain_tip = BlockHeight::from_u32(800_000);
874
875        let lock_by_time = LockTime::from_512_second_intervals(70); // Arbitrary value.
876        let err = lock_by_time.is_satisfied_by_height(chain_tip, Some(mined_at)).unwrap_err();
877
878        let expected_time = NumberOf512Seconds::from_512_second_intervals(70);
879        assert_eq!(
880            err,
881            IsSatisfiedByHeightError::Incompatible(IncompatibleHeightError(expected_time))
882        );
883        assert!(!format!("{}", err).is_empty());
884    }
885
886    #[test]
887    #[cfg(feature = "alloc")]
888    fn invalid_height_error() {
889        // If chain tip precedes mined_at, should return an invalid height error
890        let mined_at = BlockHeight::from_u32(900_000);
891        let chain_tip = BlockHeight::from_u32(800_000);
892
893        let block_count = NumberOfBlocks::from_count(70); // Arbitrary value.
894        let err = block_count.is_satisfied_by(chain_tip, Some(mined_at)).unwrap_err();
895
896        assert!(matches!(err, InvalidHeightError { chain_tip: _, utxo_mined_at: _ }));
897    }
898
899    #[test]
900    #[cfg(feature = "alloc")]
901    fn incompatible_time_error() {
902        // This is an error test these values are not used in the error path.
903        let mined_at = BlockMtp::from_u32(1_234_567_890);
904        let chain_tip = BlockMtp::from_u32(1_600_000_000);
905
906        let lock_by_height = LockTime::from_block_count(10); // Arbitrary value.
907        let err = lock_by_height.is_satisfied_by_time(chain_tip, Some(mined_at)).unwrap_err();
908
909        let expected_height = NumberOfBlocks::from_count(10);
910        assert_eq!(
911            err,
912            IsSatisfiedByTimeError::Incompatible(IncompatibleTimeError(expected_height))
913        );
914        assert!(!format!("{}", err).is_empty());
915    }
916
917    #[test]
918    #[cfg(feature = "alloc")]
919    fn invalid_time_error() {
920        // If chain tip precedes mined_at, should return an invalid time error
921        let mined_at = BlockMtp::from_u32(1_734_567_890);
922        let chain_tip = BlockMtp::from_u32(1_600_000_000);
923
924        let time_interval = NumberOf512Seconds::from_512_second_intervals(10); // Arbitrary value.
925        let err = time_interval.is_satisfied_by(chain_tip, Some(mined_at)).unwrap_err();
926
927        assert!(matches!(err, InvalidTimeError { chain_tip: _, utxo_mined_at: _ }));
928    }
929
930    #[test]
931    fn locktime_chain_state() {
932        fn generate_timestamps(start: u32, step: u16) -> [BlockTime; 11] {
933            let mut timestamps = [BlockTime::from_u32(0); 11];
934            for (i, ts) in timestamps.iter_mut().enumerate() {
935                *ts = BlockTime::from_u32(start.saturating_sub((step * i as u16).into()));
936            }
937            timestamps
938        }
939
940        let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200);
941        let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
942
943        let chain_height = BlockHeight::from_u32(100);
944        let chain_mtp = BlockMtp::new(timestamps);
945        let utxo_height = BlockHeight::from_u32(80);
946        let utxo_mtp = BlockMtp::new(utxo_timestamps);
947
948        let lock1 = LockTime::Blocks(NumberOfBlocks::from_count(10));
949        assert!(lock1
950            .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
951            .unwrap());
952
953        let lock2 = LockTime::Blocks(NumberOfBlocks::from_count(21));
954        assert!(lock2
955            .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
956            .unwrap());
957
958        let lock3 = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(10));
959        assert!(lock3
960            .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
961            .unwrap());
962
963        let lock4 = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(20000));
964        assert!(!lock4
965            .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
966            .unwrap());
967
968        assert!(LockTime::ZERO
969            .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
970            .unwrap());
971        assert!(LockTime::from_512_second_intervals(0)
972            .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
973            .unwrap());
974
975        let lock6 = LockTime::from_seconds_floor(5000).unwrap();
976        assert!(lock6
977            .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
978            .unwrap());
979
980        let max_height_lock = LockTime::Blocks(NumberOfBlocks::MAX);
981        assert!(!max_height_lock
982            .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
983            .unwrap());
984
985        let max_time_lock = LockTime::Time(NumberOf512Seconds::MAX);
986        assert!(!max_time_lock
987            .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
988            .unwrap());
989
990        let max_chain_height = BlockHeight::from_u32(u32::MAX);
991        let max_chain_mtp = BlockMtp::new(generate_timestamps(u32::MAX, 100));
992        let max_utxo_height = BlockHeight::MAX;
993        let max_utxo_mtp = max_chain_mtp;
994        assert!(!max_height_lock
995            .is_satisfied_by(
996                max_chain_height,
997                max_chain_mtp,
998                Some(max_utxo_height),
999                Some(max_utxo_mtp)
1000            )
1001            .unwrap());
1002        assert!(!max_time_lock
1003            .is_satisfied_by(
1004                max_chain_height,
1005                max_chain_mtp,
1006                Some(max_utxo_height),
1007                Some(max_utxo_mtp)
1008            )
1009            .unwrap());
1010    }
1011
1012    #[test]
1013    fn sanity_check() {
1014        assert_eq!(LockTime::from(NumberOfBlocks::MAX).to_consensus_u32(), u32::from(u16::MAX));
1015        assert_eq!(
1016            NumberOf512Seconds::from_512_second_intervals(100).to_512_second_intervals(),
1017            100u16
1018        );
1019        assert_eq!(
1020            LockTime::from(NumberOf512Seconds::from_512_second_intervals(100)).to_consensus_u32(),
1021            4_194_404u32
1022        ); // 0x400064
1023        assert_eq!(NumberOf512Seconds::from_512_second_intervals(1).to_seconds(), 512);
1024    }
1025
1026    #[test]
1027    fn from_512_second_intervals_roundtrip() {
1028        let intervals = 100_u16;
1029        let locktime = NumberOf512Seconds::from_512_second_intervals(intervals);
1030        assert_eq!(locktime.to_512_second_intervals(), intervals);
1031    }
1032
1033    #[test]
1034    fn from_seconds_ceil_success() {
1035        let actual = NumberOf512Seconds::from_seconds_ceil(100).unwrap();
1036        let expected = NumberOf512Seconds(1_u16);
1037        assert_eq!(actual, expected);
1038    }
1039
1040    #[test]
1041    fn from_seconds_ceil_with_maximum_encodable_seconds_success() {
1042        let actual = NumberOf512Seconds::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS).unwrap();
1043        let expected = NumberOf512Seconds(u16::MAX);
1044        assert_eq!(actual, expected);
1045    }
1046
1047    #[test]
1048    fn from_seconds_ceil_causes_time_overflow_error() {
1049        let result = NumberOf512Seconds::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS + 1);
1050        assert!(result.is_err());
1051    }
1052
1053    #[test]
1054    fn from_seconds_floor_success() {
1055        let actual = NumberOf512Seconds::from_seconds_floor(100).unwrap();
1056        let expected = NumberOf512Seconds(0_u16);
1057        assert_eq!(actual, expected);
1058    }
1059
1060    #[test]
1061    fn from_seconds_floor_with_exact_interval() {
1062        let actual = NumberOf512Seconds::from_seconds_floor(512).unwrap();
1063        let expected = NumberOf512Seconds(1_u16);
1064        assert_eq!(actual, expected);
1065    }
1066
1067    #[test]
1068    fn from_seconds_floor_with_maximum_encodable_seconds_success() {
1069        let actual =
1070            NumberOf512Seconds::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 511).unwrap();
1071        let expected = NumberOf512Seconds(u16::MAX);
1072        assert_eq!(actual, expected);
1073    }
1074
1075    #[test]
1076    fn from_seconds_floor_causes_time_overflow_error() {
1077        let result = NumberOf512Seconds::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 512);
1078        assert!(result.is_err());
1079    }
1080
1081    fn generate_timestamps(start: u32, step: u16) -> [BlockTime; 11] {
1082        let mut timestamps = [BlockTime::from_u32(0); 11];
1083        for (i, ts) in timestamps.iter_mut().enumerate() {
1084            *ts = BlockTime::from_u32(start.saturating_sub((step * i as u16).into()));
1085        }
1086        timestamps
1087    }
1088
1089    #[test]
1090    fn time_chain_state() {
1091        use crate::BlockMtp;
1092
1093        let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200);
1094        let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
1095
1096        let timestamps2: [BlockTime; 11] = generate_timestamps(1_599_995_119, 200);
1097        let utxo_timestamps2: [BlockTime; 11] = generate_timestamps(1_599_990_000, 200);
1098
1099        let timestamps3: [BlockTime; 11] = generate_timestamps(1_600_050_000, 200);
1100        let utxo_timestamps3: [BlockTime; 11] = generate_timestamps(1_599_990_000, 200);
1101
1102        // Test case 1: Satisfaction (current_mtp >= utxo_mtp + required_seconds)
1103        // 10 intervals × 512 seconds = 5120 seconds
1104        let time_lock = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(10));
1105        let chain_state1 = BlockMtp::new(timestamps);
1106        let utxo_state1 = BlockMtp::new(utxo_timestamps);
1107        assert!(time_lock.is_satisfied_by_time(chain_state1, Some(utxo_state1)).unwrap());
1108
1109        // Test case 2: Not satisfied (current_mtp < utxo_mtp + required_seconds)
1110        let chain_state2 = BlockMtp::new(timestamps2);
1111        let utxo_state2 = BlockMtp::new(utxo_timestamps2);
1112        assert!(!time_lock.is_satisfied_by_time(chain_state2, Some(utxo_state2)).unwrap());
1113
1114        // Test case 3: Test with a larger value (100 intervals = 51200 seconds)
1115        let larger_lock = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(100));
1116        let chain_state3 = BlockMtp::new(timestamps3);
1117        let utxo_state3 = BlockMtp::new(utxo_timestamps3);
1118        assert!(larger_lock.is_satisfied_by_time(chain_state3, Some(utxo_state3)).unwrap());
1119
1120        // Test case 4: Overflow handling - tests that is_satisfied_by_time handles overflow gracefully
1121        let max_time_lock = LockTime::Time(NumberOf512Seconds::MAX);
1122        let chain_state4 = BlockMtp::new(timestamps);
1123        let utxo_state4 = BlockMtp::new(utxo_timestamps);
1124        assert!(!max_time_lock.is_satisfied_by_time(chain_state4, Some(utxo_state4)).unwrap());
1125    }
1126
1127    #[test]
1128    fn height_chain_state() {
1129        let height_lock = LockTime::Blocks(NumberOfBlocks(10));
1130
1131        // Test case 1: Satisfaction (current_height >= utxo_height + required)
1132        let chain_state1 = BlockHeight::from_u32(89);
1133        let utxo_state1 = BlockHeight::from_u32(80);
1134        assert!(height_lock.is_satisfied_by_height(chain_state1, Some(utxo_state1)).unwrap());
1135
1136        // Test case 2: Not satisfied (current_height < utxo_height + required)
1137        let chain_state2 = BlockHeight::from_u32(88);
1138        let utxo_state2 = BlockHeight::from_u32(80);
1139        assert!(!height_lock.is_satisfied_by_height(chain_state2, Some(utxo_state2)).unwrap());
1140
1141        // Test case 3: Overflow handling - tests that is_satisfied_by_height handles overflow gracefully
1142        let max_height_lock = LockTime::Blocks(NumberOfBlocks::MAX);
1143        let chain_state3 = BlockHeight::from_u32(1000);
1144        let utxo_state3 = BlockHeight::from_u32(80);
1145        assert!(!max_height_lock.is_satisfied_by_height(chain_state3, Some(utxo_state3)).unwrap());
1146    }
1147    #[test]
1148    fn parent_child_can_be_mined_in_the_same_block_lock_by_count_if_zero() {
1149        let height_lock = LockTime::Blocks(NumberOfBlocks::ZERO);
1150        // Value is meaningless, we only check that lock is ZERO.
1151        let chain_state = BlockHeight::from_u32(100);
1152
1153        assert!(height_lock.is_satisfied_by_height(chain_state, None).unwrap());
1154    }
1155
1156    #[test]
1157    fn parent_child_cannot_be_mined_in_the_same_block_lock_by_count_if_non_zero() {
1158        let height_lock = LockTime::from_block_count(1);
1159        // Value is meaningless, we only check that lock is ZERO.
1160        let chain_state = BlockHeight::from_u32(100);
1161
1162        assert!(!height_lock.is_satisfied_by_height(chain_state, None).unwrap());
1163    }
1164
1165    #[test]
1166    fn parent_child_can_be_mined_in_the_same_block_lock_by_time_if_zero() {
1167        let height_lock = LockTime::Time(NumberOf512Seconds::ZERO);
1168        // Value is meaningless, we only check that lock is ZERO.
1169        let chain_state = crate::BlockMtp::from_u32(83_176_862);
1170
1171        assert!(height_lock.is_satisfied_by_time(chain_state, None).unwrap());
1172    }
1173
1174    #[test]
1175    fn parent_child_cannot_be_mined_in_the_same_block_lock_by_time_if_non_zero() {
1176        let height_lock = LockTime::from_512_second_intervals(1);
1177        // Value is meaningless, we only check that lock is ZERO.
1178        let chain_state = crate::BlockMtp::from_u32(83_176_862);
1179
1180        assert!(!height_lock.is_satisfied_by_time(chain_state, None).unwrap());
1181    }
1182
1183    #[test]
1184    fn satisfied_by_height_boundary() {
1185        for n in [0, 2, 5, 9, 20, u16::MAX] {
1186            let lock = NumberOfBlocks::from_count(n);
1187            let mined_at = BlockHeight::from_u32(100);
1188
1189            let first_spendable_tip = BlockHeight::from_u32(100 + u32::from(n).saturating_sub(1));
1190            assert!(
1191                lock.is_satisfied_by(first_spendable_tip, Some(mined_at)).unwrap(),
1192                "n={n} must be satisfied at tip {first_spendable_tip} ({n} confirmations)"
1193            );
1194
1195            if n > 1 {
1196                let too_early = BlockHeight::from_u32(100 + u32::from(n) - 2);
1197                assert!(
1198                    !lock.is_satisfied_by(too_early, Some(mined_at)).unwrap(),
1199                    "n={n} must not be satisfied at tip {too_early}"
1200                );
1201            }
1202        }
1203    }
1204
1205    #[test]
1206    fn max_height_satisfaction() {
1207        // If the difference between these two is u32::MAX, we should get Ok(true)
1208        let mined_at = BlockHeight::from_u32(u32::MIN);
1209        let chain_tip = BlockHeight::from_u32(u32::MAX);
1210
1211        let block_height = NumberOfBlocks::from_count(10); // Arbitrary value.
1212        assert!(block_height.is_satisfied_by(chain_tip, Some(mined_at)).unwrap());
1213    }
1214}