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