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