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