bitcoin_primitives/locktime/
absolute.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-blockheight and lock-by-blocktime, distinguished by
6//! whether `LockTime < LOCKTIME_THRESHOLD`.
7
8use core::cmp::Ordering;
9use core::fmt;
10
11#[cfg(feature = "arbitrary")]
12use arbitrary::{Arbitrary, Unstructured};
13#[cfg(all(test, mutate))]
14use mutagen::mutate;
15use units::parse::{self, PrefixedHexError, UnprefixedHexError};
16
17#[cfg(all(doc, feature = "alloc"))]
18use crate::{absolute, Transaction};
19
20#[rustfmt::skip]                // Keep public re-exports separate.
21#[doc(inline)]
22pub use units::locktime::absolute::{ConversionError, Height, ParseHeightError, ParseTimeError, Time, LOCK_TIME_THRESHOLD};
23
24/// An absolute lock time value, representing either a block height or a UNIX timestamp (seconds
25/// since epoch).
26///
27/// Used for transaction lock time (`nLockTime` in Bitcoin Core and [`Transaction::lock_time`]
28/// in this library) and also for the argument to opcode 'OP_CHECKLOCKTIMEVERIFY`.
29///
30/// ### Note on ordering
31///
32/// Locktimes may be height- or time-based, and these metrics are incommensurate; there is no total
33/// ordering on locktimes. We therefore have implemented [`PartialOrd`] but not [`Ord`].
34/// For [`Transaction`], which has a locktime field, we implement a total ordering to make
35/// it easy to store transactions in sorted data structures, and use the locktime's 32-bit integer
36/// consensus encoding to order it. We also implement [`ordered::ArbitraryOrd`] if the "ordered"
37/// feature is enabled.
38///
39/// ### Relevant BIPs
40///
41/// * [BIP-65 OP_CHECKLOCKTIMEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)
42/// * [BIP-113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki)
43///
44/// # Examples
45///
46/// ```
47/// # use bitcoin_primitives::absolute::{LockTime, LockTime::*};
48/// # let n = LockTime::from_consensus(741521);          // n OP_CHECKLOCKTIMEVERIFY
49/// # let lock_time = LockTime::from_consensus(741521);  // nLockTime
50/// // To compare absolute lock times there are various `is_satisfied_*` methods, you may also use:
51/// let _is_satisfied = match (n, lock_time) {
52///     (Blocks(n), Blocks(lock_time)) => n <= lock_time,
53///     (Seconds(n), Seconds(lock_time)) => n <= lock_time,
54///     _ => panic!("handle invalid comparison error"),
55/// };
56/// ```
57#[derive(Clone, Copy, PartialEq, Eq, Hash)]
58pub enum LockTime {
59    /// A block height lock time value.
60    ///
61    /// # Examples
62    ///
63    /// ```rust
64    /// use bitcoin_primitives::absolute::LockTime;
65    ///
66    /// let block: u32 = 741521;
67    /// let n = LockTime::from_height(block).expect("valid height");
68    /// assert!(n.is_block_height());
69    /// assert_eq!(n.to_consensus_u32(), block);
70    /// ```
71    Blocks(Height),
72    /// A UNIX timestamp lock time value.
73    ///
74    /// # Examples
75    ///
76    /// ```rust
77    /// use bitcoin_primitives::absolute::LockTime;
78    ///
79    /// let seconds: u32 = 1653195600; // May 22nd, 5am UTC.
80    /// let n = LockTime::from_time(seconds).expect("valid time");
81    /// assert!(n.is_block_time());
82    /// assert_eq!(n.to_consensus_u32(), seconds);
83    /// ```
84    Seconds(Time),
85}
86
87impl LockTime {
88    /// If [`Transaction::lock_time`] is set to zero it is ignored, in other words a
89    /// transaction with nLocktime==0 is able to be included immediately in any block.
90    pub const ZERO: LockTime = LockTime::Blocks(Height::ZERO);
91
92    /// The number of bytes that the locktime contributes to the size of a transaction.
93    pub const SIZE: usize = 4; // Serialized length of a u32.
94
95    /// Constructs a new `LockTime` from a prefixed hex string.
96    pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
97        let lock_time = parse::hex_u32_prefixed(s)?;
98        Ok(Self::from_consensus(lock_time))
99    }
100
101    /// Constructs a new `LockTime` from an unprefixed hex string.
102    pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
103        let lock_time = parse::hex_u32_unprefixed(s)?;
104        Ok(Self::from_consensus(lock_time))
105    }
106
107    /// Constructs a new `LockTime` from an nLockTime value or the argument to OP_CHEKCLOCKTIMEVERIFY.
108    ///
109    /// # Examples
110    ///
111    /// ```rust
112    /// # use bitcoin_primitives::absolute::LockTime;
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 units::locktime::absolute::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 new `LockTime` from `n`, expecting `n` to be a valid block height.
128    ///
129    /// # Note
130    ///
131    /// If the current block height is `h` and the locktime is set to `h`,
132    /// the transaction can be included in block `h+1` or later.
133    /// It is possible to broadcast the transaction at block height `h`.
134    ///
135    /// See [`LOCK_TIME_THRESHOLD`] for definition of a valid height value.
136    ///
137    /// # Examples
138    ///
139    /// ```rust
140    /// # use bitcoin_primitives::absolute::LockTime;
141    /// assert!(LockTime::from_height(741521).is_ok());
142    /// assert!(LockTime::from_height(1653195600).is_err());
143    /// ```
144    #[inline]
145    pub fn from_height(n: u32) -> Result<Self, ConversionError> {
146        let height = Height::from_consensus(n)?;
147        Ok(LockTime::Blocks(height))
148    }
149
150    /// Constructs a new `LockTime` from `n`, expecting `n` to be a valid block time.
151    ///
152    /// # Note
153    ///
154    /// If the locktime is set to a timestamp `T`,
155    /// the transaction can be included in a block only if the median time past (MTP) of the
156    /// last 11 blocks is greater than `T`.
157    /// It is possible to broadcast the transaction once the MTP is greater than `T`.[see BIP-113]
158    ///
159    /// [BIP-113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki)
160    ///
161    /// See [`LOCK_TIME_THRESHOLD`] for definition of a valid time value.
162    ///
163    /// # Examples
164    ///
165    /// ```rust
166    /// # use bitcoin_primitives::absolute::LockTime;
167    /// assert!(LockTime::from_time(1653195600).is_ok());
168    /// assert!(LockTime::from_time(741521).is_err());
169    /// ```
170    #[inline]
171    pub fn from_time(n: u32) -> Result<Self, ConversionError> {
172        let time = Time::from_consensus(n)?;
173        Ok(LockTime::Seconds(time))
174    }
175
176    /// Returns true if both lock times use the same unit i.e., both height based or both time based.
177    #[inline]
178    pub const fn is_same_unit(&self, other: LockTime) -> bool {
179        matches!(
180            (self, other),
181            (LockTime::Blocks(_), LockTime::Blocks(_))
182                | (LockTime::Seconds(_), LockTime::Seconds(_))
183        )
184    }
185
186    /// Returns true if this lock time value is a block height.
187    #[inline]
188    pub const fn is_block_height(&self) -> bool { matches!(*self, LockTime::Blocks(_)) }
189
190    /// Returns true if this lock time value is a block time (UNIX timestamp).
191    #[inline]
192    pub const fn is_block_time(&self) -> bool { !self.is_block_height() }
193
194    /// Returns true if this timelock constraint is satisfied by the respective `height`/`time`.
195    ///
196    /// If `self` is a blockheight based lock then it is checked against `height` and if `self` is a
197    /// blocktime based lock it is checked against `time`.
198    ///
199    /// A 'timelock constraint' refers to the `n` from `n OP_CHEKCLOCKTIMEVERIFY`, this constraint
200    /// is satisfied if a transaction with nLockTime ([`Transaction::lock_time`]) set to
201    /// `height`/`time` is valid.
202    ///
203    /// # Examples
204    ///
205    /// ```no_run
206    /// # use bitcoin_primitives::absolute::{LockTime, Height, Time};
207    /// // Can be implemented if block chain data is available.
208    /// fn get_height() -> Height { todo!("return the current block height") }
209    /// fn get_time() -> Time { todo!("return the current block time") }
210    ///
211    /// let n = LockTime::from_consensus(741521); // `n OP_CHEKCLOCKTIMEVERIFY`.
212    /// if n.is_satisfied_by(get_height(), get_time()) {
213    ///     // Can create and mine a transaction that satisfies the OP_CLTV timelock constraint.
214    /// }
215    /// ````
216    #[inline]
217    #[cfg_attr(all(test, mutate), mutate)]
218    pub fn is_satisfied_by(&self, height: Height, time: Time) -> bool {
219        use LockTime::*;
220
221        match *self {
222            Blocks(n) => n <= height,
223            Seconds(n) => n <= time,
224        }
225    }
226
227    /// Returns true if satisfaction of `other` lock time implies satisfaction of this
228    /// [`absolute::LockTime`].
229    ///
230    /// A lock time can only be satisfied by n blocks being mined or n seconds passing. If you have
231    /// two lock times (same unit) then the larger lock time being satisfied implies (in a
232    /// mathematical sense) the smaller one being satisfied.
233    ///
234    /// This function is useful if you wish to check a lock time against various other locks e.g.,
235    /// filtering out locks which cannot be satisfied. Can also be used to remove the smaller value
236    /// of two `OP_CHECKLOCKTIMEVERIFY` operations within one branch of the script.
237    ///
238    /// # Examples
239    ///
240    /// ```rust
241    /// # use bitcoin_primitives::absolute::LockTime;
242    /// let lock_time = LockTime::from_consensus(741521);
243    /// let check = LockTime::from_consensus(741521 + 1);
244    /// assert!(lock_time.is_implied_by(check));
245    /// ```
246    #[inline]
247    #[cfg_attr(all(test, mutate), mutate)]
248    pub fn is_implied_by(&self, other: LockTime) -> bool {
249        use LockTime::*;
250
251        match (*self, other) {
252            (Blocks(this), Blocks(other)) => this <= other,
253            (Seconds(this), Seconds(other)) => this <= other,
254            _ => false, // Not the same units.
255        }
256    }
257
258    /// Returns the inner `u32` value. This is the value used when creating this `LockTime`
259    /// i.e., `n OP_CHECKLOCKTIMEVERIFY` or nLockTime.
260    ///
261    /// # Warning
262    ///
263    /// Do not compare values return by this method. The whole point of the `LockTime` type is to
264    /// assist in doing correct comparisons. Either use `is_satisfied_by`, `is_satisfied_by_lock`,
265    /// or use the pattern below:
266    ///
267    /// # Examples
268    ///
269    /// ```rust
270    /// # use bitcoin_primitives::absolute::{LockTime, LockTime::*};
271    /// # let n = LockTime::from_consensus(741521);              // n OP_CHECKLOCKTIMEVERIFY
272    /// # let lock_time = LockTime::from_consensus(741521 + 1);  // nLockTime
273    ///
274    /// let _is_satisfied = match (n, lock_time) {
275    ///     (Blocks(n), Blocks(lock_time)) => n <= lock_time,
276    ///     (Seconds(n), Seconds(lock_time)) => n <= lock_time,
277    ///     _ => panic!("invalid comparison"),
278    /// };
279    ///
280    /// // Or, if you have Rust 1.53 or greater
281    /// // let is_satisfied = n.partial_cmp(&lock_time).expect("invalid comparison").is_le();
282    /// ```
283    #[inline]
284    pub fn to_consensus_u32(self) -> u32 {
285        match self {
286            LockTime::Blocks(ref h) => h.to_consensus_u32(),
287            LockTime::Seconds(ref t) => t.to_consensus_u32(),
288        }
289    }
290}
291
292units::impl_parse_str_from_int_infallible!(LockTime, u32, from_consensus);
293
294impl From<Height> for LockTime {
295    #[inline]
296    fn from(h: Height) -> Self { LockTime::Blocks(h) }
297}
298
299impl From<Time> for LockTime {
300    #[inline]
301    fn from(t: Time) -> Self { LockTime::Seconds(t) }
302}
303
304impl PartialOrd for LockTime {
305    #[inline]
306    fn partial_cmp(&self, other: &LockTime) -> Option<Ordering> {
307        use LockTime::*;
308
309        match (*self, *other) {
310            (Blocks(ref a), Blocks(ref b)) => a.partial_cmp(b),
311            (Seconds(ref a), Seconds(ref b)) => a.partial_cmp(b),
312            (_, _) => None,
313        }
314    }
315}
316
317impl fmt::Debug for LockTime {
318    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
319        use LockTime::*;
320
321        match *self {
322            Blocks(ref h) => write!(f, "{} blocks", h),
323            Seconds(ref t) => write!(f, "{} seconds", t),
324        }
325    }
326}
327
328impl fmt::Display for LockTime {
329    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
330        use LockTime::*;
331
332        if f.alternate() {
333            match *self {
334                Blocks(ref h) => write!(f, "block-height {}", h),
335                Seconds(ref t) => write!(f, "block-time {} (seconds since epoch)", t),
336            }
337        } else {
338            match *self {
339                Blocks(ref h) => fmt::Display::fmt(h, f),
340                Seconds(ref t) => fmt::Display::fmt(t, f),
341            }
342        }
343    }
344}
345
346#[cfg(feature = "serde")]
347impl serde::Serialize for LockTime {
348    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
349    where
350        S: serde::Serializer,
351    {
352        serializer.serialize_u32(self.to_consensus_u32())
353    }
354}
355
356#[cfg(feature = "serde")]
357impl<'de> serde::Deserialize<'de> for LockTime {
358    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
359    where
360        D: serde::Deserializer<'de>,
361    {
362        struct Visitor;
363        impl serde::de::Visitor<'_> for Visitor {
364            type Value = u32;
365            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("a u32") }
366            // We cannot just implement visit_u32 because JSON (among other things) always
367            // calls visit_u64, even when called from Deserializer::deserialize_u32. The
368            // other visit_u*s have default implementations that forward to visit_u64.
369            fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<u32, E> {
370                v.try_into().map_err(|_| {
371                    E::invalid_value(serde::de::Unexpected::Unsigned(v), &"a 32-bit number")
372                })
373            }
374            // Also do the signed version, just for good measure.
375            fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<u32, E> {
376                v.try_into().map_err(|_| {
377                    E::invalid_value(serde::de::Unexpected::Signed(v), &"a 32-bit number")
378                })
379            }
380        }
381        deserializer.deserialize_u32(Visitor).map(LockTime::from_consensus)
382    }
383}
384
385#[cfg(feature = "ordered")]
386impl ordered::ArbitraryOrd for LockTime {
387    fn arbitrary_cmp(&self, other: &Self) -> Ordering {
388        use LockTime::*;
389
390        match (self, other) {
391            (Blocks(_), Seconds(_)) => Ordering::Less,
392            (Seconds(_), Blocks(_)) => Ordering::Greater,
393            (Blocks(this), Blocks(that)) => this.cmp(that),
394            (Seconds(this), Seconds(that)) => this.cmp(that),
395        }
396    }
397}
398
399#[cfg(feature = "arbitrary")]
400impl<'a> Arbitrary<'a> for LockTime {
401    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
402        let l = u32::arbitrary(u)?;
403        Ok(LockTime::from_consensus(l))
404    }
405}
406
407#[cfg(test)]
408mod tests {
409    use super::*;
410
411    #[test]
412    fn display_and_alternate() {
413        let n = LockTime::from_consensus(741521);
414        let s = format!("{}", n);
415        assert_eq!(&s, "741521");
416
417        let got = format!("{:#}", n);
418        assert_eq!(got, "block-height 741521");
419    }
420
421    #[test]
422    fn lock_time_from_hex_lower() {
423        let lock = LockTime::from_hex("0x6289c350").unwrap();
424        assert_eq!(lock, LockTime::from_consensus(0x6289C350));
425    }
426
427    #[test]
428    fn lock_time_from_hex_upper() {
429        let lock = LockTime::from_hex("0X6289C350").unwrap();
430        assert_eq!(lock, LockTime::from_consensus(0x6289C350));
431    }
432
433    #[test]
434    fn lock_time_from_unprefixed_hex_lower() {
435        let lock = LockTime::from_unprefixed_hex("6289c350").unwrap();
436        assert_eq!(lock, LockTime::from_consensus(0x6289C350));
437    }
438
439    #[test]
440    fn lock_time_from_unprefixed_hex_upper() {
441        let lock = LockTime::from_unprefixed_hex("6289C350").unwrap();
442        assert_eq!(lock, LockTime::from_consensus(0x6289C350));
443    }
444
445    #[test]
446    fn lock_time_from_invalid_hex_should_err() {
447        let hex = "0xzb93";
448        let result = LockTime::from_hex(hex);
449        assert!(result.is_err());
450    }
451
452    #[test]
453    fn parses_correctly_to_height_or_time() {
454        let lock = LockTime::from_consensus(750_000);
455
456        assert!(lock.is_block_height());
457        assert!(!lock.is_block_time());
458
459        let t: u32 = 1653195600; // May 22nd, 5am UTC.
460        let lock = LockTime::from_consensus(t);
461
462        assert!(!lock.is_block_height());
463        assert!(lock.is_block_time());
464    }
465
466    #[test]
467    fn satisfied_by_height() {
468        let lock = LockTime::from_consensus(750_000);
469
470        let height = Height::from_consensus(800_000).expect("failed to parse height");
471
472        let t: u32 = 1653195600; // May 22nd, 5am UTC.
473        let time = Time::from_consensus(t).expect("invalid time value");
474
475        assert!(lock.is_satisfied_by(height, time))
476    }
477
478    #[test]
479    fn satisfied_by_time() {
480        let lock = LockTime::from_consensus(1053195600);
481
482        let t: u32 = 1653195600; // May 22nd, 5am UTC.
483        let time = Time::from_consensus(t).expect("invalid time value");
484
485        let height = Height::from_consensus(800_000).expect("failed to parse height");
486
487        assert!(lock.is_satisfied_by(height, time))
488    }
489
490    #[test]
491    fn satisfied_by_same_height() {
492        let h = 750_000;
493        let lock = LockTime::from_consensus(h);
494        let height = Height::from_consensus(h).expect("failed to parse height");
495
496        let t: u32 = 1653195600; // May 22nd, 5am UTC.
497        let time = Time::from_consensus(t).expect("invalid time value");
498
499        assert!(lock.is_satisfied_by(height, time))
500    }
501
502    #[test]
503    fn satisfied_by_same_time() {
504        let t: u32 = 1653195600; // May 22nd, 5am UTC.
505        let lock = LockTime::from_consensus(t);
506        let time = Time::from_consensus(t).expect("invalid time value");
507
508        let height = Height::from_consensus(800_000).expect("failed to parse height");
509
510        assert!(lock.is_satisfied_by(height, time))
511    }
512
513    #[test]
514    fn height_correctly_implies() {
515        let lock = LockTime::from_consensus(750_005);
516
517        assert!(!lock.is_implied_by(LockTime::from_consensus(750_004)));
518        assert!(lock.is_implied_by(LockTime::from_consensus(750_005)));
519        assert!(lock.is_implied_by(LockTime::from_consensus(750_006)));
520    }
521
522    #[test]
523    fn time_correctly_implies() {
524        let t: u32 = 1700000005;
525        let lock = LockTime::from_consensus(t);
526
527        assert!(!lock.is_implied_by(LockTime::from_consensus(1700000004)));
528        assert!(lock.is_implied_by(LockTime::from_consensus(1700000005)));
529        assert!(lock.is_implied_by(LockTime::from_consensus(1700000006)));
530    }
531
532    #[test]
533    fn incorrect_units_do_not_imply() {
534        let lock = LockTime::from_consensus(750_005);
535        assert!(!lock.is_implied_by(LockTime::from_consensus(1700000004)));
536    }
537}