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