bitcoint4/blockdata/locktime/
absolute.rs

1// Rust Bitcoin Library - Written by the rust-bitcoin developers.
2// SPDX-License-Identifier: CC0-1.0
3
4//! Provides type [`LockTime`] that implements the logic around nLockTime/OP_CHECKLOCKTIMEVERIFY.
5//!
6//! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by
7//! whether `LockTime < LOCKTIME_THRESHOLD`.
8//!
9
10use core::{mem, fmt};
11use core::cmp::{PartialOrd, Ordering};
12
13use bitcoin_internals::write_err;
14
15#[cfg(all(test, mutate))]
16use mutagen::mutate;
17
18use crate::consensus::encode::{self, Decodable, Encodable};
19use crate::error::ParseIntError;
20use crate::io::{self, Read, Write};
21use crate::parse::{impl_parse_str_from_int_infallible, impl_parse_str_from_int_fallible};
22use crate::prelude::*;
23use crate::string::FromHexStr;
24
25#[cfg(doc)]
26use crate::absolute;
27
28/// The Threshold for deciding whether a lock time value is a height or a time (see [Bitcoin Core]).
29///
30/// `LockTime` values _below_ the threshold are interpreted as block heights, values _above_ (or
31/// equal to) the threshold are interpreted as block times (UNIX timestamp, seconds since epoch).
32///
33/// Bitcoin is able to safely use this value because a block height greater than 500,000,000 would
34/// never occur because it would represent a height in approximately 9500 years. Conversely, block
35/// times under 500,000,000 will never happen because they would represent times before 1986 which
36/// are, for obvious reasons, not useful within the Bitcoin network.
37///
38/// [Bitcoin Core]: https://github.com/bitcoin/bitcoin/blob/9ccaee1d5e2e4b79b0a7c29aadb41b97e4741332/src/script/script.h#L39
39pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000;
40
41/// An absolute lock time value, representing either a block height or a UNIX timestamp (seconds
42/// since epoch).
43///
44/// Used for transaction lock time (`nLockTime` in Bitcoin Core and [`crate::Transaction::lock_time`]
45/// in this library) and also for the argument to opcode 'OP_CHECKLOCKTIMEVERIFY`.
46///
47/// ### Note on ordering
48///
49/// Because locktimes may be height- or time-based, and these metrics are incommensurate, there
50/// is no total ordering on locktimes. We therefore have implemented [`PartialOrd`] but not [`Ord`].
51/// For [`crate::Transaction`], which has a locktime field, we implement a total ordering to make
52/// it easy to store transactions in sorted data structures, and use the locktime's 32-bit integer
53/// consensus encoding to order it.
54///
55/// ### Relevant BIPs
56///
57/// * [BIP-65 OP_CHECKLOCKTIMEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)
58/// * [BIP-113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki)
59///
60/// # Examples
61/// ```
62/// # use bitcoin::absolute::{LockTime, LockTime::*};
63/// # let n = LockTime::from_consensus(741521);          // n OP_CHECKLOCKTIMEVERIFY
64/// # let lock_time = LockTime::from_consensus(741521);  // nLockTime
65/// // To compare absolute lock times there are various `is_satisfied_*` methods, you may also use:
66/// let is_satisfied = match (n, lock_time) {
67///     (Blocks(n), Blocks(lock_time)) => n <= lock_time,
68///     (Seconds(n), Seconds(lock_time)) => n <= lock_time,
69///     _ => panic!("handle invalid comparison error"),
70/// };
71/// ```
72#[allow(clippy::derive_ord_xor_partial_ord)]
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
74pub enum LockTime {
75    /// A block height lock time value.
76    ///
77    /// # Examples
78    /// ```rust
79    /// use bitcoin::absolute::LockTime;
80    ///
81    /// let block: u32 = 741521;
82    /// let n = LockTime::from_height(block).expect("valid height");
83    /// assert!(n.is_block_height());
84    /// assert_eq!(n.to_consensus_u32(), block);
85    /// ```
86    Blocks(Height),
87    /// A UNIX timestamp lock time value.
88    ///
89    /// # Examples
90    /// ```rust
91    /// use bitcoin::absolute::LockTime;
92    ///
93    /// let seconds: u32 = 1653195600; // May 22nd, 5am UTC.
94    /// let n = LockTime::from_time(seconds).expect("valid time");
95    /// assert!(n.is_block_time());
96    /// assert_eq!(n.to_consensus_u32(), seconds);
97    /// ```
98    Seconds(Time),
99}
100
101impl LockTime {
102    /// If [`crate::Transaction::lock_time`] is set to zero it is ignored, in other words a
103    /// transaction with nLocktime==0 is able to be included immediately in any block.
104    pub const ZERO: LockTime = LockTime::Blocks(Height::ZERO);
105
106    /// Constructs a `LockTime` from an nLockTime value or the argument to OP_CHEKCLOCKTIMEVERIFY.
107    ///
108    /// # Examples
109    ///
110    /// ```rust
111    /// # use bitcoin::absolute::LockTime;
112    /// # let n = LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY
113    ///
114    /// // `from_consensus` roundtrips as expected with `to_consensus_u32`.
115    /// let n_lock_time: u32 = 741521;
116    /// let lock_time = LockTime::from_consensus(n_lock_time);
117    /// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
118    #[inline]
119    pub fn from_consensus(n: u32) -> Self {
120        if is_block_height(n) {
121            Self::Blocks(Height::from_consensus(n).expect("n is valid"))
122        } else {
123            Self::Seconds(Time::from_consensus(n).expect("n is valid"))
124        }
125    }
126
127    /// Constructs a `LockTime` from `n`, expecting `n` to be a valid block height.
128    ///
129    /// See [`LOCK_TIME_THRESHOLD`] for definition of a valid height value.
130    ///
131    /// # Examples
132    /// ```rust
133    /// # use bitcoin::absolute::LockTime;
134    /// assert!(LockTime::from_height(741521).is_ok());
135    /// assert!(LockTime::from_height(1653195600).is_err());
136    /// ```
137    #[inline]
138    pub fn from_height(n: u32) -> Result<Self, Error> {
139        let height = Height::from_consensus(n)?;
140        Ok(LockTime::Blocks(height))
141    }
142
143    /// Constructs a `LockTime` from `n`, expecting `n` to be a valid block time.
144    ///
145    /// See [`LOCK_TIME_THRESHOLD`] for definition of a valid time value.
146    ///
147    /// # Examples
148    /// ```rust
149    /// # use bitcoin::absolute::LockTime;
150    /// assert!(LockTime::from_time(1653195600).is_ok());
151    /// assert!(LockTime::from_time(741521).is_err());
152    /// ```
153    #[inline]
154    pub fn from_time(n: u32) -> Result<Self, Error> {
155        let time = Time::from_consensus(n)?;
156        Ok(LockTime::Seconds(time))
157    }
158
159    /// Returns true if both lock times use the same unit i.e., both height based or both time based.
160    #[inline]
161    pub fn is_same_unit(&self, other: LockTime) -> bool {
162        mem::discriminant(self) == mem::discriminant(&other)
163    }
164
165    /// Returns true if this lock time value is a block height.
166    #[inline]
167    pub fn is_block_height(&self) -> bool {
168        match *self {
169            LockTime::Blocks(_) => true,
170            LockTime::Seconds(_) => false,
171        }
172    }
173
174    /// Returns true if this lock time value is a block time (UNIX timestamp).
175    #[inline]
176    pub fn is_block_time(&self) -> bool {
177        !self.is_block_height()
178    }
179
180    /// Returns true if this timelock constraint is satisfied by the respective `height`/`time`.
181    ///
182    /// If `self` is a blockheight based lock then it is checked against `height` and if `self` is a
183    /// blocktime based lock it is checked against `time`.
184    ///
185    /// A 'timelock constraint' refers to the `n` from `n OP_CHEKCLOCKTIMEVERIFY`, this constraint
186    /// is satisfied if a transaction with nLockTime ([`crate::Transaction::lock_time`]) set to
187    /// `height`/`time` is valid.
188    ///
189    /// # Examples
190    /// ```no_run
191    /// # use bitcoin::absolute::{LockTime, Height, Time};
192    /// // Can be implemented if block chain data is available.
193    /// fn get_height() -> Height { todo!("return the current block height") }
194    /// fn get_time() -> Time { todo!("return the current block time") }
195    ///
196    /// let n = LockTime::from_consensus(741521); // `n OP_CHEKCLOCKTIMEVERIFY`.
197    /// if n.is_satisfied_by(get_height(), get_time()) {
198    ///     // Can create and mine a transaction that satisfies the OP_CLTV timelock constraint.
199    /// }
200    /// ````
201    #[inline]
202    #[cfg_attr(all(test, mutate), mutate)]
203    pub fn is_satisfied_by(&self, height: Height, time: Time) -> bool {
204        use LockTime::*;
205
206        match *self {
207            Blocks(n) => n <= height,
208            Seconds(n) => n <= time,
209        }
210    }
211
212    /// Returns true if satisfaction of `other` lock time implies satisfaction of this
213    /// [`absolute::LockTime`].
214    ///
215    /// A lock time can only be satisfied by n blocks being mined or n seconds passing. If you have
216    /// two lock times (same unit) then the larger lock time being satisfied implies (in a
217    /// mathematical sense) the smaller one being satisfied.
218    ///
219    /// This function is useful if you wish to check a lock time against various other locks e.g.,
220    /// filtering out locks which cannot be satisfied. Can also be used to remove the smaller value
221    /// of two `OP_CHECKLOCKTIMEVERIFY` operations within one branch of the script.
222    ///
223    /// # Examples
224    ///
225    /// ```rust
226    /// # use bitcoin::absolute::{LockTime, LockTime::*};
227    /// let lock_time = LockTime::from_consensus(741521);
228    /// let check = LockTime::from_consensus(741521 + 1);
229    /// assert!(lock_time.is_implied_by(check));
230    /// ```
231    #[inline]
232    #[cfg_attr(all(test, mutate), mutate)]
233    pub fn is_implied_by(&self, other: LockTime) -> bool {
234        use LockTime::*;
235
236        match (*self, other) {
237            (Blocks(this), Blocks(other)) => this <= other,
238            (Seconds(this), Seconds(other)) => this <= other,
239            _ => false, // Not the same units.
240        }
241    }
242
243    /// Returns the inner `u32` value. This is the value used when creating this `LockTime`
244    /// i.e., `n OP_CHECKLOCKTIMEVERIFY` or nLockTime.
245    ///
246    /// # Warning
247    ///
248    /// Do not compare values return by this method. The whole point of the `LockTime` type is to
249    /// assist in doing correct comparisons. Either use `is_satisfied_by`, `is_satisfied_by_lock`,
250    /// or use the pattern below:
251    ///
252    /// # Examples
253    ///
254    /// ```rust
255    /// # use bitcoin::absolute::{LockTime, LockTime::*};
256    /// # let n = LockTime::from_consensus(741521);              // n OP_CHECKLOCKTIMEVERIFY
257    /// # let lock_time = LockTime::from_consensus(741521 + 1);  // nLockTime
258    ///
259    /// let is_satisfied = match (n, lock_time) {
260    ///     (Blocks(n), Blocks(lock_time)) => n <= lock_time,
261    ///     (Seconds(n), Seconds(lock_time)) => n <= lock_time,
262    ///     _ => panic!("invalid comparison"),
263    /// };
264    ///
265    /// // Or, if you have Rust 1.53 or greater
266    /// // let is_satisfied = n.partial_cmp(&lock_time).expect("invalid comparison").is_le();
267    /// ```
268    #[inline]
269    pub fn to_consensus_u32(self) -> u32 {
270        match self {
271            LockTime::Blocks(ref h) => h.to_consensus_u32(),
272            LockTime::Seconds(ref t) => t.to_consensus_u32(),
273        }
274    }
275}
276
277impl_parse_str_from_int_infallible!(LockTime, u32, from_consensus);
278
279impl From<Height> for LockTime {
280    #[inline]
281    fn from(h: Height) -> Self {
282        LockTime::Blocks(h)
283    }
284}
285
286impl From<Time> for LockTime {
287    #[inline]
288    fn from(t: Time) -> Self {
289        LockTime::Seconds(t)
290    }
291}
292
293impl PartialOrd for LockTime {
294    #[inline]
295    fn partial_cmp(&self, other: &LockTime) -> Option<Ordering> {
296        use LockTime::*;
297
298        match (*self, *other) {
299            (Blocks(ref a), Blocks(ref b)) => a.partial_cmp(b),
300            (Seconds(ref a), Seconds(ref b)) => a.partial_cmp(b),
301            (_, _) => None,
302        }
303    }
304}
305
306impl fmt::Display for LockTime {
307    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308        use LockTime::*;
309
310        if f.alternate() {
311            match *self {
312                Blocks(ref h) => write!(f, "block-height {}", h),
313                Seconds(ref t) => write!(f, "block-time {} (seconds since epoch)", t),
314            }
315        } else {
316            match *self {
317                Blocks(ref h) => fmt::Display::fmt(h, f),
318                Seconds(ref t) => fmt::Display::fmt(t, f),
319            }
320        }
321    }
322}
323
324impl FromHexStr for LockTime {
325    type Error = Error;
326
327    #[inline]
328    fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
329        let packed_lock_time = crate::parse::hex_u32(s)?;
330        Ok(Self::from_consensus(packed_lock_time))
331    }
332}
333
334impl Encodable for LockTime {
335    #[inline]
336    fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
337        let v = self.to_consensus_u32();
338        v.consensus_encode(w)
339    }
340}
341
342impl Decodable for LockTime {
343    #[inline]
344    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
345        u32::consensus_decode(r).map(LockTime::from_consensus)
346    }
347}
348
349#[cfg(feature = "serde")]
350impl serde::Serialize for LockTime {
351    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
352    where
353        S: serde::Serializer,
354    {
355        serializer.serialize_u32(self.to_consensus_u32())
356    }
357}
358
359#[cfg(feature = "serde")]
360impl<'de> serde::Deserialize<'de> for LockTime {
361    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
362    where
363        D: serde::Deserializer<'de>,
364    {
365        struct Visitor;
366        impl<'de> serde::de::Visitor<'de> for Visitor {
367            type Value = u32;
368            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("a u32") }
369            // We cannot just implement visit_u32 because JSON (among other things) always
370            // calls visit_u64, even when called from Deserializer::deserialize_u32. The
371            // other visit_u*s have default implementations that forward to visit_u64.
372            fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<u32, E> {
373                use core::convert::TryInto;
374                v.try_into().map_err(|_| E::invalid_value(serde::de::Unexpected::Unsigned(v), &"a 32-bit number"))
375            }
376            // Also do the signed version, just for good measure.
377            fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<u32, E> {
378                use core::convert::TryInto;
379                v.try_into().map_err(|_| E::invalid_value(serde::de::Unexpected::Signed(v), &"a 32-bit number"))
380            }
381        }
382        deserializer.deserialize_u32(Visitor).map(LockTime::from_consensus)
383    }
384}
385
386
387/// An absolute block height, guaranteed to always contain a valid height value.
388#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
389#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
390#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
391pub struct Height(u32);
392
393impl Height {
394    /// Absolute block height 0, the genesis block.
395    pub const ZERO: Self = Height(0);
396
397    /// The minimum absolute block height (0), the genesis block.
398    pub const MIN: Self = Self::ZERO;
399
400    /// The maximum absolute block height.
401    pub const MAX: Self = Height(LOCK_TIME_THRESHOLD - 1);
402
403    /// The minimum absolute block height (0), the genesis block.
404    ///
405    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Height::MIN`].
406    pub const fn min_value() -> Self { Self::MIN }
407
408    /// The maximum absolute block height.
409    ///
410    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Height::MAX`].
411    pub const fn max_value() -> Self { Self::MAX }
412
413    /// Constructs a new block height.
414    ///
415    /// # Errors
416    ///
417    /// If `n` does not represent a block height value (see documentation on [`LockTime`]).
418    ///
419    /// # Examples
420    /// ```rust
421    /// use bitcoin::locktime::absolute::Height;
422    ///
423    /// let h: u32 = 741521;
424    /// let height = Height::from_consensus(h).expect("invalid height value");
425    /// assert_eq!(height.to_consensus_u32(), h);
426    /// ```
427    #[inline]
428    pub fn from_consensus(n: u32) -> Result<Height, Error> {
429        if is_block_height(n) {
430            Ok(Self(n))
431        } else {
432            Err(ConversionError::invalid_height(n).into())
433        }
434    }
435
436    /// Converts this `Height` to its inner `u32` value.
437    ///
438    /// # Examples
439    /// ```rust
440    /// use bitcoin::absolute::LockTime;
441    ///
442    /// let n_lock_time: u32 = 741521;
443    /// let lock_time = LockTime::from_consensus(n_lock_time);
444    /// assert!(lock_time.is_block_height());
445    /// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
446    #[inline]
447    pub fn to_consensus_u32(self) -> u32 {
448        self.0
449    }
450}
451
452impl_parse_str_from_int_fallible!(Height, u32, from_consensus, Error);
453
454impl fmt::Display for Height {
455    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456        fmt::Display::fmt(&self.0, f)
457    }
458}
459
460impl FromHexStr for Height {
461    type Error = Error;
462
463    #[inline]
464    fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
465        let height = crate::parse::hex_u32(s)?;
466        Self::from_consensus(height)
467    }
468}
469
470/// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value.
471///
472/// Note that there is no manipulation of the inner value during construction or when using
473/// `to_consensus_u32()`. Said another way, `Time(x)` means 'x seconds since epoch' _not_ '(x -
474/// threshold) seconds since epoch'.
475#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
476#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
477#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
478pub struct Time(u32);
479
480impl Time {
481    /// The minimum absolute block time (Tue Nov 05 1985 00:53:20 GMT+0000).
482    pub const MIN: Self = Time(LOCK_TIME_THRESHOLD);
483
484    /// The maximum absolute block time (Sun Feb 07 2106 06:28:15 GMT+0000).
485    pub const MAX: Self = Time(u32::max_value());
486
487    /// The minimum absolute block time.
488    ///
489    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Time::MIN`].
490    pub const fn min_value() -> Self { Self::MIN }
491
492    /// The maximum absolute block time.
493    ///
494    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Time::MAX`].
495    pub const fn max_value() -> Self { Self::MAX }
496
497    /// Constructs a new block time.
498    ///
499    /// # Errors
500    ///
501    /// If `n` does not encode a UNIX time stamp (see documentation on [`LockTime`]).
502    ///
503    /// # Examples
504    /// ```rust
505    /// use bitcoin::locktime::absolute::Time;
506    ///
507    /// let t: u32 = 1653195600; // May 22nd, 5am UTC.
508    /// let time = Time::from_consensus(t).expect("invalid time value");
509    /// assert_eq!(time.to_consensus_u32(), t);
510    /// ```
511    #[inline]
512    pub fn from_consensus(n: u32) -> Result<Time, Error> {
513        if is_block_time(n) {
514            Ok(Self(n))
515        } else {
516            Err(ConversionError::invalid_time(n).into())
517        }
518    }
519
520    /// Converts this `Time` to its inner `u32` value.
521    ///
522    /// # Examples
523    /// ```rust
524    /// use bitcoin::absolute::LockTime;
525    ///
526    /// let n_lock_time: u32 = 1653195600; // May 22nd, 5am UTC.
527    /// let lock_time = LockTime::from_consensus(n_lock_time);
528    /// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
529    /// ```
530    #[inline]
531    pub fn to_consensus_u32(self) -> u32 {
532        self.0
533    }
534}
535
536impl_parse_str_from_int_fallible!(Time, u32, from_consensus, Error);
537
538impl fmt::Display for Time {
539    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
540        fmt::Display::fmt(&self.0, f)
541    }
542}
543
544impl FromHexStr for Time {
545    type Error = Error;
546
547    #[inline]
548    fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
549        let time = crate::parse::hex_u32(s)?;
550        Time::from_consensus(time)
551    }
552}
553
554/// Returns true if `n` is a block height i.e., less than 500,000,000.
555fn is_block_height(n: u32) -> bool {
556    n < LOCK_TIME_THRESHOLD
557}
558
559/// Returns true if `n` is a UNIX timestamp i.e., greater than or equal to 500,000,000.
560fn is_block_time(n: u32) -> bool {
561    n >= LOCK_TIME_THRESHOLD
562}
563
564/// Catchall type for errors that relate to time locks.
565#[derive(Debug, Clone, PartialEq, Eq)]
566#[non_exhaustive]
567pub enum Error {
568    /// An error occurred while converting a `u32` to a lock time variant.
569    Conversion(ConversionError),
570    /// An error occurred while operating on lock times.
571    Operation(OperationError),
572    /// An error occurred while parsing a string into an `u32`.
573    Parse(ParseIntError),
574}
575
576impl fmt::Display for Error {
577    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
578        use self::Error::*;
579
580        match *self {
581            Conversion(ref e) => write_err!(f, "error converting lock time value"; e),
582            Operation(ref e) => write_err!(f, "error during lock time operation"; e),
583            Parse(ref e) => write_err!(f, "failed to parse lock time from string"; e),
584        }
585    }
586}
587
588#[cfg(feature = "std")]
589#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
590impl std::error::Error for Error {
591    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
592        use self::Error::*;
593
594        match *self {
595            Conversion(ref e) => Some(e),
596            Operation(ref e) => Some(e),
597            Parse(ref e) => Some(e),
598        }
599    }
600}
601
602impl From<ConversionError> for Error {
603    #[inline]
604    fn from(e: ConversionError) -> Self {
605        Error::Conversion(e)
606    }
607}
608
609impl From<OperationError> for Error {
610    #[inline]
611    fn from(e: OperationError) -> Self {
612        Error::Operation(e)
613    }
614}
615
616impl From<ParseIntError> for Error {
617    #[inline]
618    fn from(e: ParseIntError) -> Self {
619        Error::Parse(e)
620    }
621}
622
623/// An error that occurs when converting a `u32` to a lock time variant.
624#[derive(Debug, Clone, Eq, PartialEq, Hash)]
625pub struct ConversionError {
626    /// The expected timelock unit, height (blocks) or time (seconds).
627    unit: LockTimeUnit,
628    /// The invalid input value.
629    input: u32,
630}
631
632impl ConversionError {
633    /// Constructs a `ConversionError` from an invalid `n` when expecting a height value.
634    fn invalid_height(n: u32) -> Self {
635        Self {
636            unit: LockTimeUnit::Blocks,
637            input: n,
638        }
639    }
640
641    /// Constructs a `ConversionError` from an invalid `n` when expecting a time value.
642    fn invalid_time(n: u32) -> Self {
643        Self {
644            unit: LockTimeUnit::Seconds,
645            input: n,
646        }
647    }
648}
649
650impl fmt::Display for ConversionError {
651    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652        write!(f, "invalid lock time value {}, {}", self.input, self.unit)
653    }
654}
655
656#[cfg(feature = "std")]
657#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
658impl std::error::Error for ConversionError {}
659
660/// Describes the two types of locking, lock-by-blockheight and lock-by-blocktime.
661#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
662enum LockTimeUnit {
663    /// Lock by blockheight.
664    Blocks,
665    /// Lock by blocktime.
666    Seconds,
667}
668
669impl fmt::Display for LockTimeUnit {
670    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
671        use LockTimeUnit::*;
672
673        match *self {
674            Blocks => write!(f, "expected lock-by-blockheight (must be < {})", LOCK_TIME_THRESHOLD),
675            Seconds => write!(f, "expected lock-by-blocktime (must be >= {})", LOCK_TIME_THRESHOLD),
676        }
677    }
678}
679
680/// Errors than occur when operating on lock times.
681#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
682#[non_exhaustive]
683pub enum OperationError {
684    /// Cannot compare different lock time units (height vs time).
685    InvalidComparison,
686}
687
688impl fmt::Display for OperationError {
689    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
690        use self::OperationError::*;
691
692        match *self {
693            InvalidComparison => f.write_str("cannot compare different lock units (height vs time)"),
694        }
695    }
696}
697
698#[cfg(feature = "std")]
699#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
700impl std::error::Error for OperationError {}
701
702#[cfg(test)]
703mod tests {
704    use super::*;
705
706    #[test]
707    fn display_and_alternate() {
708        let n = LockTime::from_consensus(741521);
709        let s = format!("{}", n);
710        assert_eq!(&s, "741521");
711
712        let got = format!("{:#}", n);
713        assert_eq!(got, "block-height 741521");
714    }
715
716    #[test]
717    fn time_from_str_hex_happy_path() {
718        let actual = Time::from_hex_str("0x6289C350").unwrap();
719        let expected = Time::from_consensus(0x6289C350).unwrap();
720        assert_eq!(actual, expected);
721    }
722
723    #[test]
724    fn time_from_str_hex_no_prefix_happy_path() {
725        let time = Time::from_hex_str_no_prefix("6289C350").unwrap();
726        assert_eq!(time, Time(0x6289C350));
727    }
728
729    #[test]
730    fn time_from_str_hex_invalid_hex_should_err() {
731        let hex = "0xzb93";
732        let result = Time::from_hex_str(hex);
733        assert!(result.is_err());
734    }
735
736    #[test]
737    fn packed_lock_time_from_str_hex_happy_path() {
738        let actual = LockTime::from_hex_str("0xBA70D").unwrap();
739        let expected = LockTime::from_consensus(0xBA70D);
740        assert_eq!(actual, expected);
741    }
742
743    #[test]
744    fn packed_lock_time_from_str_hex_no_prefix_happy_path() {
745        let lock_time = LockTime::from_hex_str_no_prefix("BA70D").unwrap();
746        assert_eq!(lock_time, LockTime::from_consensus(0xBA70D));
747    }
748
749    #[test]
750    fn packed_lock_time_from_str_hex_invalid_hex_should_ergr() {
751        let hex = "0xzb93";
752        let result = LockTime::from_hex_str(hex);
753        assert!(result.is_err());
754    }
755
756    #[test]
757    fn height_from_str_hex_happy_path() {
758        let actual = Height::from_hex_str("0xBA70D").unwrap();
759        let expected = Height(0xBA70D);
760        assert_eq!(actual, expected);
761    }
762
763    #[test]
764    fn height_from_str_hex_no_prefix_happy_path() {
765        let height = Height::from_hex_str_no_prefix("BA70D").unwrap();
766        assert_eq!(height, Height(0xBA70D));
767    }
768
769    #[test]
770    fn height_from_str_hex_invalid_hex_should_err() {
771        let hex = "0xzb93";
772        let result = Height::from_hex_str(hex);
773        assert!(result.is_err());
774    }
775
776    #[test]
777    fn parses_correctly_to_height_or_time() {
778        let lock = LockTime::from_consensus(750_000);
779
780        assert!(lock.is_block_height());
781        assert!(!lock.is_block_time());
782
783        let t: u32 = 1653195600; // May 22nd, 5am UTC.
784        let lock = LockTime::from_consensus(t);
785
786        assert!(!lock.is_block_height());
787        assert!(lock.is_block_time());
788    }
789
790    #[test]
791    fn satisfied_by_height() {
792        let lock = LockTime::from_consensus(750_000);
793
794        let height = Height::from_consensus(800_000).expect("failed to parse height");
795
796        let t: u32 = 1653195600; // May 22nd, 5am UTC.
797        let time = Time::from_consensus(t).expect("invalid time value");
798
799        assert!(lock.is_satisfied_by(height, time))
800    }
801
802    #[test]
803    fn satisfied_by_time() {
804        let lock = LockTime::from_consensus(1053195600);
805
806        let t: u32 = 1653195600; // May 22nd, 5am UTC.
807        let time = Time::from_consensus(t).expect("invalid time value");
808
809        let height = Height::from_consensus(800_000).expect("failed to parse height");
810
811        assert!(lock.is_satisfied_by(height, time))
812    }
813
814    #[test]
815    fn satisfied_by_same_height() {
816        let h = 750_000;
817        let lock = LockTime::from_consensus(h);
818        let height = Height::from_consensus(h).expect("failed to parse height");
819
820        let t: u32 = 1653195600; // May 22nd, 5am UTC.
821        let time = Time::from_consensus(t).expect("invalid time value");
822
823        assert!(lock.is_satisfied_by(height, time))
824    }
825
826    #[test]
827    fn satisfied_by_same_time() {
828        let t: u32 = 1653195600; // May 22nd, 5am UTC.
829        let lock = LockTime::from_consensus(t);
830        let time = Time::from_consensus(t).expect("invalid time value");
831
832        let height = Height::from_consensus(800_000).expect("failed to parse height");
833
834        assert!(lock.is_satisfied_by(height, time))
835    }
836
837    #[test]
838    fn height_correctly_implies() {
839        let lock = LockTime::from_consensus(750_005);
840
841        assert!(!lock.is_implied_by(LockTime::from_consensus(750_004)));
842        assert!(lock.is_implied_by(LockTime::from_consensus(750_005)));
843        assert!(lock.is_implied_by(LockTime::from_consensus(750_006)));
844   }
845
846    #[test]
847    fn time_correctly_implies() {
848        let t: u32 = 1700000005;
849        let lock = LockTime::from_consensus(t);
850
851        assert!(!lock.is_implied_by(LockTime::from_consensus(1700000004)));
852        assert!(lock.is_implied_by(LockTime::from_consensus(1700000005)));
853        assert!(lock.is_implied_by(LockTime::from_consensus(1700000006)));
854   }
855
856    #[test]
857    fn incorrect_units_do_not_imply() {
858        let lock = LockTime::from_consensus(750_005);
859        assert!(!lock.is_implied_by(LockTime::from_consensus(1700000004)));
860    }
861}