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}