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")]
401encoding::encoder_newtype_exact! {
402 /// The encoder for the [`LockTime`] type.
403 pub struct LockTimeEncoder<'e>(encoding::ArrayEncoder<4>);
404}
405
406#[cfg(feature = "encoding")]
407impl encoding::Encodable for LockTime {
408 type Encoder<'e> = LockTimeEncoder<'e>;
409 fn encoder(&self) -> Self::Encoder<'_> {
410 LockTimeEncoder::new(encoding::ArrayEncoder::without_length_prefix(
411 self.to_consensus_u32().to_le_bytes(),
412 ))
413 }
414}
415
416/// The decoder for the [`LockTime`] type.
417#[cfg(feature = "encoding")]
418pub struct LockTimeDecoder(encoding::ArrayDecoder<4>);
419
420#[cfg(feature = "encoding")]
421impl LockTimeDecoder {
422 /// Constructs a new [`LockTime`] decoder.
423 pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
424}
425
426#[cfg(feature = "encoding")]
427impl Default for LockTimeDecoder {
428 fn default() -> Self { Self::new() }
429}
430
431#[cfg(feature = "encoding")]
432impl encoding::Decoder for LockTimeDecoder {
433 type Output = LockTime;
434 type Error = LockTimeDecoderError;
435
436 #[inline]
437 fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
438 Ok(self.0.push_bytes(bytes).map_err(LockTimeDecoderError)?)
439 }
440
441 #[inline]
442 fn end(self) -> Result<Self::Output, Self::Error> {
443 let n = u32::from_le_bytes(self.0.end().map_err(LockTimeDecoderError)?);
444 Ok(LockTime::from_consensus(n))
445 }
446
447 #[inline]
448 fn read_limit(&self) -> usize { self.0.read_limit() }
449}
450
451#[cfg(feature = "encoding")]
452impl encoding::Decodable for LockTime {
453 type Decoder = LockTimeDecoder;
454 fn decoder() -> Self::Decoder { LockTimeDecoder(encoding::ArrayDecoder::<4>::new()) }
455}
456
457impl From<Height> for LockTime {
458 #[inline]
459 fn from(h: Height) -> Self { Self::Blocks(h) }
460}
461
462impl From<MedianTimePast> for LockTime {
463 #[inline]
464 fn from(t: MedianTimePast) -> Self { Self::Seconds(t) }
465}
466
467impl fmt::Debug for LockTime {
468 #[inline]
469 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
470 match *self {
471 Self::Blocks(ref h) => write!(f, "{} blocks", h),
472 Self::Seconds(ref t) => write!(f, "{} seconds", t),
473 }
474 }
475}
476
477impl fmt::Display for LockTime {
478 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
479 if f.alternate() {
480 match *self {
481 Self::Blocks(ref h) => write!(f, "block-height {}", h),
482 Self::Seconds(ref t) => write!(f, "block-time {} (seconds since epoch)", t),
483 }
484 } else {
485 match *self {
486 Self::Blocks(ref h) => fmt::Display::fmt(h, f),
487 Self::Seconds(ref t) => fmt::Display::fmt(t, f),
488 }
489 }
490 }
491}
492
493#[cfg(feature = "serde")]
494impl serde::Serialize for LockTime {
495 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
496 where
497 S: serde::Serializer,
498 {
499 self.to_consensus_u32().serialize(serializer)
500 }
501}
502
503#[cfg(feature = "serde")]
504impl<'de> serde::Deserialize<'de> for LockTime {
505 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
506 where
507 D: serde::Deserializer<'de>,
508 {
509 u32::deserialize(deserializer).map(Self::from_consensus)
510 }
511}
512
513/// An absolute block height, guaranteed to always contain a valid height value.
514#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
515pub struct Height(u32);
516
517impl Height {
518 /// Absolute block height 0, the genesis block.
519 pub const ZERO: Self = Self(0);
520
521 /// The minimum absolute block height (0), the genesis block.
522 pub const MIN: Self = Self::ZERO;
523
524 /// The maximum absolute block height.
525 pub const MAX: Self = Self(LOCK_TIME_THRESHOLD - 1);
526
527 /// Constructs a new [`Height`] from a hex string.
528 ///
529 /// The input string may or may not contain a typical hex prefix e.g., `0x`.
530 ///
531 /// # Errors
532 ///
533 /// If the input string is not a valid hex representation of a block height.
534 pub fn from_hex(s: &str) -> Result<Self, ParseHeightError> { parse_hex(s, Self::from_u32) }
535
536 #[deprecated(since = "1.0.0-rc.0", note = "use `from_u32` instead")]
537 #[doc(hidden)]
538 pub const fn from_consensus(n: u32) -> Result<Self, ConversionError> { Self::from_u32(n) }
539
540 #[deprecated(since = "1.0.0-rc.0", note = "use `to_u32` instead")]
541 #[doc(hidden)]
542 pub const fn to_consensus_u32(self) -> u32 { self.to_u32() }
543
544 /// Constructs a new block height directly from a `u32` value.
545 ///
546 /// # Errors
547 ///
548 /// If `n` does not represent a block height within the valid range for a locktime:
549 /// `[0, 499_999_999]`.
550 ///
551 /// # Examples
552 ///
553 /// ```rust
554 /// use bitcoin_units::locktime::absolute;
555 ///
556 /// let h: u32 = 741521;
557 /// let height = absolute::Height::from_u32(h)?;
558 /// assert_eq!(height.to_u32(), h);
559 /// # Ok::<_, absolute::error::ConversionError>(())
560 /// ```
561 #[inline]
562 pub const fn from_u32(n: u32) -> Result<Self, ConversionError> {
563 if is_block_height(n) {
564 Ok(Self(n))
565 } else {
566 Err(ConversionError::invalid_height(n))
567 }
568 }
569
570 /// Converts this [`Height`] to a raw `u32` value.
571 ///
572 /// # Examples
573 ///
574 /// ```rust
575 /// use bitcoin_units::locktime::absolute;
576 ///
577 /// assert_eq!(absolute::Height::MAX.to_u32(), 499_999_999);
578 /// ```
579 #[inline]
580 pub const fn to_u32(self) -> u32 { self.0 }
581
582 /// Returns true if a transaction with this locktime can be included in the next block.
583 ///
584 /// `self` is value of the `LockTime` and if `height` is the current chain tip then
585 /// a transaction with this lock can be broadcast for inclusion in the next block.
586 #[inline]
587 pub fn is_satisfied_by(self, height: Self) -> bool {
588 // Use u64 so that there can be no overflow.
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 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 pub fn new(timestamps: [crate::BlockTime; 11]) -> Result<Self, ConversionError> {
638 crate::BlockMtp::new(timestamps).try_into()
639 }
640
641 /// Constructs a new [`MedianTimePast`] from a big-endian hex-encoded `u32`.
642 ///
643 /// The input string may or may not contain a typical hex prefix e.g., `0x`.
644 ///
645 /// # Errors
646 ///
647 /// If the input string is not a valid hex representation of a block time.
648 pub fn from_hex(s: &str) -> Result<Self, ParseTimeError> { parse_hex(s, Self::from_u32) }
649
650 #[deprecated(since = "1.0.0-rc.0", note = "use `from_u32` instead")]
651 #[doc(hidden)]
652 pub const fn from_consensus(n: u32) -> Result<Self, ConversionError> { Self::from_u32(n) }
653
654 #[deprecated(since = "1.0.0-rc.0", note = "use `to_u32` instead")]
655 #[doc(hidden)]
656 pub const fn to_consensus_u32(self) -> u32 { self.to_u32() }
657
658 /// Constructs a new MTP directly from a `u32` value.
659 ///
660 /// This function, with [`MedianTimePast::to_u32`], is used to obtain a raw MTP value. It is
661 /// **not** used to convert to or from a block timestamp, which is not a MTP.
662 ///
663 /// # Errors
664 ///
665 /// If `n` is not in the allowable range of MTPs in a locktime: `[500_000_000, 2^32 - 1]`.
666 ///
667 /// # Examples
668 ///
669 /// ```rust
670 /// use bitcoin_units::locktime::absolute;
671 ///
672 /// let t: u32 = 1653195600; // May 22nd, 5am UTC.
673 /// let time = absolute::MedianTimePast::from_u32(t)?;
674 /// assert_eq!(time.to_u32(), t);
675 /// # Ok::<_, absolute::error::ConversionError>(())
676 /// ```
677 #[inline]
678 pub const fn from_u32(n: u32) -> Result<Self, ConversionError> {
679 if is_block_time(n) {
680 Ok(Self(n))
681 } else {
682 Err(ConversionError::invalid_time(n))
683 }
684 }
685
686 /// Converts this [`MedianTimePast`] to a raw `u32` value.
687 ///
688 /// # Examples
689 ///
690 /// ```rust
691 /// use bitcoin_units::locktime::absolute;
692 ///
693 /// assert_eq!(absolute::MedianTimePast::MIN.to_u32(), 500_000_000);
694 /// ```
695 #[inline]
696 pub const fn to_u32(self) -> u32 { self.0 }
697
698 /// Returns true if a transaction with this locktime can be included in the next block.
699 ///
700 /// `self` is the value of the `LockTime` and if `time` is the median time past of the block at
701 /// the chain tip then a transaction with this lock can be broadcast for inclusion in the next
702 /// block.
703 #[inline]
704 pub fn is_satisfied_by(self, time: Self) -> bool {
705 // The locktime check in Core during block validation uses the MTP
706 // of the previous block - which is the expected to be `time` here.
707 self <= time
708 }
709}
710
711crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(MedianTimePast);
712
713impl fmt::Display for MedianTimePast {
714 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
715}
716
717parse_int::impl_parse_str!(MedianTimePast, ParseTimeError, parser(MedianTimePast::from_u32));
718
719fn parser<T, E, S, F>(f: F) -> impl FnOnce(S) -> Result<T, E>
720where
721 E: From<ParseError>,
722 S: AsRef<str> + Into<InputString>,
723 F: FnOnce(u32) -> Result<T, ConversionError>,
724{
725 move |s| {
726 let n = s.as_ref().parse::<i64>().map_err(ParseError::invalid_int(s))?;
727 let n = u32::try_from(n).map_err(|_| ParseError::Conversion(n))?;
728 f(n).map_err(ParseError::from).map_err(Into::into)
729 }
730}
731
732fn parse_hex<T, E, S, F>(s: S, f: F) -> Result<T, E>
733where
734 E: From<ParseError>,
735 S: AsRef<str> + Into<InputString>,
736 F: FnOnce(u32) -> Result<T, ConversionError>,
737{
738 let n = i64::from_str_radix(parse_int::hex_remove_optional_prefix(s.as_ref()), 16)
739 .map_err(ParseError::invalid_int(s))?;
740 let n = u32::try_from(n).map_err(|_| ParseError::Conversion(n))?;
741 f(n).map_err(ParseError::from).map_err(Into::into)
742}
743
744/// Returns true if `n` is a block height i.e., less than 500,000,000.
745pub const fn is_block_height(n: u32) -> bool { n < LOCK_TIME_THRESHOLD }
746
747/// Returns true if `n` is a UNIX timestamp i.e., greater than or equal to 500,000,000.
748pub const fn is_block_time(n: u32) -> bool { n >= LOCK_TIME_THRESHOLD }
749
750#[cfg(feature = "arbitrary")]
751impl<'a> Arbitrary<'a> for LockTime {
752 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
753 let l = u32::arbitrary(u)?;
754 Ok(Self::from_consensus(l))
755 }
756}
757
758#[cfg(feature = "arbitrary")]
759impl<'a> Arbitrary<'a> for Height {
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 h = u.int_in_range(min..=max)?;
769 Ok(Self::from_u32(h).unwrap())
770 }
771 }
772 }
773}
774
775#[cfg(feature = "arbitrary")]
776impl<'a> Arbitrary<'a> for MedianTimePast {
777 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
778 let choice = u.int_in_range(0..=2)?;
779 match choice {
780 0 => Ok(Self::MIN),
781 1 => Ok(Self::MAX),
782 _ => {
783 let min = Self::MIN.to_u32();
784 let max = Self::MAX.to_u32();
785 let t = u.int_in_range(min..=max)?;
786 Ok(Self::from_u32(t).unwrap())
787 }
788 }
789 }
790}
791
792#[cfg(test)]
793mod tests {
794 #[cfg(feature = "alloc")]
795 use alloc::{boxed::Box, format, string::String};
796
797 use super::*;
798
799 #[test]
800 #[cfg(feature = "alloc")]
801 fn display_and_alternate() {
802 let lock_by_height = LockTime::from_height(741_521).unwrap();
803 let lock_by_time = LockTime::from_mtp(1_653_195_600).unwrap(); // May 22nd 2022, 5am UTC.
804
805 assert_eq!(format!("{}", lock_by_height), "741521");
806 assert_eq!(format!("{:#}", lock_by_height), "block-height 741521");
807 assert!(!format!("{:?}", lock_by_height).is_empty());
808
809 assert_eq!(format!("{}", lock_by_time), "1653195600");
810 assert_eq!(format!("{:#}", lock_by_time), "block-time 1653195600 (seconds since epoch)");
811 assert!(!format!("{:?}", lock_by_time).is_empty());
812 }
813
814 #[test]
815 fn roundtrips() {
816 let lock_by_height = LockTime::from_consensus(741_521);
817 let lock_by_time = LockTime::from_consensus(1_653_195_600);
818
819 assert_eq!(lock_by_height.to_consensus_u32(), 741_521);
820 assert_eq!(lock_by_time.to_consensus_u32(), 1_653_195_600);
821 }
822
823 #[test]
824 fn lock_time_from_hex_lower() {
825 let lock_by_time = LockTime::from_hex("0x6289c350").unwrap();
826 assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
827 }
828
829 #[test]
830 fn lock_time_from_hex_upper() {
831 let lock_by_time = LockTime::from_hex("0X6289C350").unwrap();
832 assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
833 }
834
835 #[test]
836 fn lock_time_from_unprefixed_hex_lower() {
837 let lock_by_time = LockTime::from_unprefixed_hex("6289c350").unwrap();
838 assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
839 }
840
841 #[test]
842 fn lock_time_from_unprefixed_hex_upper() {
843 let lock_by_time = LockTime::from_unprefixed_hex("6289C350").unwrap();
844 assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
845 }
846
847 #[test]
848 fn invalid_hex() {
849 assert!(LockTime::from_hex("0xzb93").is_err());
850 assert!(LockTime::from_unprefixed_hex("zb93").is_err());
851 }
852
853 #[test]
854 fn invalid_locktime_type() {
855 assert!(LockTime::from_height(499_999_999).is_ok()); // Below the threshold.
856 assert!(LockTime::from_height(500_000_000).is_err()); // The threshold.
857 assert!(LockTime::from_height(500_000_001).is_err()); // Above the threshold.
858
859 assert!(LockTime::from_mtp(499_999_999).is_err()); // Below the threshold.
860 assert!(LockTime::from_mtp(500_000_000).is_ok()); // The threshold.
861 assert!(LockTime::from_mtp(500_000_001).is_ok()); // Above the threshold.
862 }
863
864 #[test]
865 fn parses_correctly_to_height_or_time() {
866 let lock_by_height = LockTime::from_consensus(750_000);
867
868 assert!(lock_by_height.is_block_height());
869 assert!(!lock_by_height.is_block_time());
870
871 let t: u32 = 1_653_195_600; // May 22nd, 5am UTC.
872 let lock_by_time = LockTime::from_consensus(t);
873
874 assert!(!lock_by_time.is_block_height());
875 assert!(lock_by_time.is_block_time());
876
877 // Test is_same_unit() logic
878 assert!(lock_by_height.is_same_unit(LockTime::from_consensus(800_000)));
879 assert!(!lock_by_height.is_same_unit(lock_by_time));
880 assert!(lock_by_time.is_same_unit(LockTime::from_consensus(1_653_282_000)));
881 assert!(!lock_by_time.is_same_unit(lock_by_height));
882 }
883
884 #[test]
885 fn satisfied_by_height() {
886 let height_below = Height::from_u32(700_000).unwrap();
887 let height = Height::from_u32(750_000).unwrap();
888 let height_above = Height::from_u32(800_000).unwrap();
889
890 let lock_by_height = LockTime::from(height);
891
892 let t: u32 = 1_653_195_600; // May 22nd, 5am UTC.
893 let time = MedianTimePast::from_u32(t).unwrap();
894
895 assert!(!lock_by_height.is_satisfied_by(height_below, time));
896 assert!(lock_by_height.is_satisfied_by(height, time));
897 assert!(lock_by_height.is_satisfied_by(height_above, time));
898 }
899
900 #[test]
901 fn satisfied_by_time() {
902 let time_before = MedianTimePast::from_u32(1_653_109_200).unwrap(); // "May 21th 2022, 5am UTC.
903 let time = MedianTimePast::from_u32(1_653_195_600).unwrap(); // "May 22nd 2022, 5am UTC.
904 let time_after = MedianTimePast::from_u32(1_653_282_000).unwrap(); // "May 23rd 2022, 5am UTC.
905
906 let lock_by_time = LockTime::from(time);
907
908 let height = Height::from_u32(800_000).unwrap();
909
910 assert!(!lock_by_time.is_satisfied_by(height, time_before));
911 assert!(lock_by_time.is_satisfied_by(height, time));
912 assert!(lock_by_time.is_satisfied_by(height, time_after));
913 }
914
915 #[test]
916 fn height_correctly_implies() {
917 let lock_by_height = LockTime::from_consensus(750_005);
918
919 assert!(!lock_by_height.is_implied_by(LockTime::from_consensus(750_004)));
920 assert!(lock_by_height.is_implied_by(LockTime::from_consensus(750_005)));
921 assert!(lock_by_height.is_implied_by(LockTime::from_consensus(750_006)));
922 }
923
924 #[test]
925 fn time_correctly_implies() {
926 let t: u32 = 1_700_000_005;
927 let lock_by_time = LockTime::from_consensus(t);
928
929 assert!(!lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_004)));
930 assert!(lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_005)));
931 assert!(lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_006)));
932 }
933
934 #[test]
935 fn incorrect_units_do_not_imply() {
936 let lock_by_height = LockTime::from_consensus(750_005);
937 assert!(!lock_by_height.is_implied_by(LockTime::from_consensus(1_700_000_004)));
938 }
939
940 #[test]
941 fn time_from_str_hex_happy_path() {
942 let actual = MedianTimePast::from_hex("0x6289C350").unwrap();
943 let expected = MedianTimePast::from_u32(0x6289_C350).unwrap();
944 assert_eq!(actual, expected);
945 }
946
947 #[test]
948 fn time_from_str_hex_no_prefix_happy_path() {
949 let time = MedianTimePast::from_hex("6289C350").unwrap();
950 assert_eq!(time, MedianTimePast(0x6289_C350));
951 }
952
953 #[test]
954 fn time_from_str_hex_invalid_hex_should_err() {
955 let hex = "0xzb93";
956 let result = MedianTimePast::from_hex(hex);
957 assert!(result.is_err());
958 }
959
960 #[test]
961 fn height_from_str_hex_happy_path() {
962 let actual = Height::from_hex("0xBA70D").unwrap();
963 let expected = Height(0xBA70D);
964 assert_eq!(actual, expected);
965 }
966
967 #[test]
968 fn height_from_str_hex_no_prefix_happy_path() {
969 let height = Height::from_hex("BA70D").unwrap();
970 assert_eq!(height, Height(0xBA70D));
971 }
972
973 #[test]
974 fn height_from_str_hex_invalid_hex_should_err() {
975 let hex = "0xzb93";
976 let result = Height::from_hex(hex);
977 assert!(result.is_err());
978 }
979
980 #[test]
981 fn height_try_from_stringlike_happy_path() {
982 let want = Height::from_u32(10).unwrap();
983 assert_eq!("10".parse::<Height>().unwrap(), want);
984 assert_eq!(Height::try_from("10").unwrap(), want);
985 #[cfg(feature = "alloc")]
986 {
987 assert_eq!(Height::try_from(String::from("10")).unwrap(), want);
988 assert_eq!(Height::try_from(Box::<str>::from("10")).unwrap(), want);
989 }
990 }
991
992 #[test]
993 fn height_try_from_stringlike_hex_error_path() {
994 // Only base-10 values should parse
995 assert!("0xab".parse::<Height>().is_err());
996 assert!(Height::try_from("0xab").is_err());
997 #[cfg(feature = "alloc")]
998 {
999 assert!(Height::try_from(String::from("0xab")).is_err());
1000 assert!(Height::try_from(Box::<str>::from("0xab")).is_err());
1001 }
1002 }
1003
1004 #[test]
1005 fn height_try_from_stringlike_decimal_error_path() {
1006 // Only integers should parse
1007 assert!("10.123".parse::<Height>().is_err());
1008 assert!(Height::try_from("10.123").is_err());
1009 #[cfg(feature = "alloc")]
1010 {
1011 assert!(Height::try_from(String::from("10.123")).is_err());
1012 assert!(Height::try_from(Box::<str>::from("10.123")).is_err());
1013 }
1014 }
1015
1016 #[test]
1017 fn is_block_height_or_time() {
1018 assert!(is_block_height(499_999_999));
1019 assert!(!is_block_height(500_000_000));
1020
1021 assert!(!is_block_time(499_999_999));
1022 assert!(is_block_time(500_000_000));
1023 }
1024
1025 #[test]
1026 fn valid_chain_computes_mtp() {
1027 use crate::BlockTime;
1028
1029 let mut timestamps = [
1030 BlockTime::from_u32(500_000_010),
1031 BlockTime::from_u32(500_000_003),
1032 BlockTime::from_u32(500_000_005),
1033 BlockTime::from_u32(500_000_008),
1034 BlockTime::from_u32(500_000_001),
1035 BlockTime::from_u32(500_000_004),
1036 BlockTime::from_u32(500_000_006),
1037 BlockTime::from_u32(500_000_009),
1038 BlockTime::from_u32(500_000_002),
1039 BlockTime::from_u32(500_000_007),
1040 BlockTime::from_u32(500_000_000),
1041 ];
1042
1043 // Try various reorderings
1044 assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1045 timestamps.reverse();
1046 assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1047 timestamps.sort();
1048 assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1049 timestamps.reverse();
1050 assert_eq!(MedianTimePast::new(timestamps).unwrap().to_u32(), 500_000_005);
1051 }
1052
1053 #[test]
1054 fn height_is_satisfied_by() {
1055 let chain_tip = Height::from_u32(100).unwrap();
1056
1057 // lock is satisfied if transaction can go in the next block (height <= chain_tip + 1).
1058 let locktime = Height::from_u32(100).unwrap();
1059 assert!(locktime.is_satisfied_by(chain_tip));
1060 let locktime = Height::from_u32(101).unwrap();
1061 assert!(locktime.is_satisfied_by(chain_tip));
1062
1063 // It is not satisfied if the lock height is after the next block.
1064 let locktime = Height::from_u32(102).unwrap();
1065 assert!(!locktime.is_satisfied_by(chain_tip));
1066 }
1067
1068 #[test]
1069 fn median_time_past_is_satisfied_by() {
1070 let mtp = MedianTimePast::from_u32(500_000_001).unwrap();
1071
1072 // lock is satisfied if transaction can go in the next block (locktime <= mtp).
1073 let locktime = MedianTimePast::from_u32(500_000_000).unwrap();
1074 assert!(locktime.is_satisfied_by(mtp));
1075 let locktime = MedianTimePast::from_u32(500_000_001).unwrap();
1076 assert!(locktime.is_satisfied_by(mtp));
1077
1078 // It is not satisfied if the lock time is after the median time past.
1079 let locktime = MedianTimePast::from_u32(500_000_002).unwrap();
1080 assert!(!locktime.is_satisfied_by(mtp));
1081 }
1082}