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