// Rust Bitcoin Library - Written by the rust-bitcoin developers. // SPDX-License-Identifier: CC0-1.0 //! Provides type [`LockTime`] that implements the logic around nSequence/OP_CHECKSEQUENCEVERIFY. //! //! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by //! whether bit 22 of the `u32` consensus value is set. //! use core::fmt; use core::convert::TryFrom; #[cfg(docsrs)] use crate::relative; /// A relative lock time value, representing either a block height or time (512 second intervals). /// /// The `relative::LockTime` type does not have any constructors, this is by design, please use /// `Sequence::to_relative_lock_time` to create a relative lock time. /// /// ### Relevant BIPs /// /// * [BIP 68 Relative lock-time using consensus-enforced sequence numbers](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) /// * [BIP 112 CHECKSEQUENCEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki) #[allow(clippy::derive_ord_xor_partial_ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(crate = "actual_serde"))] pub enum LockTime { /// A block height lock time value. Blocks(Height), /// A 512 second time interval value. Time(Time), } impl LockTime { /// Returns true if this [`relative::LockTime`] is satisfied by either height or time. /// /// # Examples /// /// ```rust /// # use bitcoin::Sequence; /// # use bitcoin::locktime::relative::{LockTime, Height, Time}; /// /// # let height = 100; // 100 blocks. /// # let intervals = 70; // Approx 10 hours. /// # let current_height = || Height::from(height + 10); /// # let current_time = || Time::from_512_second_intervals(intervals + 10); /// # let lock = Sequence::from_height(height).to_relative_lock_time().expect("valid height"); /// /// // Users that have chain data can get the current height and time to check against a lock. /// let height_and_time = (current_time(), current_height()); // tuple order does not matter. /// assert!(lock.is_satisfied_by(current_height(), current_time())); /// ``` pub fn is_satisfied_by(&self, h: Height, t: Time) -> bool { if let Ok(true) = self.is_satisfied_by_height(h) { true } else if let Ok(true) = self.is_satisfied_by_time(t) { true } else { false } } /// Returns true if satisfaction of `other` lock time implies satisfaction of this /// [`relative::LockTime`]. /// /// This function is useful when checking sequence values against a lock, first one checks the /// sequence represents a relative lock time by converting to `LockTime` then use this function /// to see if satisfaction of the newly created lock time would imply satisfaction of `self`. /// /// # Examples /// /// ```rust /// # use bitcoin::Sequence; /// # use bitcoin::locktime::relative::{LockTime, Height, Time}; /// /// # let height = 100; // 100 blocks. /// # let lock = Sequence::from_height(height).to_relative_lock_time().expect("valid height"); /// # let test_sequence = Sequence::from_height(height + 10); /// /// let satisfied = match test_sequence.to_relative_lock_time() { /// None => false, // Handle non-lock-time case. /// Some(test_lock) => lock.is_implied_by(test_lock), /// }; /// assert!(satisfied); /// ``` pub fn is_implied_by(&self, other: LockTime) -> bool { use LockTime::*; match (*self, other) { (Blocks(this), Blocks(other)) => this.value() <= other.value(), (Time(this), Time(other)) => this.value() <= other.value(), _ => false, // Not the same units. } } /// Returns true if this [`relative::LockTime`] is satisfied by [`Height`]. /// /// # Errors /// /// Returns an error if this lock is not lock-by-height. /// /// # Examples /// /// ```rust /// # use bitcoin::Sequence; /// # use bitcoin::locktime::relative::{LockTime, Height, Time}; /// /// let height: u16 = 100; /// let lock = Sequence::from_height(height).to_relative_lock_time().expect("valid height"); /// assert!(lock.is_satisfied_by_height(Height::from(height+1)).expect("a height")); /// ``` #[inline] pub fn is_satisfied_by_height(&self, h: Height) -> Result { use LockTime::*; match *self { Blocks(ref height) => Ok(height.value() <= h.value()), Time(ref time) => Err(Error::IncompatibleTime(*self, *time)), } } /// Returns true if this [`relative::LockTime`] is satisfied by [`Time`]. /// /// # Errors /// /// Returns an error if this lock is not lock-by-time. /// /// # Examples /// /// ```rust /// # use bitcoin::Sequence; /// # use bitcoin::locktime::relative::{LockTime, Height, Time}; /// /// let intervals: u16 = 70; // approx 10 hours; /// let lock = Sequence::from_512_second_intervals(intervals).to_relative_lock_time().expect("valid time"); /// assert!(lock.is_satisfied_by_time(Time::from_512_second_intervals(intervals + 10)).expect("a time")); /// ``` #[inline] pub fn is_satisfied_by_time(&self, t: Time) -> Result { use LockTime::*; match *self { Time(ref time) => Ok(time.value() <= t.value()), Blocks(ref height) => Err(Error::IncompatibleHeight(*self, *height)), } } } impl From for LockTime { #[inline] fn from(h: Height) -> Self { LockTime::Blocks(h) } } impl From