bitcoin_units/locktime/absolute/mod.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-height and lock-by-time, distinguished by
6//! whether `LockTime < LOCKTIME_THRESHOLD`. To support these we provide the [`Height`] and
7//! [`MedianTimePast`] types.
8
9pub mod error;
10
11use core::fmt;
12
13#[cfg(feature = "arbitrary")]
14use arbitrary::{Arbitrary, Unstructured};
15use internals::error::InputString;
16
17use self::error::ParseError;
18#[cfg(doc)]
19use crate::absolute;
20use crate::parse_int::{self, PrefixedHexError, UnprefixedHexError};
21
22#[rustfmt::skip] // Keep public re-exports separate.
23#[doc(no_inline)]
24pub use self::error::{
25 ConversionError, IncompatibleHeightError, IncompatibleTimeError, ParseHeightError, ParseTimeError,
26};
27#[cfg(feature = "encoding")]
28#[doc(no_inline)]
29pub use self::error::LockTimeDecoderError;
30
31/// The Threshold for deciding whether a lock time value is a height or a time (see [Bitcoin Core]).
32///
33/// `LockTime` values _below_ the threshold are interpreted as block heights, values _above_ (or
34/// equal to) the threshold are interpreted as block times (UNIX timestamp, seconds since epoch).
35///
36/// Bitcoin is able to safely use this value because a block height greater than 500,000,000 would
37/// never occur because it would represent a height in approximately 9500 years. Conversely, block
38/// times under 500,000,000 will never happen because they would represent times before 1986 which
39/// are, for obvious reasons, not useful within the Bitcoin network.
40///
41/// [Bitcoin Core]: https://github.com/bitcoin/bitcoin/blob/9ccaee1d5e2e4b79b0a7c29aadb41b97e4741332/src/script/script.h#L39
42pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000;
43
44/// An absolute lock time value, representing either a block height or a UNIX timestamp (seconds
45/// since epoch).
46///
47/// Used for transaction lock time (`nLockTime` in Bitcoin Core and `Transaction::lock_time`
48/// in `rust-bitcoin`) and also for the argument to opcode `OP_CHECKLOCKTIMEVERIFY`.
49///
50/// # Note on ordering
51///
52/// Locktimes may be height- or time-based, and these metrics are incommensurate; there is no total
53/// ordering on locktimes. In order to compare locktimes, instead of using `<` or `>` we provide the
54/// [`LockTime::is_satisfied_by`] API.
55///
56/// For transaction, which has a locktime field, we implement a total ordering to make
57/// it easy to store transactions in sorted data structures, and use the locktime's 32-bit integer
58/// consensus encoding to order it.
59///
60/// # Relevant BIPs
61///
62/// * [BIP-0065 OP_CHECKLOCKTIMEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)
63/// * [BIP-0113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki)
64///
65/// # Examples
66///
67/// ```
68/// use bitcoin_units::absolute::{self, LockTime as L};
69/// # let n = absolute::LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY
70/// # let lock_time = absolute::LockTime::from_consensus(741521); // nLockTime
71/// // To compare absolute lock times there are various `is_satisfied_*` methods, you may also use:
72/// let _is_satisfied = match (n, lock_time) {
73/// (L::Blocks(n), L::Blocks(lock_time)) => n <= lock_time,
74/// (L::Seconds(n), L::Seconds(lock_time)) => n <= lock_time,
75/// _ => panic!("handle invalid comparison error"),
76/// };
77/// ```
78#[derive(Clone, Copy, PartialEq, Eq, Hash)]
79pub enum LockTime {
80 /// A block height lock time value.
81 ///
82 /// # Examples
83 ///
84 /// ```rust
85 /// use bitcoin_units::absolute;
86 ///
87 /// let block: u32 = 741521;
88 /// let n = absolute::LockTime::from_height(block).expect("valid height");
89 /// assert!(n.is_block_height());
90 /// assert_eq!(n.to_consensus_u32(), block);
91 /// ```
92 Blocks(Height),
93 /// A UNIX timestamp lock time value.
94 ///
95 /// # Examples
96 ///
97 /// ```rust
98 /// use bitcoin_units::absolute;
99 ///
100 /// let seconds: u32 = 1653195600; // May 22nd, 5am UTC.
101 /// let n = absolute::LockTime::from_mtp(seconds).expect("valid time");
102 /// assert!(n.is_block_time());
103 /// assert_eq!(n.to_consensus_u32(), seconds);
104 /// ```
105 Seconds(MedianTimePast),
106}
107
108impl LockTime {
109 /// If transaction lock time is set to zero it is ignored, in other words a
110 /// transaction with nLocktime==0 is able to be included immediately in any block.
111 pub const ZERO: Self = Self::Blocks(Height::ZERO);
112
113 /// The number of bytes that the locktime contributes to the size of a transaction.
114 pub const SIZE: usize = 4; // Serialized length of a u32.
115
116 /// Constructs a new `LockTime` from a prefixed hex string.
117 ///
118 /// # Errors
119 ///
120 /// If the input string is not a valid hex representation of a locktime or it does not include
121 /// the `0x` prefix.
122 ///
123 /// # Examples
124 ///
125 /// ```
126 /// # use bitcoin_units::{absolute, parse_int};
127 /// let hex_str = "0x61cf9980"; // Unix timestamp for January 1, 2022
128 /// let lock_time = absolute::LockTime::from_hex(hex_str)?;
129 /// assert_eq!(lock_time.to_consensus_u32(), 0x61cf9980);
130 ///
131 /// # Ok::<_, parse_int::PrefixedHexError>(())
132 /// ```
133 #[inline]
134 pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
135 let lock_time = parse_int::hex_u32_prefixed(s)?;
136 Ok(Self::from_consensus(lock_time))
137 }
138
139 /// Constructs a new `LockTime` from an unprefixed hex string.
140 ///
141 /// # Errors
142 ///
143 /// If the input string is not a valid hex representation of a locktime or if it includes the
144 /// `0x` prefix.
145 ///
146 /// # Examples
147 ///
148 /// ```
149 /// # use bitcoin_units::{absolute, parse_int};
150 /// let hex_str = "61cf9980"; // Unix timestamp for January 1, 2022
151 /// let lock_time = absolute::LockTime::from_unprefixed_hex(hex_str)?;
152 /// assert_eq!(lock_time.to_consensus_u32(), 0x61cf9980);
153 ///
154 /// # Ok::<_, parse_int::UnprefixedHexError>(())
155 /// ```
156 #[inline]
157 pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
158 let lock_time = parse_int::hex_u32_unprefixed(s)?;
159 Ok(Self::from_consensus(lock_time))
160 }
161
162 /// Constructs a new `LockTime` from an `nLockTime` value or the argument to `OP_CHECKLOCKTIMEVERIFY`.
163 ///
164 /// # Examples
165 ///
166 /// ```rust
167 /// # use bitcoin_units::absolute;
168 ///
169 /// // `from_consensus` roundtrips as expected with `to_consensus_u32`.
170 /// let n_lock_time: u32 = 741521;
171 /// let lock_time = absolute::LockTime::from_consensus(n_lock_time);
172 /// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
173 #[inline]
174 #[allow(clippy::missing_panics_doc)]
175 pub fn from_consensus(n: u32) -> Self {
176 if crate::locktime::absolute::is_block_height(n) {
177 Self::Blocks(Height::from_u32(n).expect("n is valid"))
178 } else {
179 Self::Seconds(MedianTimePast::from_u32(n).expect("n is valid"))
180 }
181 }
182
183 /// Constructs a new `LockTime` from `n`, expecting `n` to be a valid block height.
184 ///
185 /// # Note
186 ///
187 /// If the current block height is `h` and the locktime is set to `h`,
188 /// the transaction can be included in block `h+1` or later.
189 /// It is possible to broadcast the transaction at block height `h`.
190 ///
191 /// See [`LOCK_TIME_THRESHOLD`] for definition of a valid height value.
192 ///
193 /// # Errors
194 ///
195 /// If `n` does not represent a block height within the valid range for a locktime:
196 /// `[0, 499_999_999]`.
197 ///
198 /// # Examples
199 ///
200 /// ```rust
201 /// # use bitcoin_units::absolute;
202 /// assert!(absolute::LockTime::from_height(741521).is_ok());
203 /// assert!(absolute::LockTime::from_height(1653195600).is_err());
204 /// ```
205 #[inline]
206 pub fn from_height(n: u32) -> Result<Self, ConversionError> {
207 let height = Height::from_u32(n)?;
208 Ok(Self::Blocks(height))
209 }
210
211 /// Constructs a new `LockTime` from `n`, expecting `n` to be a median-time-past (MTP)
212 /// which is in range for a locktime.
213 ///
214 /// # Note
215 ///
216 /// If the locktime is set to an MTP `t`, the transaction can be included in a block only if
217 /// the MTP of last recent 11 blocks is greater than `t`.
218 ///
219 /// It is possible to broadcast the transaction once the MTP is greater than `t`. See BIP-0113.
220 ///
221 /// [BIP-0113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki)
222 ///
223 /// See [`LOCK_TIME_THRESHOLD`] for definition of a valid time value.
224 ///
225 /// # Errors
226 ///
227 /// If `n` is not in the allowable range of MTPs in a locktime: `[500_000_000, 2^32 - 1]`.
228 ///
229 /// # Examples
230 ///
231 /// ```rust
232 /// # use bitcoin_units::absolute;
233 /// assert!(absolute::LockTime::from_mtp(1653195600).is_ok());
234 /// assert!(absolute::LockTime::from_mtp(741521).is_err());
235 /// ```
236 #[inline]
237 pub fn from_mtp(n: u32) -> Result<Self, ConversionError> {
238 let time = MedianTimePast::from_u32(n)?;
239 Ok(Self::Seconds(time))
240 }
241
242 /// Returns true if both lock times use the same unit i.e., both height based or both time based.
243 #[inline]
244 pub const fn is_same_unit(self, other: Self) -> bool {
245 matches!(
246 (self, other),
247 (Self::Blocks(_), Self::Blocks(_)) | (Self::Seconds(_), Self::Seconds(_))
248 )
249 }
250
251 /// Returns true if this lock time value is a block height.
252 #[inline]
253 pub const fn is_block_height(self) -> bool { matches!(self, Self::Blocks(_)) }
254
255 /// Returns true if this lock time value is a block time (UNIX timestamp).
256 #[inline]
257 pub const fn is_block_time(self) -> bool { !self.is_block_height() }
258
259 /// Returns true if this timelock constraint is satisfied by the respective `height`/`time`.
260 ///
261 /// If `self` is a blockheight based lock then it is checked against `height` and if `self` is a
262 /// blocktime based lock it is checked against `time`.
263 ///
264 /// A 'timelock constraint' refers to the `n` from `n OP_CHECKLOCKTIMEVERIFY`, this constraint
265 /// is satisfied if a transaction with `nLockTime` set to `height`/`time` is valid.
266 ///
267 /// If `height` and `mtp` represent the current chain tip then a transaction with this
268 /// locktime can be broadcast for inclusion in the next block.
269 ///
270 /// If you do not have, or do not wish to calculate, both parameters consider using:
271 ///
272 /// * [`is_satisfied_by_height()`](absolute::LockTime::is_satisfied_by_height)
273 /// * [`is_satisfied_by_time()`](absolute::LockTime::is_satisfied_by_time)
274 ///
275 /// # Examples
276 ///
277 /// ```no_run
278 /// # use bitcoin_units::absolute;
279 /// // Can be implemented if block chain data is available.
280 /// fn get_height() -> absolute::Height { todo!("return the current block height") }
281 /// fn get_time() -> absolute::MedianTimePast { todo!("return the current block MTP") }
282 ///
283 /// let n = absolute::LockTime::from_consensus(741521); // `n OP_CHECKLOCKTIMEVERIFY`.
284 /// if n.is_satisfied_by(get_height(), get_time()) {
285 /// // Can create and mine a transaction that satisfies the OP_CLTV timelock constraint.
286 /// }
287 /// ````
288 #[inline]
289 pub fn is_satisfied_by(self, height: Height, mtp: MedianTimePast) -> bool {
290 match self {
291 Self::Blocks(blocks) => blocks.is_satisfied_by(height),
292 Self::Seconds(time) => time.is_satisfied_by(mtp),
293 }
294 }
295
296 /// Returns true if a transaction with this locktime can be spent in the next block.
297 ///
298 /// If `height` is the current block height of the chain then a transaction with this locktime
299 /// can be broadcast for inclusion in the next block.
300 ///
301 /// # Errors
302 ///
303 /// Returns an error if this lock is not lock-by-height.
304 #[inline]
305 pub fn is_satisfied_by_height(self, height: Height) -> Result<bool, IncompatibleHeightError> {
306 match self {
307 Self::Blocks(blocks) => Ok(blocks.is_satisfied_by(height)),
308 Self::Seconds(time) =>
309 Err(IncompatibleHeightError { lock: time, incompatible: height }),
310 }
311 }
312
313 /// Returns true if a transaction with this locktime can be included in the next block.
314 ///
315 /// # Errors
316 ///
317 /// Returns an error if this lock is not lock-by-time.
318 #[inline]
319 pub fn is_satisfied_by_time(self, mtp: MedianTimePast) -> Result<bool, IncompatibleTimeError> {
320 match self {
321 Self::Seconds(time) => Ok(time.is_satisfied_by(mtp)),
322 Self::Blocks(blocks) => Err(IncompatibleTimeError { lock: blocks, incompatible: mtp }),
323 }
324 }
325
326 /// Returns true if satisfaction of `other` lock time implies satisfaction of this
327 /// [`absolute::LockTime`].
328 ///
329 /// A lock time can only be satisfied by n blocks being mined or n seconds passing. If you have
330 /// two lock times (same unit) then the larger lock time being satisfied implies (in a
331 /// mathematical sense) the smaller one being satisfied.
332 ///
333 /// This function serves multiple purposes:
334 ///
335 /// * When evaluating `OP_CHECKLOCKTIMEVERIFY` the argument must be less than or equal to the
336 /// transactions nLockTime. If using this function to validate a script `self` is the argument
337 /// to `CLTV` and `other` is the transaction nLockTime.
338 ///
339 /// * If you wish to check a lock time against various other locks e.g., filtering out locks
340 /// which cannot be satisfied. Can also be used to remove the smaller value of two
341 /// `OP_CHECKLOCKTIMEVERIFY` operations within one branch of the script.
342 ///
343 /// # Examples
344 ///
345 /// ```rust
346 /// # use bitcoin_units::absolute;
347 /// let lock_time = absolute::LockTime::from_consensus(741521);
348 /// let check = absolute::LockTime::from_consensus(741521 + 1);
349 /// assert!(lock_time.is_implied_by(check));
350 /// ```
351 #[inline]
352 pub fn is_implied_by(self, other: Self) -> bool {
353 match (self, other) {
354 (Self::Blocks(this), Self::Blocks(other)) => this <= other,
355 (Self::Seconds(this), Self::Seconds(other)) => this <= other,
356 _ => false, // Not the same units.
357 }
358 }
359
360 /// Returns the inner `u32` value. This is the value used when creating this `LockTime`
361 /// i.e., `n OP_CHECKLOCKTIMEVERIFY` or `nLockTime`.
362 ///
363 /// # Warning
364 ///
365 /// Do not compare values return by this method. The whole point of the `LockTime` type is to
366 /// assist in doing correct comparisons. Either use `is_satisfied_by`, `is_satisfied_by_lock`,
367 /// or use the pattern below:
368 ///
369 /// # Examples
370 ///
371 /// ```rust
372 /// use bitcoin_units::absolute::{self, LockTime as L};
373 /// # let n = absolute::LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY
374 /// # let lock_time = absolute::LockTime::from_consensus(741521 + 1); // nLockTime
375 ///
376 /// let _is_satisfied = match (n, lock_time) {
377 /// (L::Blocks(n), L::Blocks(lock_time)) => n <= lock_time,
378 /// (L::Seconds(n), L::Seconds(lock_time)) => n <= lock_time,
379 /// _ => panic!("invalid comparison"),
380 /// };
381 ///
382 /// // Or, if you have Rust 1.53 or greater
383 /// // let is_satisfied = n.partial_cmp(&lock_time).expect("invalid comparison").is_le();
384 /// ```
385 #[inline]
386 pub fn to_consensus_u32(self) -> u32 {
387 match self {
388 Self::Blocks(ref h) => h.to_u32(),
389 Self::Seconds(ref t) => t.to_u32(),
390 }
391 }
392}
393
394parse_int::impl_parse_str_from_int_infallible!(LockTime, u32, from_consensus);
395
396#[cfg(feature = "encoding")]
397impl encoding::Encode for LockTime {
398 type Encoder<'e> = LockTimeEncoder<'e>;
399 #[inline]
400 fn encoder(&self) -> Self::Encoder<'_> {
401 LockTimeEncoder::new(encoding::ArrayEncoder::without_length_prefix(
402 self.to_consensus_u32().to_le_bytes(),
403 ))
404 }
405}
406
407#[cfg(feature = "encoding")]
408impl encoding::Decode for LockTime {
409 type Decoder = LockTimeDecoder;
410}
411
412#[cfg(feature = "encoding")]
413encoding::encoder_newtype_exact! {
414 /// The encoder for the [`LockTime`] type.
415 #[derive(Debug, Clone)]
416 pub struct LockTimeEncoder<'e>(encoding::ArrayEncoder<4>);
417}
418
419#[cfg(feature = "encoding")]
420crate::decoder_newtype! {
421 /// The decoder for the [`LockTime`] type.
422 #[derive(Debug, Clone)]
423 pub struct LockTimeDecoder(encoding::ArrayDecoder<4>);
424
425 /// Constructs a new [`LockTime`] decoder.
426 pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
427
428 fn end(result: Result<[u8; 4], encoding::UnexpectedEofError>) -> Result<LockTime, LockTimeDecoderError> {
429 let value = result.map_err(LockTimeDecoderError)?;
430 let n = u32::from_le_bytes(value);
431 Ok(LockTime::from_consensus(n))
432 }
433}
434
435impl From<Height> for LockTime {
436 #[inline]
437 fn from(h: Height) -> Self { Self::Blocks(h) }
438}
439
440impl From<MedianTimePast> for LockTime {
441 #[inline]
442 fn from(t: MedianTimePast) -> Self { Self::Seconds(t) }
443}
444
445impl fmt::Debug for LockTime {
446 #[inline]
447 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
448 match *self {
449 Self::Blocks(ref h) => write!(f, "{} blocks", h),
450 Self::Seconds(ref t) => write!(f, "{} seconds", t),
451 }
452 }
453}
454
455impl fmt::Display for LockTime {
456 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
457 if f.alternate() {
458 match *self {
459 Self::Blocks(ref h) => write!(f, "block-height {}", h),
460 Self::Seconds(ref t) => write!(f, "block-time {} (seconds since epoch)", t),
461 }
462 } else {
463 match *self {
464 Self::Blocks(ref h) => fmt::Display::fmt(h, f),
465 Self::Seconds(ref t) => fmt::Display::fmt(t, f),
466 }
467 }
468 }
469}
470
471#[cfg(feature = "serde")]
472impl serde::Serialize for LockTime {
473 #[inline]
474 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
475 where
476 S: serde::Serializer,
477 {
478 self.to_consensus_u32().serialize(serializer)
479 }
480}
481
482#[cfg(feature = "serde")]
483impl<'de> serde::Deserialize<'de> for LockTime {
484 #[inline]
485 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
486 where
487 D: serde::Deserializer<'de>,
488 {
489 u32::deserialize(deserializer).map(Self::from_consensus)
490 }
491}
492
493/// An absolute block height, guaranteed to always contain a valid height value.
494#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
495pub struct Height(u32);
496
497impl Height {
498 /// Absolute block height 0, the genesis block.
499 pub const ZERO: Self = Self(0);
500
501 /// The minimum absolute block height (0), the genesis block.
502 pub const MIN: Self = Self::ZERO;
503
504 /// The maximum absolute block height.
505 pub const MAX: Self = Self(LOCK_TIME_THRESHOLD - 1);
506
507 /// Constructs a new [`Height`] from a prefixed hex string.
508 ///
509 /// # Errors
510 ///
511 /// If the input string is not a valid hex representation of a block height or it does not
512 /// include the `0x` prefix.
513 #[inline]
514 pub fn from_hex(s: &str) -> Result<Self, ParseHeightError> {
515 let height = parse_int::hex_u32_prefixed(s).map_err(ParseError::PrefixedHex)?;
516 Ok(Self::from_u32(height).map_err(|_| ParseError::Conversion(height.into()))?)
517 }
518
519 /// Constructs a new [`Height`] from an unprefixed hex string.
520 ///
521 /// # Errors
522 ///
523 /// If the input string is not a valid hex representation of a block height or if it
524 /// includes the `0x` prefix.
525 #[inline]
526 pub fn from_unprefixed_hex(s: &str) -> Result<Self, ParseHeightError> {
527 let height = parse_int::hex_u32_unprefixed(s).map_err(ParseError::UnprefixedHex)?;
528 Ok(Self::from_u32(height).map_err(|_| ParseError::Conversion(height.into()))?)
529 }
530
531 /// Constructs a new block height directly from a `u32` value.
532 ///
533 /// # Errors
534 ///
535 /// If `n` does not represent a block height within the valid range for a locktime:
536 /// `[0, 499_999_999]`.
537 ///
538 /// # Examples
539 ///
540 /// ```rust
541 /// use bitcoin_units::locktime::absolute;
542 ///
543 /// let h: u32 = 741521;
544 /// let height = absolute::Height::from_u32(h)?;
545 /// assert_eq!(height.to_u32(), h);
546 /// # Ok::<_, absolute::error::ConversionError>(())
547 /// ```
548 #[inline]
549 pub const fn from_u32(n: u32) -> Result<Self, ConversionError> {
550 if is_block_height(n) {
551 Ok(Self(n))
552 } else {
553 Err(ConversionError::invalid_height(n))
554 }
555 }
556
557 /// Converts this [`Height`] to a raw `u32` value.
558 ///
559 /// # Examples
560 ///
561 /// ```rust
562 /// use bitcoin_units::locktime::absolute;
563 ///
564 /// assert_eq!(absolute::Height::MAX.to_u32(), 499_999_999);
565 /// ```
566 #[inline]
567 pub const fn to_u32(self) -> u32 { self.0 }
568
569 /// Returns true if a transaction with this locktime can be included in the next block.
570 ///
571 /// `self` is value of the `LockTime` and if `height` is the current chain tip then
572 /// a transaction with this lock can be broadcast for inclusion in the next block.
573 #[inline]
574 pub fn is_satisfied_by(self, height: Self) -> bool {
575 // Use u64 so that there can be no overflow.
576 let next_block_height = u64::from(height.to_u32()) + 1;
577 u64::from(self.to_u32()) <= next_block_height
578 }
579}
580
581crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(Height);
582
583impl fmt::Display for Height {
584 #[inline]
585 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
586}
587
588parse_int::impl_parse_str!(Height, ParseHeightError, parser(Height::from_u32));
589
590/// The median timestamp of 11 consecutive blocks, representing "the timestamp" of the
591/// final block for locktime-checking purposes.
592///
593/// Time-based locktimes are not measured against the timestamps in individual block
594/// headers, since these are not monotone and may be subject to miner manipulation.
595/// Instead, locktimes use the "median-time-past" (MTP) of the most recent 11 blocks,
596/// a quantity which is required by consensus to be monotone and which is difficult
597/// for any individual miner to manipulate.
598#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
599pub struct MedianTimePast(u32);
600
601impl MedianTimePast {
602 /// The minimum MTP allowable in a locktime (Tue Nov 05 1985 00:53:20 GMT+0000).
603 pub const MIN: Self = Self(LOCK_TIME_THRESHOLD);
604
605 /// The maximum MTP allowable in a locktime (Sun Feb 07 2106 06:28:15 GMT+0000).
606 pub const MAX: Self = Self(u32::MAX);
607
608 /// Constructs an [`MedianTimePast`] by computing the median-time-past from the last
609 /// 11 block timestamps.
610 ///
611 /// Because block timestamps are not monotonic, this function internally sorts them;
612 /// it is therefore not important what order they appear in the array; use whatever
613 /// is most convenient.
614 ///
615 /// # Errors
616 ///
617 /// If the median block timestamp is not in the allowable range of MTPs in a
618 /// locktime: `[500_000_000, 2^32 - 1]`. Because there is a consensus rule that MTP
619 /// be monotonically increasing, and the MTP of the first 11 blocks exceeds `500_000_000`
620 /// for every real-life chain, this error typically cannot be hit in practice.
621 #[inline]
622 pub fn new(timestamps: [crate::BlockTime; 11]) -> Result<Self, ConversionError> {
623 crate::BlockMtp::new(timestamps).try_into()
624 }
625
626 /// Constructs a new [`MedianTimePast`] from a prefixed hex string.
627 ///
628 /// # Errors
629 ///
630 /// If the input string is not a valid hex representation of a block time or it does not
631 /// include the `0x` prefix.
632 #[inline]
633 pub fn from_hex(s: &str) -> Result<Self, ParseTimeError> {
634 let height = parse_int::hex_u32_prefixed(s).map_err(ParseError::PrefixedHex)?;
635 Ok(Self::from_u32(height).map_err(|_| ParseError::Conversion(height.into()))?)
636 }
637
638 /// Constructs a new [`MedianTimePast`] from an unprefixed hex string.
639 ///
640 /// # Errors
641 ///
642 /// If the input string is not a valid hex representation of a block time or if it
643 /// includes the `0x` prefix.
644 #[inline]
645 pub fn from_unprefixed_hex(s: &str) -> Result<Self, ParseTimeError> {
646 let height = parse_int::hex_u32_unprefixed(s).map_err(ParseError::UnprefixedHex)?;
647 Ok(Self::from_u32(height).map_err(|_| ParseError::Conversion(height.into()))?)
648 }
649
650 /// Constructs a new MTP directly from a `u32` value.
651 ///
652 /// This function, with [`MedianTimePast::to_u32`], is used to obtain a raw MTP value. It is
653 /// **not** used to convert to or from a block timestamp, which is not a MTP.
654 ///
655 /// # Errors
656 ///
657 /// If `n` is not in the allowable range of MTPs in a locktime: `[500_000_000, 2^32 - 1]`.
658 ///
659 /// # Examples
660 ///
661 /// ```rust
662 /// use bitcoin_units::locktime::absolute;
663 ///
664 /// let t: u32 = 1653195600; // May 22nd, 5am UTC.
665 /// let time = absolute::MedianTimePast::from_u32(t)?;
666 /// assert_eq!(time.to_u32(), t);
667 /// # Ok::<_, absolute::error::ConversionError>(())
668 /// ```
669 #[inline]
670 pub const fn from_u32(n: u32) -> Result<Self, ConversionError> {
671 if is_block_time(n) {
672 Ok(Self(n))
673 } else {
674 Err(ConversionError::invalid_time(n))
675 }
676 }
677
678 /// Converts this [`MedianTimePast`] to a raw `u32` value.
679 ///
680 /// # Examples
681 ///
682 /// ```rust
683 /// use bitcoin_units::locktime::absolute;
684 ///
685 /// assert_eq!(absolute::MedianTimePast::MIN.to_u32(), 500_000_000);
686 /// ```
687 #[inline]
688 pub const fn to_u32(self) -> u32 { self.0 }
689
690 /// Returns true if a transaction with this locktime can be included in the next block.
691 ///
692 /// `self` is the value of the `LockTime` and if `time` is the median time past of the block at
693 /// the chain tip then a transaction with this lock can be broadcast for inclusion in the next
694 /// block.
695 #[inline]
696 pub fn is_satisfied_by(self, time: Self) -> bool {
697 // The locktime check in Core during block validation uses the MTP
698 // of the previous block - which is the expected to be `time` here.
699 self <= time
700 }
701}
702
703crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(MedianTimePast);
704
705impl fmt::Display for MedianTimePast {
706 #[inline]
707 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
708}
709
710parse_int::impl_parse_str!(MedianTimePast, ParseTimeError, parser(MedianTimePast::from_u32));
711
712fn parser<T, E, S, F>(f: F) -> impl FnOnce(S) -> Result<T, E>
713where
714 E: From<ParseError>,
715 S: AsRef<str> + Into<InputString>,
716 F: FnOnce(u32) -> Result<T, ConversionError>,
717{
718 move |s| {
719 let n = s.as_ref().parse::<i64>().map_err(ParseError::invalid_int(s))?;
720 let n = u32::try_from(n).map_err(|_| ParseError::Conversion(n))?;
721 f(n).map_err(ParseError::from).map_err(Into::into)
722 }
723}
724
725/// Returns true if `n` is a block height i.e., less than 500,000,000.
726#[inline]
727pub const fn is_block_height(n: u32) -> bool { n < LOCK_TIME_THRESHOLD }
728
729/// Returns true if `n` is a UNIX timestamp i.e., greater than or equal to 500,000,000.
730#[inline]
731pub const fn is_block_time(n: u32) -> bool { n >= LOCK_TIME_THRESHOLD }
732
733#[cfg(feature = "arbitrary")]
734impl<'a> Arbitrary<'a> for LockTime {
735 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
736 let l = u32::arbitrary(u)?;
737 Ok(Self::from_consensus(l))
738 }
739}
740
741#[cfg(feature = "arbitrary")]
742impl<'a> Arbitrary<'a> for Height {
743 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
744 let choice = u.int_in_range(0..=2)?;
745 match choice {
746 0 => Ok(Self::MIN),
747 1 => Ok(Self::MAX),
748 _ => {
749 let min = Self::MIN.to_u32();
750 let max = Self::MAX.to_u32();
751 let h = u.int_in_range(min..=max)?;
752 Ok(Self::from_u32(h).unwrap())
753 }
754 }
755 }
756}
757
758#[cfg(feature = "arbitrary")]
759impl<'a> Arbitrary<'a> for MedianTimePast {
760 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
761 let choice = u.int_in_range(0..=2)?;
762 match choice {
763 0 => Ok(Self::MIN),
764 1 => Ok(Self::MAX),
765 _ => {
766 let min = Self::MIN.to_u32();
767 let max = Self::MAX.to_u32();
768 let t = u.int_in_range(min..=max)?;
769 Ok(Self::from_u32(t).unwrap())
770 }
771 }
772 }
773}
774
775#[cfg(test)]
776mod tests {
777 #[cfg(feature = "alloc")]
778 use alloc::{boxed::Box, format, string::String};
779
780 use super::*;
781
782 #[test]
783 #[cfg(feature = "alloc")]
784 fn display_and_alternate() {
785 let lock_by_height = LockTime::from_height(741_521).unwrap();
786 let lock_by_time = LockTime::from_mtp(1_653_195_600).unwrap(); // May 22nd 2022, 5am UTC.
787
788 assert_eq!(format!("{}", lock_by_height), "741521");
789 assert_eq!(format!("{:#}", lock_by_height), "block-height 741521");
790 assert!(!format!("{:?}", lock_by_height).is_empty());
791
792 assert_eq!(format!("{}", lock_by_time), "1653195600");
793 assert_eq!(format!("{:#}", lock_by_time), "block-time 1653195600 (seconds since epoch)");
794 assert!(!format!("{:?}", lock_by_time).is_empty());
795 }
796
797 #[test]
798 fn roundtrips() {
799 let lock_by_height = LockTime::from_consensus(741_521);
800 let lock_by_time = LockTime::from_consensus(1_653_195_600);
801
802 assert_eq!(lock_by_height.to_consensus_u32(), 741_521);
803 assert_eq!(lock_by_time.to_consensus_u32(), 1_653_195_600);
804 }
805
806 #[test]
807 fn lock_time_from_hex_lower() {
808 let lock_by_time = LockTime::from_hex("0x6289c350").unwrap();
809 assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
810 }
811
812 #[test]
813 fn lock_time_from_hex_upper() {
814 let lock_by_time = LockTime::from_hex("0X6289C350").unwrap();
815 assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
816 }
817
818 #[test]
819 fn lock_time_from_unprefixed_hex_lower() {
820 let lock_by_time = LockTime::from_unprefixed_hex("6289c350").unwrap();
821 assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
822 }
823
824 #[test]
825 fn lock_time_from_unprefixed_hex_upper() {
826 let lock_by_time = LockTime::from_unprefixed_hex("6289C350").unwrap();
827 assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
828 }
829
830 #[test]
831 fn invalid_hex() {
832 assert!(LockTime::from_hex("0xzb93").is_err());
833 assert!(LockTime::from_unprefixed_hex("zb93").is_err());
834 }
835
836 #[test]
837 fn invalid_locktime_type() {
838 assert!(LockTime::from_height(499_999_999).is_ok()); // Below the threshold.
839 assert!(LockTime::from_height(500_000_000).is_err()); // The threshold.
840 assert!(LockTime::from_height(500_000_001).is_err()); // Above the threshold.
841
842 assert!(LockTime::from_mtp(499_999_999).is_err()); // Below the threshold.
843 assert!(LockTime::from_mtp(500_000_000).is_ok()); // The threshold.
844 assert!(LockTime::from_mtp(500_000_001).is_ok()); // Above the threshold.
845 }
846
847 #[test]
848 fn parses_correctly_to_height_or_time() {
849 let lock_by_height = LockTime::from_consensus(750_000);
850
851 assert!(lock_by_height.is_block_height());
852 assert!(!lock_by_height.is_block_time());
853
854 let t: u32 = 1_653_195_600; // May 22nd, 5am UTC.
855 let lock_by_time = LockTime::from_consensus(t);
856
857 assert!(!lock_by_time.is_block_height());
858 assert!(lock_by_time.is_block_time());
859
860 // Test is_same_unit() logic
861 assert!(lock_by_height.is_same_unit(LockTime::from_consensus(800_000)));
862 assert!(!lock_by_height.is_same_unit(lock_by_time));
863 assert!(lock_by_time.is_same_unit(LockTime::from_consensus(1_653_282_000)));
864 assert!(!lock_by_time.is_same_unit(lock_by_height));
865 }
866
867 #[test]
868 fn satisfied_by_height() {
869 let height_below = Height::from_u32(700_000).unwrap();
870 let height = Height::from_u32(750_000).unwrap();
871 let height_above = Height::from_u32(800_000).unwrap();
872
873 let lock_by_height = LockTime::from(height);
874
875 let t: u32 = 1_653_195_600; // May 22nd, 5am UTC.
876 let time = MedianTimePast::from_u32(t).unwrap();
877
878 assert!(!lock_by_height.is_satisfied_by(height_below, time));
879 assert!(lock_by_height.is_satisfied_by(height, time));
880 assert!(lock_by_height.is_satisfied_by(height_above, time));
881 }
882
883 #[test]
884 fn satisfied_by_time() {
885 let time_before = MedianTimePast::from_u32(1_653_109_200).unwrap(); // "May 21th 2022, 5am UTC.
886 let time = MedianTimePast::from_u32(1_653_195_600).unwrap(); // "May 22nd 2022, 5am UTC.
887 let time_after = MedianTimePast::from_u32(1_653_282_000).unwrap(); // "May 23rd 2022, 5am UTC.
888
889 let lock_by_time = LockTime::from(time);
890
891 let height = Height::from_u32(800_000).unwrap();
892
893 assert!(!lock_by_time.is_satisfied_by(height, time_before));
894 assert!(lock_by_time.is_satisfied_by(height, time));
895 assert!(lock_by_time.is_satisfied_by(height, time_after));
896 }
897
898 #[test]
899 fn height_correctly_implies() {
900 let lock_by_height = LockTime::from_consensus(750_005);
901
902 assert!(!lock_by_height.is_implied_by(LockTime::from_consensus(750_004)));
903 assert!(lock_by_height.is_implied_by(LockTime::from_consensus(750_005)));
904 assert!(lock_by_height.is_implied_by(LockTime::from_consensus(750_006)));
905 }
906
907 #[test]
908 fn time_correctly_implies() {
909 let t: u32 = 1_700_000_005;
910 let lock_by_time = LockTime::from_consensus(t);
911
912 assert!(!lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_004)));
913 assert!(lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_005)));
914 assert!(lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_006)));
915 }
916
917 #[test]
918 fn incorrect_units_do_not_imply() {
919 let lock_by_height = LockTime::from_consensus(750_005);
920 assert!(!lock_by_height.is_implied_by(LockTime::from_consensus(1_700_000_004)));
921 }
922
923 #[test]
924 fn time_from_str_hex_happy_path() {
925 let actual = MedianTimePast::from_hex("0x6289C350").unwrap();
926 let expected = MedianTimePast::from_u32(0x6289_C350).unwrap();
927 assert_eq!(actual, expected);
928 }
929
930 #[test]
931 fn time_from_str_hex_no_prefix_happy_path() {
932 let time = MedianTimePast::from_unprefixed_hex("6289C350").unwrap();
933 assert_eq!(time, MedianTimePast(0x6289_C350));
934 }
935
936 #[test]
937 fn time_from_str_hex_invalid_hex_should_err() {
938 let hex = "0xzb93";
939 let result = MedianTimePast::from_hex(hex);
940 assert!(result.is_err());
941 }
942
943 #[test]
944 fn height_from_str_hex_happy_path() {
945 let actual = Height::from_hex("0xBA70D").unwrap();
946 let expected = Height(0xBA70D);
947 assert_eq!(actual, expected);
948 }
949
950 #[test]
951 fn height_from_str_hex_no_prefix_happy_path() {
952 let height = Height::from_unprefixed_hex("BA70D").unwrap();
953 assert_eq!(height, Height(0xBA70D));
954 }
955
956 #[test]
957 fn height_from_str_hex_invalid_hex_should_err() {
958 let hex = "0xzb93";
959 let result = Height::from_hex(hex);
960 assert!(result.is_err());
961 }
962
963 #[test]
964 fn height_try_from_stringlike_happy_path() {
965 let want = Height::from_u32(10).unwrap();
966 assert_eq!("10".parse::<Height>().unwrap(), want);
967 assert_eq!(Height::try_from("10").unwrap(), want);
968 #[cfg(feature = "alloc")]
969 {
970 assert_eq!(Height::try_from(String::from("10")).unwrap(), want);
971 assert_eq!(Height::try_from(Box::<str>::from("10")).unwrap(), want);
972 }
973 }
974
975 #[test]
976 fn height_try_from_stringlike_hex_error_path() {
977 // Only base-10 values should parse
978 assert!("0xab".parse::<Height>().is_err());
979 assert!(Height::try_from("0xab").is_err());
980 #[cfg(feature = "alloc")]
981 {
982 assert!(Height::try_from(String::from("0xab")).is_err());
983 assert!(Height::try_from(Box::<str>::from("0xab")).is_err());
984 }
985 }
986
987 #[test]
988 fn height_try_from_stringlike_decimal_error_path() {
989 // Only integers should parse
990 assert!("10.123".parse::<Height>().is_err());
991 assert!(Height::try_from("10.123").is_err());
992 #[cfg(feature = "alloc")]
993 {
994 assert!(Height::try_from(String::from("10.123")).is_err());
995 assert!(Height::try_from(Box::<str>::from("10.123")).is_err());
996 }
997 }
998
999 #[test]
1000 fn is_block_height_or_time() {
1001 assert!(is_block_height(499_999_999));
1002 assert!(!is_block_height(500_000_000));
1003
1004 assert!(!is_block_time(499_999_999));
1005 assert!(is_block_time(500_000_000));
1006 }
1007
1008 #[test]
1009 fn valid_chain_computes_mtp() {
1010 use crate::BlockTime;
1011
1012 let mut timestamps = [
1013 BlockTime::from_u32(500_000_010),
1014 BlockTime::from_u32(500_000_003),
1015 BlockTime::from_u32(500_000_005),
1016 BlockTime::from_u32(500_000_008),
1017 BlockTime::from_u32(500_000_001),
1018 BlockTime::from_u32(500_000_004),
1019 BlockTime::from_u32(500_000_006),
1020 BlockTime::from_u32(500_000_009),
1021 BlockTime::from_u32(500_000_002),
1022 BlockTime::from_u32(500_000_007),
1023 BlockTime::from_u32(500_000_000),
1024 ];
1025
1026 // Try various reorderings
1027 assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1028 timestamps.reverse();
1029 assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1030 timestamps.sort();
1031 assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1032 timestamps.reverse();
1033 assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1034 }
1035
1036 #[test]
1037 fn height_is_satisfied_by() {
1038 let chain_tip = Height::from_u32(100).unwrap();
1039
1040 // lock is satisfied if transaction can go in the next block (height <= chain_tip + 1).
1041 let locktime = Height::from_u32(100).unwrap();
1042 assert!(locktime.is_satisfied_by(chain_tip));
1043 let locktime = Height::from_u32(101).unwrap();
1044 assert!(locktime.is_satisfied_by(chain_tip));
1045
1046 // It is not satisfied if the lock height is after the next block.
1047 let locktime = Height::from_u32(102).unwrap();
1048 assert!(!locktime.is_satisfied_by(chain_tip));
1049 }
1050
1051 #[test]
1052 fn median_time_past_is_satisfied_by() {
1053 let mtp = MedianTimePast::from_u32(500_000_001).unwrap();
1054
1055 // lock is satisfied if transaction can go in the next block (locktime <= mtp).
1056 let locktime = MedianTimePast::from_u32(500_000_000).unwrap();
1057 assert!(locktime.is_satisfied_by(mtp));
1058 let locktime = MedianTimePast::from_u32(500_000_001).unwrap();
1059 assert!(locktime.is_satisfied_by(mtp));
1060
1061 // It is not satisfied if the lock time is after the median time past.
1062 let locktime = MedianTimePast::from_u32(500_000_002).unwrap();
1063 assert!(!locktime.is_satisfied_by(mtp));
1064 }
1065}