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