Skip to main content

bitcoin_units/locktime/absolute/
mod.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Provides type [`LockTime`] that implements the logic around `nLockTime`/`OP_CHECKLOCKTIMEVERIFY`.
4//!
5//! There are two types of lock time: lock-by-height and lock-by-time, distinguished by
6//! whether [`LockTime`] < [`LOCK_TIME_THRESHOLD`]. To support these we provide the [`Height`] and
7//! [`MedianTimePast`] types.
8
9pub mod error;
10
11use core::fmt;
12
13#[cfg(feature = "arbitrary")]
14use arbitrary::{Arbitrary, Unstructured};
15use internals::error::InputString;
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Deserializer, Serialize, Serializer};
18
19use self::error::ParseError;
20#[cfg(doc)]
21use crate::absolute;
22use crate::parse_int::{self, PrefixedHexError, UnprefixedHexError};
23
24#[rustfmt::skip]                // Keep public re-exports separate.
25#[doc(no_inline)]
26pub use self::error::{
27    ConversionError, IncompatibleHeightError, IncompatibleTimeError, ParseHeightError, ParseTimeError,
28};
29#[cfg(feature = "encoding")]
30#[doc(no_inline)]
31pub use self::error::LockTimeDecoderError;
32
33/// The Threshold for deciding whether a lock time value is a height or a time (see [Bitcoin Core]).
34///
35/// [`LockTime`] values _below_ the threshold are interpreted as block heights, values _above_ (or
36/// equal to) the threshold are interpreted as block times (UNIX timestamp, seconds since epoch).
37///
38/// Bitcoin is able to safely use this value because a block height greater than 500,000,000 would
39/// never occur because it would represent a height in approximately 9500 years. Conversely, block
40/// times under 500,000,000 will never happen because they would represent times before 1986 which
41/// are, for obvious reasons, not useful within the Bitcoin network.
42///
43/// [Bitcoin Core]: https://github.com/bitcoin/bitcoin/blob/9ccaee1d5e2e4b79b0a7c29aadb41b97e4741332/src/script/script.h#L39
44pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000;
45
46/// An absolute lock time value, representing either a block height or a UNIX timestamp (seconds
47/// since epoch).
48///
49/// Used for transaction lock time (`nLockTime` in Bitcoin Core and `Transaction::lock_time`
50/// in `rust-bitcoin`) and also for the argument to opcode `OP_CHECKLOCKTIMEVERIFY`.
51///
52/// # Note on ordering
53///
54/// Locktimes may be height- or time-based, and these metrics are incommensurate; there is no total
55/// ordering on locktimes. In order to compare locktimes, instead of using `<` or `>` we provide the
56/// [`LockTime::is_satisfied_by`] API.
57///
58/// For `Transaction`, which has a locktime field, we implement a total ordering to make
59/// it easy to store transactions in sorted data structures, and use the locktime's 32-bit integer
60/// consensus encoding to order it.
61///
62/// # Relevant BIPs
63///
64/// * [BIP-0065 OP_CHECKLOCKTIMEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)
65/// * [BIP-0113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki)
66///
67/// # Examples
68///
69/// ```
70/// use bitcoin_units::absolute::{self, LockTime as L};
71/// # let n = absolute::LockTime::from_consensus(741521);          // n OP_CHECKLOCKTIMEVERIFY
72/// # let lock_time = absolute::LockTime::from_consensus(741521);  // nLockTime
73/// // To compare absolute lock times there are various `is_satisfied_*` methods, you may also use:
74/// let _is_satisfied = match (n, lock_time) {
75///     (L::Blocks(n), L::Blocks(lock_time)) => n <= lock_time,
76///     (L::Seconds(n), L::Seconds(lock_time)) => n <= lock_time,
77///     _ => panic!("handle invalid comparison error"),
78/// };
79/// ```
80#[derive(Clone, Copy, PartialEq, Eq, Hash)]
81pub enum LockTime {
82    /// A block height lock time value.
83    ///
84    /// # Examples
85    ///
86    /// ```rust
87    /// use bitcoin_units::absolute;
88    ///
89    /// let block: u32 = 741521;
90    /// let n = absolute::LockTime::from_height(block).expect("valid height");
91    /// assert!(n.is_block_height());
92    /// assert_eq!(n.to_consensus_u32(), block);
93    /// ```
94    Blocks(Height),
95    /// A UNIX timestamp lock time value.
96    ///
97    /// # Examples
98    ///
99    /// ```rust
100    /// use bitcoin_units::absolute;
101    ///
102    /// let seconds: u32 = 1653195600; // May 22nd, 5am UTC.
103    /// let n = absolute::LockTime::from_mtp(seconds).expect("valid time");
104    /// assert!(n.is_block_time());
105    /// assert_eq!(n.to_consensus_u32(), seconds);
106    /// ```
107    Seconds(MedianTimePast),
108}
109
110impl LockTime {
111    /// If transaction lock time is set to zero it is ignored, in other words a
112    /// transaction with nLocktime==0 is able to be included immediately in any block.
113    pub const ZERO: Self = Self::Blocks(Height::ZERO);
114
115    /// The number of bytes that the locktime contributes to the size of a transaction.
116    pub const SIZE: usize = 4; // Serialized length of a u32.
117
118    /// Constructs a new [`LockTime`] from a prefixed hex string.
119    ///
120    /// # Errors
121    ///
122    /// If the input string is not a valid hex representation of a locktime or it does not include
123    /// the `0x` prefix.
124    ///
125    /// # Examples
126    ///
127    /// ```
128    /// # use bitcoin_units::{absolute, parse_int};
129    /// let hex_str = "0x61cf9980"; // Unix timestamp for January 1, 2022
130    /// let lock_time = absolute::LockTime::from_hex(hex_str)?;
131    /// assert_eq!(lock_time.to_consensus_u32(), 0x61cf9980);
132    ///
133    /// # Ok::<_, parse_int::PrefixedHexError>(())
134    /// ```
135    #[inline]
136    pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
137        let lock_time = parse_int::hex_u32_prefixed(s)?;
138        Ok(Self::from_consensus(lock_time))
139    }
140
141    /// Constructs a new [`LockTime`] from an unprefixed hex string.
142    ///
143    /// # Errors
144    ///
145    /// If the input string is not a valid hex representation of a locktime or if it includes the
146    /// `0x` prefix.
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// # use bitcoin_units::{absolute, parse_int};
152    /// let hex_str = "61cf9980"; // Unix timestamp for January 1, 2022
153    /// let lock_time = absolute::LockTime::from_unprefixed_hex(hex_str)?;
154    /// assert_eq!(lock_time.to_consensus_u32(), 0x61cf9980);
155    ///
156    /// # Ok::<_, parse_int::UnprefixedHexError>(())
157    /// ```
158    #[inline]
159    pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
160        let lock_time = parse_int::hex_u32_unprefixed(s)?;
161        Ok(Self::from_consensus(lock_time))
162    }
163
164    /// Constructs a new [`LockTime`] from an `nLockTime` value or the argument to
165    /// `OP_CHECKLOCKTIMEVERIFY`.
166    ///
167    /// # Examples
168    ///
169    /// ```rust
170    /// # use bitcoin_units::absolute;
171    ///
172    /// // `from_consensus` roundtrips as expected with `to_consensus_u32`.
173    /// let n_lock_time: u32 = 741521;
174    /// let lock_time = absolute::LockTime::from_consensus(n_lock_time);
175    /// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
176    /// ```
177    #[inline]
178    #[allow(clippy::missing_panics_doc)]
179    pub fn from_consensus(n: u32) -> Self {
180        if crate::locktime::absolute::is_block_height(n) {
181            Self::Blocks(Height::from_u32(n).expect("n is valid"))
182        } else {
183            Self::Seconds(MedianTimePast::from_u32(n).expect("n is valid"))
184        }
185    }
186
187    /// Constructs a new [`LockTime`] from `n`, expecting `n` to be a valid block height.
188    ///
189    /// # Note
190    ///
191    /// If the current block height is `h` and the locktime is set to `h`,
192    /// the transaction can be included in block `h+1` or later.
193    /// It is possible to broadcast the transaction at block height `h`.
194    ///
195    /// See [`LOCK_TIME_THRESHOLD`] for definition of a valid height value.
196    ///
197    /// # Errors
198    ///
199    /// If `n` does not represent a block height within the valid range for a locktime:
200    /// `[0, 499_999_999]`.
201    ///
202    /// # Examples
203    ///
204    /// ```rust
205    /// # use bitcoin_units::absolute;
206    /// assert!(absolute::LockTime::from_height(741521).is_ok());
207    /// assert!(absolute::LockTime::from_height(1653195600).is_err());
208    /// ```
209    #[inline]
210    pub fn from_height(n: u32) -> Result<Self, ConversionError> {
211        let height = Height::from_u32(n)?;
212        Ok(Self::Blocks(height))
213    }
214
215    /// Constructs a new [`LockTime`] from `n`, expecting `n` to be a median-time-past (MTP)
216    /// which is in range for a locktime.
217    ///
218    /// # Note
219    ///
220    /// If the locktime is set to an MTP `t`, the transaction can be included in a block only if
221    /// the MTP of last recent 11 blocks is greater than `t`.
222    ///
223    /// It is possible to broadcast the transaction once the MTP is greater than `t`. See BIP-0113.
224    ///
225    /// [BIP-0113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki)
226    ///
227    /// See [`LOCK_TIME_THRESHOLD`] for definition of a valid time value.
228    ///
229    /// # Errors
230    ///
231    /// If `n` is not in the allowable range of MTPs in a locktime: `[500_000_000, 2^32 - 1]`.
232    ///
233    /// # Examples
234    ///
235    /// ```rust
236    /// # use bitcoin_units::absolute;
237    /// assert!(absolute::LockTime::from_mtp(1653195600).is_ok());
238    /// assert!(absolute::LockTime::from_mtp(741521).is_err());
239    /// ```
240    #[inline]
241    pub fn from_mtp(n: u32) -> Result<Self, ConversionError> {
242        let time = MedianTimePast::from_u32(n)?;
243        Ok(Self::Seconds(time))
244    }
245
246    /// Returns `true` if both lock times use the same unit i.e., both height based or both time
247    /// based.
248    #[inline]
249    pub const fn is_same_unit(self, other: Self) -> bool {
250        matches!(
251            (self, other),
252            (Self::Blocks(_), Self::Blocks(_)) | (Self::Seconds(_), Self::Seconds(_))
253        )
254    }
255
256    /// Returns `true` if this lock time value is a block height.
257    #[inline]
258    pub const fn is_block_height(self) -> bool { matches!(self, Self::Blocks(_)) }
259
260    /// Returns `true` if this lock time value is a block time (UNIX timestamp).
261    #[inline]
262    pub const fn is_block_time(self) -> bool { !self.is_block_height() }
263
264    /// Returns `true` if this timelock constraint is satisfied by the respective `height`/`time`.
265    ///
266    /// If `self` is a blockheight based lock then it is checked against `height` and if `self` is a
267    /// blocktime based lock it is checked against `time`.
268    ///
269    /// A 'timelock constraint' refers to the `n` from `n OP_CHECKLOCKTIMEVERIFY`, this constraint
270    /// is satisfied if a transaction with `nLockTime` set to `height`/`time` is valid.
271    ///
272    /// If `height` and `mtp` represent the current chain tip then a transaction with this
273    /// locktime can be broadcast for inclusion in the next block.
274    ///
275    /// If you do not have, or do not wish to calculate, both parameters consider using:
276    ///
277    /// * [`is_satisfied_by_height()`]
278    /// * [`is_satisfied_by_time()`]
279    ///
280    /// [`is_satisfied_by_height()`]: Self::is_satisfied_by_height
281    /// [`is_satisfied_by_time()`]: Self::is_satisfied_by_time
282    ///
283    /// # Examples
284    ///
285    /// ```no_run
286    /// # use bitcoin_units::absolute;
287    /// // Can be implemented if block chain data is available.
288    /// fn get_height() -> absolute::Height { todo!("return the current block height") }
289    /// fn get_time() -> absolute::MedianTimePast { todo!("return the current block MTP") }
290    ///
291    /// let n = absolute::LockTime::from_consensus(741521); // `n OP_CHECKLOCKTIMEVERIFY`.
292    /// if n.is_satisfied_by(get_height(), get_time()) {
293    ///     // Can create and mine a transaction that satisfies the OP_CLTV timelock constraint.
294    /// }
295    /// ```
296    #[inline]
297    pub fn is_satisfied_by(self, height: Height, mtp: MedianTimePast) -> bool {
298        match self {
299            Self::Blocks(blocks) => blocks.is_satisfied_by(height),
300            Self::Seconds(time) => time.is_satisfied_by(mtp),
301        }
302    }
303
304    /// Returns `true` if a transaction with this locktime can be spent in the next block.
305    ///
306    /// If `height` is the current block height of the chain then a transaction with this locktime
307    /// can be broadcast for inclusion in the next block.
308    ///
309    /// # Errors
310    ///
311    /// Returns an error if this lock is not lock-by-height.
312    #[inline]
313    pub fn is_satisfied_by_height(self, height: Height) -> Result<bool, IncompatibleHeightError> {
314        match self {
315            Self::Blocks(blocks) => Ok(blocks.is_satisfied_by(height)),
316            Self::Seconds(time) =>
317                Err(IncompatibleHeightError { lock: time, incompatible: height }),
318        }
319    }
320
321    /// Returns `true` if a transaction with this locktime can be included in the next block.
322    ///
323    /// # Errors
324    ///
325    /// Returns an error if this lock is not lock-by-time.
326    #[inline]
327    pub fn is_satisfied_by_time(self, mtp: MedianTimePast) -> Result<bool, IncompatibleTimeError> {
328        match self {
329            Self::Seconds(time) => Ok(time.is_satisfied_by(mtp)),
330            Self::Blocks(blocks) => Err(IncompatibleTimeError { lock: blocks, incompatible: mtp }),
331        }
332    }
333
334    /// Returns `true` if satisfaction of `other` lock time implies satisfaction of this
335    /// [`absolute::LockTime`].
336    ///
337    /// A lock time can only be satisfied by n blocks being mined or n seconds passing. If you have
338    /// two lock times (same unit) then the larger lock time being satisfied implies (in a
339    /// mathematical sense) the smaller one being satisfied.
340    ///
341    /// This function serves multiple purposes:
342    ///
343    /// * When evaluating `OP_CHECKLOCKTIMEVERIFY` the argument must be less than or equal to the
344    ///   transactions nLockTime. If using this function to validate a script `self` is the argument
345    ///   to `CLTV` and `other` is the transaction nLockTime.
346    ///
347    /// * If you wish to check a lock time against various other locks e.g., filtering out locks
348    ///   which cannot be satisfied. Can also be used to remove the smaller value of two
349    ///   `OP_CHECKLOCKTIMEVERIFY` operations within one branch of the script.
350    ///
351    /// # Examples
352    ///
353    /// ```rust
354    /// # use bitcoin_units::absolute;
355    /// let lock_time = absolute::LockTime::from_consensus(741521);
356    /// let check = absolute::LockTime::from_consensus(741521 + 1);
357    /// assert!(lock_time.is_implied_by(check));
358    /// ```
359    #[inline]
360    pub fn is_implied_by(self, other: Self) -> bool {
361        match (self, other) {
362            (Self::Blocks(this), Self::Blocks(other)) => this <= other,
363            (Self::Seconds(this), Self::Seconds(other)) => this <= other,
364            _ => false, // Not the same units.
365        }
366    }
367
368    /// Returns the inner `u32` value. This is the value used when creating this [`LockTime`]
369    /// i.e., `n OP_CHECKLOCKTIMEVERIFY` or `nLockTime`.
370    ///
371    /// # Warning
372    ///
373    /// Do not compare values return by this method. The whole point of the [`LockTime`] type is to
374    /// assist in doing correct comparisons. Either use [`is_satisfied_by`],
375    /// [`is_satisfied_by_time`], or use the pattern below:
376    ///
377    /// [`is_satisfied_by`]: Self::is_satisfied_by
378    /// [`is_satisfied_by_time`]: Self::is_satisfied_by_time
379    ///
380    /// # Examples
381    ///
382    /// ```rust
383    /// use bitcoin_units::absolute::{self, LockTime as L};
384    /// # let n = absolute::LockTime::from_consensus(741521);  // n OP_CHECKLOCKTIMEVERIFY
385    /// # let lock_time = absolute::LockTime::from_consensus(741521 + 1);  // nLockTime
386    ///
387    /// let _is_satisfied = match (n, lock_time) {
388    ///     (L::Blocks(n), L::Blocks(lock_time)) => n <= lock_time,
389    ///     (L::Seconds(n), L::Seconds(lock_time)) => n <= lock_time,
390    ///     _ => panic!("invalid comparison"),
391    /// };
392    ///
393    /// // Or, if you have Rust 1.53 or greater
394    /// // let is_satisfied = n.partial_cmp(&lock_time).expect("invalid comparison").is_le();
395    /// ```
396    #[inline]
397    pub fn to_consensus_u32(self) -> u32 {
398        match self {
399            Self::Blocks(ref h) => h.to_u32(),
400            Self::Seconds(ref t) => t.to_u32(),
401        }
402    }
403}
404
405parse_int::impl_parse_str_from_int_infallible!(LockTime, u32, from_consensus);
406
407#[cfg(feature = "encoding")]
408impl encoding::Encode for LockTime {
409    type Encoder<'e> = LockTimeEncoder<'e>;
410    #[inline]
411    fn encoder(&self) -> Self::Encoder<'_> {
412        LockTimeEncoder::new(encoding::ArrayEncoder::without_length_prefix(
413            self.to_consensus_u32().to_le_bytes(),
414        ))
415    }
416}
417
418#[cfg(feature = "encoding")]
419impl encoding::Decode for LockTime {
420    type Decoder = LockTimeDecoder;
421}
422
423#[cfg(feature = "encoding")]
424encoding::encoder_newtype_exact! {
425    /// The encoder for the [`LockTime`] type.
426    #[derive(Debug, Clone)]
427    pub struct LockTimeEncoder<'e>(encoding::ArrayEncoder<4>);
428}
429
430#[cfg(feature = "encoding")]
431crate::decoder_newtype! {
432    /// The decoder for the [`LockTime`] type.
433    #[derive(Debug, Clone)]
434    pub struct LockTimeDecoder(encoding::ArrayDecoder<4>);
435
436    /// Constructs a new [`LockTime`] decoder.
437    pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
438
439    fn end(result: Result<[u8; 4], encoding::UnexpectedEofError>) -> Result<LockTime, LockTimeDecoderError> {
440        let value = result.map_err(LockTimeDecoderError)?;
441        let n = u32::from_le_bytes(value);
442        Ok(LockTime::from_consensus(n))
443    }
444}
445
446impl From<Height> for LockTime {
447    #[inline]
448    fn from(h: Height) -> Self { Self::Blocks(h) }
449}
450
451impl From<MedianTimePast> for LockTime {
452    #[inline]
453    fn from(t: MedianTimePast) -> Self { Self::Seconds(t) }
454}
455
456impl fmt::Debug for LockTime {
457    #[inline]
458    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
459        match *self {
460            Self::Blocks(ref h) => write!(f, "{} blocks", h),
461            Self::Seconds(ref t) => write!(f, "{} seconds", t),
462        }
463    }
464}
465
466impl fmt::Display for LockTime {
467    #[inline]
468    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
469        if f.alternate() {
470            match *self {
471                Self::Blocks(ref h) => write!(f, "block-height {}", h),
472                Self::Seconds(ref t) => write!(f, "block-time {} (seconds since epoch)", t),
473            }
474        } else {
475            match *self {
476                Self::Blocks(ref h) => fmt::Display::fmt(h, f),
477                Self::Seconds(ref t) => fmt::Display::fmt(t, f),
478            }
479        }
480    }
481}
482
483#[cfg(feature = "serde")]
484impl serde::Serialize for LockTime {
485    #[inline]
486    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
487    where
488        S: serde::Serializer,
489    {
490        self.to_consensus_u32().serialize(serializer)
491    }
492}
493
494#[cfg(feature = "serde")]
495impl<'de> serde::Deserialize<'de> for LockTime {
496    #[inline]
497    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
498    where
499        D: serde::Deserializer<'de>,
500    {
501        u32::deserialize(deserializer).map(Self::from_consensus)
502    }
503}
504
505/// An absolute block height, guaranteed to always contain a valid height value.
506#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
507pub struct Height(u32);
508
509impl Height {
510    /// Absolute block height 0, the genesis block.
511    pub const ZERO: Self = Self(0);
512
513    /// The minimum absolute block height (0), the genesis block.
514    pub const MIN: Self = Self::ZERO;
515
516    /// The maximum absolute block height.
517    pub const MAX: Self = Self(LOCK_TIME_THRESHOLD - 1);
518
519    /// Constructs a new [`Height`] from a prefixed hex string.
520    ///
521    /// # Errors
522    ///
523    /// If the input string is not a valid hex representation of a block height or it does not
524    /// include the `0x` prefix.
525    #[inline]
526    pub fn from_hex(s: &str) -> Result<Self, ParseHeightError> {
527        let height = parse_int::hex_u32_prefixed(s).map_err(ParseError::PrefixedHex)?;
528        Ok(Self::from_u32(height).map_err(|_| ParseError::Conversion(height.into()))?)
529    }
530
531    /// Constructs a new [`Height`] from an unprefixed hex string.
532    ///
533    /// # Errors
534    ///
535    /// If the input string is not a valid hex representation of a block height or if it
536    /// includes the `0x` prefix.
537    #[inline]
538    pub fn from_unprefixed_hex(s: &str) -> Result<Self, ParseHeightError> {
539        let height = parse_int::hex_u32_unprefixed(s).map_err(ParseError::UnprefixedHex)?;
540        Ok(Self::from_u32(height).map_err(|_| ParseError::Conversion(height.into()))?)
541    }
542
543    /// Constructs a new block height directly from a `u32` value.
544    ///
545    /// # Errors
546    ///
547    /// If `n` does not represent a block height within the valid range for a locktime:
548    /// `[0, 499_999_999]`.
549    ///
550    /// # Examples
551    ///
552    /// ```rust
553    /// use bitcoin_units::locktime::absolute;
554    ///
555    /// let h: u32 = 741521;
556    /// let height = absolute::Height::from_u32(h)?;
557    /// assert_eq!(height.to_u32(), h);
558    /// # Ok::<_, absolute::error::ConversionError>(())
559    /// ```
560    #[inline]
561    pub const fn from_u32(n: u32) -> Result<Self, ConversionError> {
562        if is_block_height(n) {
563            Ok(Self(n))
564        } else {
565            Err(ConversionError::invalid_height(n))
566        }
567    }
568
569    /// Converts this [`Height`] to a raw `u32` value.
570    ///
571    /// # Examples
572    ///
573    /// ```rust
574    /// use bitcoin_units::locktime::absolute;
575    ///
576    /// assert_eq!(absolute::Height::MAX.to_u32(), 499_999_999);
577    /// ```
578    #[inline]
579    pub const fn to_u32(self) -> u32 { self.0 }
580
581    /// Returns `true` if a transaction with this locktime can be included in the next block.
582    ///
583    /// `self` is value of the [`LockTime`] and if `height` is the current chain tip then
584    /// a transaction with this lock can be broadcast for inclusion in the next block.
585    #[inline]
586    pub fn is_satisfied_by(self, height: Self) -> bool {
587        // Use u64 so that there can be no overflow.
588        // The next block will have a height chain tip + 1
589        let next_block_height = u64::from(height.to_u32()) + 1;
590        u64::from(self.to_u32()) < next_block_height
591    }
592}
593
594crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(Height);
595
596impl fmt::Display for Height {
597    #[inline]
598    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
599}
600
601parse_int::impl_parse_str!(Height, ParseHeightError, parser(Height::from_u32));
602
603#[cfg(feature = "serde")]
604impl Serialize for Height {
605    #[inline]
606    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
607    where
608        S: Serializer,
609    {
610        u32::serialize(&self.to_u32(), s)
611    }
612}
613
614#[cfg(feature = "serde")]
615impl<'de> Deserialize<'de> for Height {
616    #[inline]
617    fn deserialize<D>(d: D) -> Result<Self, D::Error>
618    where
619        D: Deserializer<'de>,
620    {
621        Self::from_u32(u32::deserialize(d)?).map_err(serde::de::Error::custom)
622    }
623}
624
625/// The median timestamp of 11 consecutive blocks, representing "the timestamp" of the
626/// final block for locktime-checking purposes.
627///
628/// Time-based locktimes are not measured against the timestamps in individual block
629/// headers, since these are not monotone and may be subject to miner manipulation.
630/// Instead, locktimes use the "median-time-past" (MTP) of the most recent 11 blocks,
631/// a quantity which is required by consensus to be monotone and which is difficult
632/// for any individual miner to manipulate.
633#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
634pub struct MedianTimePast(u32);
635
636impl MedianTimePast {
637    /// The minimum MTP allowable in a locktime (Tue Nov 05 1985 00:53:20 GMT+0000).
638    pub const MIN: Self = Self(LOCK_TIME_THRESHOLD);
639
640    /// The maximum MTP allowable in a locktime (Sun Feb 07 2106 06:28:15 GMT+0000).
641    pub const MAX: Self = Self(u32::MAX);
642
643    /// Constructs a [`MedianTimePast`] by computing the median-time-past from the last
644    /// 11 block timestamps.
645    ///
646    /// Because block timestamps are not monotonic, this function internally sorts them;
647    /// it is therefore not important what order they appear in the array; use whatever
648    /// is most convenient.
649    ///
650    /// # Errors
651    ///
652    /// If the median block timestamp is not in the allowable range of MTPs in a
653    /// locktime: `[500_000_000, 2^32 - 1]`. Because there is a consensus rule that MTP
654    /// be monotonically increasing, and the MTP of the first 11 blocks exceeds `500_000_000`
655    /// for every real-life chain, this error typically cannot be hit in practice.
656    #[inline]
657    pub fn new(timestamps: [crate::BlockTime; 11]) -> Result<Self, ConversionError> {
658        crate::BlockMtp::new(timestamps).try_into()
659    }
660
661    /// Constructs a new [`MedianTimePast`] from a prefixed hex string.
662    ///
663    /// # Errors
664    ///
665    /// If the input string is not a valid hex representation of a block time or it does not
666    /// include the `0x` prefix.
667    #[inline]
668    pub fn from_hex(s: &str) -> Result<Self, ParseTimeError> {
669        let height = parse_int::hex_u32_prefixed(s).map_err(ParseError::PrefixedHex)?;
670        Ok(Self::from_u32(height).map_err(|_| ParseError::Conversion(height.into()))?)
671    }
672
673    /// Constructs a new [`MedianTimePast`] from an unprefixed hex string.
674    ///
675    /// # Errors
676    ///
677    /// If the input string is not a valid hex representation of a block time or if it
678    /// includes the `0x` prefix.
679    #[inline]
680    pub fn from_unprefixed_hex(s: &str) -> Result<Self, ParseTimeError> {
681        let height = parse_int::hex_u32_unprefixed(s).map_err(ParseError::UnprefixedHex)?;
682        Ok(Self::from_u32(height).map_err(|_| ParseError::Conversion(height.into()))?)
683    }
684
685    /// Constructs a new MTP directly from a `u32` value.
686    ///
687    /// This function, with [`MedianTimePast::to_u32`], is used to obtain a raw MTP value. It is
688    /// **not** used to convert to or from a block timestamp, which is not an MTP.
689    ///
690    /// # Errors
691    ///
692    /// If `n` is not in the allowable range of MTPs in a locktime: `[500_000_000, 2^32 - 1]`.
693    ///
694    /// # Examples
695    ///
696    /// ```rust
697    /// use bitcoin_units::locktime::absolute;
698    ///
699    /// let t: u32 = 1653195600; // May 22nd, 5am UTC.
700    /// let time = absolute::MedianTimePast::from_u32(t)?;
701    /// assert_eq!(time.to_u32(), t);
702    /// # Ok::<_, absolute::error::ConversionError>(())
703    /// ```
704    #[inline]
705    pub const fn from_u32(n: u32) -> Result<Self, ConversionError> {
706        if is_block_time(n) {
707            Ok(Self(n))
708        } else {
709            Err(ConversionError::invalid_time(n))
710        }
711    }
712
713    /// Converts this [`MedianTimePast`] to a raw `u32` value.
714    ///
715    /// # Examples
716    ///
717    /// ```rust
718    /// use bitcoin_units::locktime::absolute;
719    ///
720    /// assert_eq!(absolute::MedianTimePast::MIN.to_u32(), 500_000_000);
721    /// ```
722    #[inline]
723    pub const fn to_u32(self) -> u32 { self.0 }
724
725    /// Returns `true` if a transaction with this locktime can be included in the next block.
726    ///
727    /// `self` is the value of the [`LockTime`] and if `time` is the median time past of the block
728    /// at the chain tip then a transaction with this lock can be broadcast for inclusion in the
729    /// next block.
730    #[inline]
731    pub fn is_satisfied_by(self, time: Self) -> bool { self < time }
732}
733
734crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(MedianTimePast);
735
736impl fmt::Display for MedianTimePast {
737    #[inline]
738    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
739}
740
741parse_int::impl_parse_str!(MedianTimePast, ParseTimeError, parser(MedianTimePast::from_u32));
742
743fn parser<T, E, S, F>(f: F) -> impl FnOnce(S) -> Result<T, E>
744where
745    E: From<ParseError>,
746    S: AsRef<str> + Into<InputString>,
747    F: FnOnce(u32) -> Result<T, ConversionError>,
748{
749    move |s| {
750        let n = s.as_ref().parse::<i64>().map_err(ParseError::invalid_int(s))?;
751        let n = u32::try_from(n).map_err(|_| ParseError::Conversion(n))?;
752        f(n).map_err(ParseError::from).map_err(Into::into)
753    }
754}
755
756#[cfg(feature = "serde")]
757impl Serialize for MedianTimePast {
758    #[inline]
759    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
760    where
761        S: Serializer,
762    {
763        u32::serialize(&self.to_u32(), s)
764    }
765}
766
767#[cfg(feature = "serde")]
768impl<'de> Deserialize<'de> for MedianTimePast {
769    #[inline]
770    fn deserialize<D>(d: D) -> Result<Self, D::Error>
771    where
772        D: Deserializer<'de>,
773    {
774        Self::from_u32(u32::deserialize(d)?).map_err(serde::de::Error::custom)
775    }
776}
777
778/// Returns `true` if `n` is a block height i.e., less than 500,000,000.
779#[inline]
780pub const fn is_block_height(n: u32) -> bool { n < LOCK_TIME_THRESHOLD }
781
782/// Returns `true` if `n` is a UNIX timestamp i.e., greater than or equal to 500,000,000.
783#[inline]
784pub const fn is_block_time(n: u32) -> bool { n >= LOCK_TIME_THRESHOLD }
785
786#[cfg(feature = "arbitrary")]
787impl<'a> Arbitrary<'a> for LockTime {
788    #[inline]
789    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
790        let l = u32::arbitrary(u)?;
791        Ok(Self::from_consensus(l))
792    }
793}
794
795#[cfg(feature = "arbitrary")]
796impl<'a> Arbitrary<'a> for Height {
797    #[inline]
798    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
799        let choice = u.int_in_range(0..=2)?;
800        match choice {
801            0 => Ok(Self::MIN),
802            1 => Ok(Self::MAX),
803            _ => {
804                let min = Self::MIN.to_u32();
805                let max = Self::MAX.to_u32();
806                let h = u.int_in_range(min..=max)?;
807                Ok(Self::from_u32(h).unwrap())
808            }
809        }
810    }
811}
812
813#[cfg(feature = "arbitrary")]
814impl<'a> Arbitrary<'a> for MedianTimePast {
815    #[inline]
816    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
817        let choice = u.int_in_range(0..=2)?;
818        match choice {
819            0 => Ok(Self::MIN),
820            1 => Ok(Self::MAX),
821            _ => {
822                let min = Self::MIN.to_u32();
823                let max = Self::MAX.to_u32();
824                let t = u.int_in_range(min..=max)?;
825                Ok(Self::from_u32(t).unwrap())
826            }
827        }
828    }
829}
830
831#[cfg(test)]
832mod tests {
833    #[cfg(feature = "alloc")]
834    use alloc::{boxed::Box, format, string::String};
835
836    use super::*;
837
838    #[test]
839    #[cfg(feature = "alloc")]
840    fn display_and_alternate() {
841        let lock_by_height = LockTime::from_height(741_521).unwrap();
842        let lock_by_time = LockTime::from_mtp(1_653_195_600).unwrap(); // May 22nd 2022, 5am UTC.
843
844        assert_eq!(format!("{}", lock_by_height), "741521");
845        assert_eq!(format!("{:#}", lock_by_height), "block-height 741521");
846        assert!(!format!("{:?}", lock_by_height).is_empty());
847
848        assert_eq!(format!("{}", lock_by_time), "1653195600");
849        assert_eq!(format!("{:#}", lock_by_time), "block-time 1653195600 (seconds since epoch)");
850        assert!(!format!("{:?}", lock_by_time).is_empty());
851    }
852
853    #[test]
854    fn roundtrips() {
855        let lock_by_height = LockTime::from_consensus(741_521);
856        let lock_by_time = LockTime::from_consensus(1_653_195_600);
857
858        assert_eq!(lock_by_height.to_consensus_u32(), 741_521);
859        assert_eq!(lock_by_time.to_consensus_u32(), 1_653_195_600);
860    }
861
862    #[test]
863    fn lock_time_from_hex_lower() {
864        let lock_by_time = LockTime::from_hex("0x6289c350").unwrap();
865        assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
866    }
867
868    #[test]
869    fn lock_time_from_hex_upper() {
870        let lock_by_time = LockTime::from_hex("0X6289C350").unwrap();
871        assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
872    }
873
874    #[test]
875    fn lock_time_from_unprefixed_hex_lower() {
876        let lock_by_time = LockTime::from_unprefixed_hex("6289c350").unwrap();
877        assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
878    }
879
880    #[test]
881    fn lock_time_from_unprefixed_hex_upper() {
882        let lock_by_time = LockTime::from_unprefixed_hex("6289C350").unwrap();
883        assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
884    }
885
886    #[test]
887    fn invalid_hex() {
888        assert!(LockTime::from_hex("0xzb93").is_err());
889        assert!(LockTime::from_unprefixed_hex("zb93").is_err());
890    }
891
892    #[test]
893    fn invalid_locktime_type() {
894        assert!(LockTime::from_height(499_999_999).is_ok()); // Below the threshold.
895        assert!(LockTime::from_height(500_000_000).is_err()); // The threshold.
896        assert!(LockTime::from_height(500_000_001).is_err()); // Above the threshold.
897
898        assert!(LockTime::from_mtp(499_999_999).is_err()); // Below the threshold.
899        assert!(LockTime::from_mtp(500_000_000).is_ok()); // The threshold.
900        assert!(LockTime::from_mtp(500_000_001).is_ok()); // Above the threshold.
901    }
902
903    #[test]
904    fn parses_correctly_to_height_or_time() {
905        let lock_by_height = LockTime::from_consensus(750_000);
906
907        assert!(lock_by_height.is_block_height());
908        assert!(!lock_by_height.is_block_time());
909
910        let t: u32 = 1_653_195_600; // May 22nd, 5am UTC.
911        let lock_by_time = LockTime::from_consensus(t);
912
913        assert!(!lock_by_time.is_block_height());
914        assert!(lock_by_time.is_block_time());
915
916        // Test is_same_unit() logic
917        assert!(lock_by_height.is_same_unit(LockTime::from_consensus(800_000)));
918        assert!(!lock_by_height.is_same_unit(lock_by_time));
919        assert!(lock_by_time.is_same_unit(LockTime::from_consensus(1_653_282_000)));
920        assert!(!lock_by_time.is_same_unit(lock_by_height));
921    }
922
923    #[test]
924    fn satisfied_by_height() {
925        let height_below = Height::from_u32(700_000).unwrap();
926        let height = Height::from_u32(750_000).unwrap();
927        let height_above = Height::from_u32(800_000).unwrap();
928
929        let lock_by_height = LockTime::from(height);
930
931        let t: u32 = 1_653_195_600; // May 22nd, 5am UTC.
932        let time = MedianTimePast::from_u32(t).unwrap();
933
934        assert!(!lock_by_height.is_satisfied_by(height_below, time));
935        assert!(lock_by_height.is_satisfied_by(height, time));
936        assert!(lock_by_height.is_satisfied_by(height_above, time));
937    }
938
939    #[test]
940    fn satisfied_by_time() {
941        let time_before = MedianTimePast::from_u32(1_653_109_200).unwrap(); // "May 21st 2022, 5am UTC.
942        let time = MedianTimePast::from_u32(1_653_195_600).unwrap(); // "May 22nd 2022, 5am UTC.
943        let time_after = MedianTimePast::from_u32(1_653_282_000).unwrap(); // "May 23rd 2022, 5am UTC.
944
945        let lock_by_time = LockTime::from(time);
946
947        let height = Height::from_u32(800_000).unwrap();
948
949        assert!(!lock_by_time.is_satisfied_by(height, time_before));
950        // Strict less-than comparison (BIP-113) means equality is not satisfied.
951        assert!(!lock_by_time.is_satisfied_by(height, time));
952        assert!(lock_by_time.is_satisfied_by(height, time_after));
953    }
954
955    #[test]
956    fn height_correctly_implies() {
957        let lock_by_height = LockTime::from_consensus(750_005);
958
959        assert!(!lock_by_height.is_implied_by(LockTime::from_consensus(750_004)));
960        assert!(lock_by_height.is_implied_by(LockTime::from_consensus(750_005)));
961        assert!(lock_by_height.is_implied_by(LockTime::from_consensus(750_006)));
962    }
963
964    #[test]
965    fn time_correctly_implies() {
966        let t: u32 = 1_700_000_005;
967        let lock_by_time = LockTime::from_consensus(t);
968
969        assert!(!lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_004)));
970        assert!(lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_005)));
971        assert!(lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_006)));
972    }
973
974    #[test]
975    fn incorrect_units_do_not_imply() {
976        let lock_by_height = LockTime::from_consensus(750_005);
977        assert!(!lock_by_height.is_implied_by(LockTime::from_consensus(1_700_000_004)));
978    }
979
980    #[test]
981    fn time_from_str_hex_happy_path() {
982        let actual = MedianTimePast::from_hex("0x6289C350").unwrap();
983        let expected = MedianTimePast::from_u32(0x6289_C350).unwrap();
984        assert_eq!(actual, expected);
985    }
986
987    #[test]
988    fn time_from_str_hex_no_prefix_happy_path() {
989        let time = MedianTimePast::from_unprefixed_hex("6289C350").unwrap();
990        assert_eq!(time, MedianTimePast(0x6289_C350));
991    }
992
993    #[test]
994    fn time_from_str_hex_invalid_hex_should_err() {
995        let hex = "0xzb93";
996        let result = MedianTimePast::from_hex(hex);
997        assert!(result.is_err());
998    }
999
1000    #[test]
1001    fn height_from_str_hex_happy_path() {
1002        let actual = Height::from_hex("0xBA70D").unwrap();
1003        let expected = Height(0xBA70D);
1004        assert_eq!(actual, expected);
1005    }
1006
1007    #[test]
1008    fn height_from_str_hex_no_prefix_happy_path() {
1009        let height = Height::from_unprefixed_hex("BA70D").unwrap();
1010        assert_eq!(height, Height(0xBA70D));
1011    }
1012
1013    #[test]
1014    fn height_from_str_hex_invalid_hex_should_err() {
1015        let hex = "0xzb93";
1016        let result = Height::from_hex(hex);
1017        assert!(result.is_err());
1018    }
1019
1020    #[test]
1021    fn height_try_from_stringlike_happy_path() {
1022        let want = Height::from_u32(10).unwrap();
1023        assert_eq!("10".parse::<Height>().unwrap(), want);
1024        assert_eq!(Height::try_from("10").unwrap(), want);
1025        #[cfg(feature = "alloc")]
1026        {
1027            assert_eq!(Height::try_from(String::from("10")).unwrap(), want);
1028            assert_eq!(Height::try_from(Box::<str>::from("10")).unwrap(), want);
1029        }
1030    }
1031
1032    #[test]
1033    fn height_try_from_stringlike_hex_error_path() {
1034        // Only base-10 values should parse
1035        assert!("0xab".parse::<Height>().is_err());
1036        assert!(Height::try_from("0xab").is_err());
1037        #[cfg(feature = "alloc")]
1038        {
1039            assert!(Height::try_from(String::from("0xab")).is_err());
1040            assert!(Height::try_from(Box::<str>::from("0xab")).is_err());
1041        }
1042    }
1043
1044    #[test]
1045    fn height_try_from_stringlike_decimal_error_path() {
1046        // Only integers should parse
1047        assert!("10.123".parse::<Height>().is_err());
1048        assert!(Height::try_from("10.123").is_err());
1049        #[cfg(feature = "alloc")]
1050        {
1051            assert!(Height::try_from(String::from("10.123")).is_err());
1052            assert!(Height::try_from(Box::<str>::from("10.123")).is_err());
1053        }
1054    }
1055
1056    #[test]
1057    fn is_block_height_or_time() {
1058        assert!(is_block_height(499_999_999));
1059        assert!(!is_block_height(500_000_000));
1060
1061        assert!(!is_block_time(499_999_999));
1062        assert!(is_block_time(500_000_000));
1063    }
1064
1065    #[test]
1066    fn valid_chain_computes_mtp() {
1067        use crate::BlockTime;
1068
1069        let mut timestamps = [
1070            BlockTime::from_u32(500_000_010),
1071            BlockTime::from_u32(500_000_003),
1072            BlockTime::from_u32(500_000_005),
1073            BlockTime::from_u32(500_000_008),
1074            BlockTime::from_u32(500_000_001),
1075            BlockTime::from_u32(500_000_004),
1076            BlockTime::from_u32(500_000_006),
1077            BlockTime::from_u32(500_000_009),
1078            BlockTime::from_u32(500_000_002),
1079            BlockTime::from_u32(500_000_007),
1080            BlockTime::from_u32(500_000_000),
1081        ];
1082
1083        // Try various reorderings
1084        assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1085        timestamps.reverse();
1086        assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1087        timestamps.sort();
1088        assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1089        timestamps.reverse();
1090        assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1091    }
1092
1093    #[test]
1094    fn height_is_satisfied_by() {
1095        let chain_tip = Height::from_u32(100).unwrap();
1096
1097        // lock is satisfied if transaction can go in the next block (height < chain_tip + 1).
1098        let locktime = Height::from_u32(99).unwrap();
1099        assert!(locktime.is_satisfied_by(chain_tip));
1100        let locktime = Height::from_u32(100).unwrap();
1101        assert!(locktime.is_satisfied_by(chain_tip));
1102
1103        // It is not satisfied if the lock height is after the next block.
1104        let locktime = Height::from_u32(101).unwrap();
1105        assert!(!locktime.is_satisfied_by(chain_tip));
1106    }
1107
1108    #[test]
1109    fn median_time_past_is_satisfied_by() {
1110        let mtp = MedianTimePast::from_u32(500_000_001).unwrap();
1111
1112        // lock is satisfied if transaction can go in the next block (locktime < mtp).
1113        let locktime = MedianTimePast::from_u32(500_000_000).unwrap();
1114        assert!(locktime.is_satisfied_by(mtp));
1115
1116        // It is not satisfied if the lock time is equal to the median time past (BIP-113).
1117        let locktime = MedianTimePast::from_u32(500_000_001).unwrap();
1118        assert!(!locktime.is_satisfied_by(mtp));
1119
1120        // It is not satisfied if the lock time is after the median time past.
1121        let locktime = MedianTimePast::from_u32(500_000_002).unwrap();
1122        assert!(!locktime.is_satisfied_by(mtp));
1123    }
1124
1125    #[test]
1126    fn median_time_past_satisfaction_bip113_compliance() {
1127        let mtp_reference = MedianTimePast::from_u32(500_000_001).unwrap();
1128
1129        // locktime is strictly less than MTP (T < MTP)
1130        let early_locktime = MedianTimePast::from_u32(500_000_000).unwrap();
1131        assert!(
1132            early_locktime.is_satisfied_by(mtp_reference),
1133            "Locktime strictly less than MTP ({} < {}) must be satisfied",
1134            early_locktime,
1135            mtp_reference
1136        );
1137
1138        // locktime is exactly equal to MTP (T == MTP)
1139        let equal_locktime = MedianTimePast::from_u32(500_000_001).unwrap();
1140        assert!(
1141            !equal_locktime.is_satisfied_by(mtp_reference),
1142            "Locktime equal to MTP ({} == {}) must not be satisfied (BIP-113 off-by-one violation)",
1143            equal_locktime,
1144            mtp_reference
1145        );
1146
1147        // locktime is strictly greater than MTP (T > MTP)
1148        let future_locktime = MedianTimePast::from_u32(500_000_002).unwrap();
1149        assert!(
1150            !future_locktime.is_satisfied_by(mtp_reference),
1151            "Locktime greater than MTP ({} > {}) must not be satisfied",
1152            future_locktime,
1153            mtp_reference
1154        );
1155    }
1156}