Merge rust-bitcoin/rust-bitcoin#4431: units: rename relative locktime types, absolute::Time, and constructors

826acb8273 units: rename relative::HeightInterval constructors (Andrew Poelstra)
39b4f7670d units: rename relative::Height to HeightInterval (Andrew Poelstra)
d3619cc1bc units: deprecate relative::MtpInterval::to_consensus_u32 (Andrew Poelstra)
1a6b8b4c7a units: rename relative::Time to MtpInterval (Andrew Poelstra)
39b057fade units: rename absolute::Height consensus functions (Andrew Poelstra)
5a8f33f380 units: rename absolute::Mtp consensus functions (Andrew Poelstra)
8ffcd2cf30 units: rename absolute::Time to absolute::Mtp (Andrew Poelstra)

Pull request description:

  This PR does a whole bunch of renames to the `units` locktime modules. These modules contain the underlying units for locktime types and hopefully aren't used directly too often, which should minimize the disruption from these renames.

  This fixes a number of issues:

  * Confusion between blocktimes and MTPs (which in fairness, *are* blocktimes, but they're not the one that users are likely to reach for)
  * Constructor and conversion names that imply there is a "consensus encoding" for these bare units, which corresponded to the consensus encoding of the corresponding locktimes/sequence numbers
  * `from_*` methods without `to_*` methods and vice-versa (overlaps with the above)
  * the horribly named `value` method on relative heights and times (but not absolute ones)

  This PR does **not** remove the `MtpAndHeight` type, nor does it add constructors for `Mtp` from lists of blocktimes. This is because the PR was too big already and I felt I should split it up.

  Alternate to #4427

ACKs for top commit:
  tcharding:
    ACK 826acb8273

Tree-SHA512: 6e0491e17927625cde85c2cf92ff152a10613e632474122a626ee31b662d21c09fcb9fa3014c44708c97536535a33845cbbcd81e73dcdf98e9ee9fd6143c698f
This commit is contained in:
merge-script 2025-05-05 09:08:30 +10:00
commit 1b04f5df65
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
11 changed files with 353 additions and 250 deletions

View File

@ -30,7 +30,7 @@ use crate::transaction::{Transaction, TransactionExt as _, Wtxid};
#[doc(inline)] #[doc(inline)]
pub use primitives::block::{Block, Checked, Unchecked, Validation, Version, BlockHash, Header, WitnessCommitment}; pub use primitives::block::{Block, Checked, Unchecked, Validation, Version, BlockHash, Header, WitnessCommitment};
#[doc(inline)] #[doc(inline)]
pub use units::block::{BlockHeight, BlockInterval, TooBigForRelativeBlockHeightError}; pub use units::block::{BlockHeight, BlockInterval, TooBigForRelativeBlockHeightIntervalError};
impl_hashencode!(BlockHash); impl_hashencode!(BlockHash);

View File

@ -41,7 +41,11 @@ pub mod locktime {
/// Re-export everything from the `primitives::locktime::absolute` module. /// Re-export everything from the `primitives::locktime::absolute` module.
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
pub use primitives::locktime::absolute::{ConversionError, Height, LockTime, ParseHeightError, ParseTimeError, Time}; pub use primitives::locktime::absolute::{ConversionError, Height, LockTime, ParseHeightError, ParseTimeError, Mtp};
#[deprecated(since = "TBD", note = "use `Mtp` instead")]
#[doc(hidden)]
pub type Time = Mtp;
impl Encodable for LockTime { impl Encodable for LockTime {
#[inline] #[inline]
@ -67,9 +71,17 @@ pub mod locktime {
/// Re-export everything from the `primitives::locktime::relative` module. /// Re-export everything from the `primitives::locktime::relative` module.
pub use primitives::locktime::relative::{ pub use primitives::locktime::relative::{
DisabledLockTimeError, Height, IncompatibleHeightError, IncompatibleTimeError, DisabledLockTimeError, HeightInterval, IncompatibleHeightError, IncompatibleTimeError,
LockTime, Time, TimeOverflowError, LockTime, MtpInterval, TimeOverflowError,
}; };
#[deprecated(since = "TBD", note = "use `Mtp` instead")]
#[doc(hidden)]
pub type Height = HeightInterval;
#[deprecated(since = "TBD", note = "use `Mtp` instead")]
#[doc(hidden)]
pub type Time = MtpInterval;
} }
} }

View File

@ -20,7 +20,7 @@ use primitives::Sequence;
use super::Weight; use super::Weight;
use crate::consensus::{self, encode, Decodable, Encodable}; use crate::consensus::{self, encode, Decodable, Encodable};
use crate::internal_macros::{impl_consensus_encoding, impl_hashencode}; use crate::internal_macros::{impl_consensus_encoding, impl_hashencode};
use crate::locktime::absolute::{self, Height, Time}; use crate::locktime::absolute::{self, Height, Mtp};
use crate::prelude::{Borrow, Vec}; use crate::prelude::{Borrow, Vec};
use crate::script::{Script, ScriptBuf, ScriptExt as _, ScriptExtPriv as _}; use crate::script::{Script, ScriptBuf, ScriptExt as _, ScriptExtPriv as _};
#[cfg(doc)] #[cfg(doc)]
@ -295,7 +295,7 @@ pub trait TransactionExt: sealed::Sealed {
/// By definition if the lock time is not enabled the transaction's absolute timelock is /// By definition if the lock time is not enabled the transaction's absolute timelock is
/// considered to be satisfied i.e., there are no timelock constraints restricting this /// considered to be satisfied i.e., there are no timelock constraints restricting this
/// transaction from being mined immediately. /// transaction from being mined immediately.
fn is_absolute_timelock_satisfied(&self, height: Height, time: Time) -> bool; fn is_absolute_timelock_satisfied(&self, height: Height, time: Mtp) -> bool;
/// Returns `true` if this transactions nLockTime is enabled ([BIP-65]). /// Returns `true` if this transactions nLockTime is enabled ([BIP-65]).
/// ///
@ -393,7 +393,7 @@ impl TransactionExt for Transaction {
fn is_explicitly_rbf(&self) -> bool { self.input.iter().any(|input| input.sequence.is_rbf()) } fn is_explicitly_rbf(&self) -> bool { self.input.iter().any(|input| input.sequence.is_rbf()) }
fn is_absolute_timelock_satisfied(&self, height: Height, time: Time) -> bool { fn is_absolute_timelock_satisfied(&self, height: Height, time: Mtp) -> bool {
if !self.is_lock_time_enabled() { if !self.is_lock_time_enabled() {
return true; return true;
} }

View File

@ -16,7 +16,11 @@ use crate::{absolute, Transaction};
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)] #[doc(inline)]
pub use units::locktime::absolute::{ConversionError, Height, ParseHeightError, ParseTimeError, Time, LOCK_TIME_THRESHOLD}; pub use units::locktime::absolute::{ConversionError, Height, ParseHeightError, ParseTimeError, Mtp, LOCK_TIME_THRESHOLD};
#[deprecated(since = "TBD", note = "use `Mtp` instead")]
#[doc(hidden)]
pub type Time = Mtp;
/// An absolute lock time value, representing either a block height or a UNIX timestamp (seconds /// An absolute lock time value, representing either a block height or a UNIX timestamp (seconds
/// since epoch). /// since epoch).
@ -79,7 +83,7 @@ pub enum LockTime {
/// assert!(n.is_block_time()); /// assert!(n.is_block_time());
/// assert_eq!(n.to_consensus_u32(), seconds); /// assert_eq!(n.to_consensus_u32(), seconds);
/// ``` /// ```
Seconds(Time), Seconds(Mtp),
} }
impl LockTime { impl LockTime {
@ -141,9 +145,9 @@ impl LockTime {
#[allow(clippy::missing_panics_doc)] #[allow(clippy::missing_panics_doc)]
pub fn from_consensus(n: u32) -> Self { pub fn from_consensus(n: u32) -> Self {
if units::locktime::absolute::is_block_height(n) { if units::locktime::absolute::is_block_height(n) {
Self::Blocks(Height::from_consensus(n).expect("n is valid")) Self::Blocks(Height::from_u32(n).expect("n is valid"))
} else { } else {
Self::Seconds(Time::from_consensus(n).expect("n is valid")) Self::Seconds(Mtp::from_u32(n).expect("n is valid"))
} }
} }
@ -166,17 +170,23 @@ impl LockTime {
/// ``` /// ```
#[inline] #[inline]
pub fn from_height(n: u32) -> Result<Self, ConversionError> { pub fn from_height(n: u32) -> Result<Self, ConversionError> {
let height = Height::from_consensus(n)?; let height = Height::from_u32(n)?;
Ok(LockTime::Blocks(height)) Ok(LockTime::Blocks(height))
} }
/// Constructs a new `LockTime` from `n`, expecting `n` to be a valid block time. #[inline]
#[deprecated(since = "TBD", note = "use `from_mtp` instead")]
#[doc(hidden)]
pub fn from_time(n: u32) -> Result<Self, ConversionError> { Self::from_mtp(n) }
/// Constructs a new `LockTime` from `n`, expecting `n` to be a median-time-past (MTP)
/// which is in range for a locktime.
/// ///
/// # Note /// # Note
/// ///
/// If the locktime is set to a timestamp `T`, /// If the locktime is set to an MTP `T`, the transaction can be included in a block only if
/// the transaction can be included in a block only if the median time past (MTP) of the /// the MTP of last recent 11 blocks is greater than `T`.
/// last 11 blocks is greater than `T`. ///
/// It is possible to broadcast the transaction once the MTP is greater than `T`.[see BIP-113] /// It is possible to broadcast the transaction once the MTP is greater than `T`.[see BIP-113]
/// ///
/// [BIP-113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki) /// [BIP-113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki)
@ -191,8 +201,8 @@ impl LockTime {
/// assert!(absolute::LockTime::from_time(741521).is_err()); /// assert!(absolute::LockTime::from_time(741521).is_err());
/// ``` /// ```
#[inline] #[inline]
pub fn from_time(n: u32) -> Result<Self, ConversionError> { pub fn from_mtp(n: u32) -> Result<Self, ConversionError> {
let time = Time::from_consensus(n)?; let time = Mtp::from_u32(n)?;
Ok(LockTime::Seconds(time)) Ok(LockTime::Seconds(time))
} }
@ -229,7 +239,7 @@ impl LockTime {
/// # use bitcoin_primitives::absolute; /// # use bitcoin_primitives::absolute;
/// // Can be implemented if block chain data is available. /// // Can be implemented if block chain data is available.
/// fn get_height() -> absolute::Height { todo!("return the current block height") } /// fn get_height() -> absolute::Height { todo!("return the current block height") }
/// fn get_time() -> absolute::Time { todo!("return the current block time") } /// fn get_time() -> absolute::Mtp { todo!("return the current block time") }
/// ///
/// let n = absolute::LockTime::from_consensus(741521); // `n OP_CHEKCLOCKTIMEVERIFY`. /// let n = absolute::LockTime::from_consensus(741521); // `n OP_CHEKCLOCKTIMEVERIFY`.
/// if n.is_satisfied_by(get_height(), get_time()) { /// if n.is_satisfied_by(get_height(), get_time()) {
@ -237,7 +247,7 @@ impl LockTime {
/// } /// }
/// ```` /// ````
#[inline] #[inline]
pub fn is_satisfied_by(self, height: Height, time: Time) -> bool { pub fn is_satisfied_by(self, height: Height, time: Mtp) -> bool {
use LockTime as L; use LockTime as L;
match self { match self {
@ -310,8 +320,8 @@ impl LockTime {
#[inline] #[inline]
pub fn to_consensus_u32(self) -> u32 { pub fn to_consensus_u32(self) -> u32 {
match self { match self {
LockTime::Blocks(ref h) => h.to_consensus_u32(), LockTime::Blocks(ref h) => h.to_u32(),
LockTime::Seconds(ref t) => t.to_consensus_u32(), LockTime::Seconds(ref t) => t.to_u32(),
} }
} }
} }
@ -323,9 +333,9 @@ impl From<Height> for LockTime {
fn from(h: Height) -> Self { LockTime::Blocks(h) } fn from(h: Height) -> Self { LockTime::Blocks(h) }
} }
impl From<Time> for LockTime { impl From<Mtp> for LockTime {
#[inline] #[inline]
fn from(t: Time) -> Self { LockTime::Seconds(t) } fn from(t: Mtp) -> Self { LockTime::Seconds(t) }
} }
impl fmt::Debug for LockTime { impl fmt::Debug for LockTime {
@ -412,7 +422,7 @@ mod tests {
#[test] #[test]
fn display_and_alternate() { fn display_and_alternate() {
let lock_by_height = LockTime::from_height(741_521).unwrap(); let lock_by_height = LockTime::from_height(741_521).unwrap();
let lock_by_time = LockTime::from_time(1_653_195_600).unwrap(); // May 22nd 2022, 5am UTC. let lock_by_time = LockTime::from_mtp(1_653_195_600).unwrap(); // May 22nd 2022, 5am UTC.
assert_eq!(format!("{}", lock_by_height), "741521"); assert_eq!(format!("{}", lock_by_height), "741521");
assert_eq!(format!("{:#}", lock_by_height), "block-height 741521"); assert_eq!(format!("{:#}", lock_by_height), "block-height 741521");
@ -468,9 +478,9 @@ mod tests {
assert!(LockTime::from_height(500_000_000).is_err()); // The threshold. assert!(LockTime::from_height(500_000_000).is_err()); // The threshold.
assert!(LockTime::from_height(500_000_001).is_err()); // Above the threshold. assert!(LockTime::from_height(500_000_001).is_err()); // Above the threshold.
assert!(LockTime::from_time(499_999_999).is_err()); // Below the threshold. assert!(LockTime::from_mtp(499_999_999).is_err()); // Below the threshold.
assert!(LockTime::from_time(500_000_000).is_ok()); // The threshold. assert!(LockTime::from_mtp(500_000_000).is_ok()); // The threshold.
assert!(LockTime::from_time(500_000_001).is_ok()); // Above the threshold. assert!(LockTime::from_mtp(500_000_001).is_ok()); // Above the threshold.
} }
#[test] #[test]
@ -495,14 +505,14 @@ mod tests {
#[test] #[test]
fn satisfied_by_height() { fn satisfied_by_height() {
let height_below = Height::from_consensus(700_000).unwrap(); let height_below = Height::from_u32(700_000).unwrap();
let height = Height::from_consensus(750_000).unwrap(); let height = Height::from_u32(750_000).unwrap();
let height_above = Height::from_consensus(800_000).unwrap(); let height_above = Height::from_u32(800_000).unwrap();
let lock_by_height = LockTime::from(height); let lock_by_height = LockTime::from(height);
let t: u32 = 1_653_195_600; // May 22nd, 5am UTC. let t: u32 = 1_653_195_600; // May 22nd, 5am UTC.
let time = Time::from_consensus(t).unwrap(); let time = Mtp::from_u32(t).unwrap();
assert!(!lock_by_height.is_satisfied_by(height_below, time)); assert!(!lock_by_height.is_satisfied_by(height_below, time));
assert!(lock_by_height.is_satisfied_by(height, time)); assert!(lock_by_height.is_satisfied_by(height, time));
@ -511,13 +521,13 @@ mod tests {
#[test] #[test]
fn satisfied_by_time() { fn satisfied_by_time() {
let time_before = Time::from_consensus(1_653_109_200).unwrap(); // "May 21th 2022, 5am UTC. let time_before = Mtp::from_u32(1_653_109_200).unwrap(); // "May 21th 2022, 5am UTC.
let time = Time::from_consensus(1_653_195_600).unwrap(); // "May 22nd 2022, 5am UTC. let time = Mtp::from_u32(1_653_195_600).unwrap(); // "May 22nd 2022, 5am UTC.
let time_after = Time::from_consensus(1_653_282_000).unwrap(); // "May 23rd 2022, 5am UTC. let time_after = Mtp::from_u32(1_653_282_000).unwrap(); // "May 23rd 2022, 5am UTC.
let lock_by_time = LockTime::from(time); let lock_by_time = LockTime::from(time);
let height = Height::from_consensus(800_000).unwrap(); let height = Height::from_u32(800_000).unwrap();
assert!(!lock_by_time.is_satisfied_by(height, time_before)); assert!(!lock_by_time.is_satisfied_by(height, time_before));
assert!(lock_by_time.is_satisfied_by(height, time)); assert!(lock_by_time.is_satisfied_by(height, time));

View File

@ -13,9 +13,17 @@ use crate::{relative, TxIn};
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)] #[doc(inline)]
pub use units::locktime::relative::{Height, Time, TimeOverflowError}; pub use units::locktime::relative::{HeightInterval, MtpInterval, TimeOverflowError};
use units::mtp_height::MtpAndHeight; use units::mtp_height::MtpAndHeight;
#[deprecated(since = "TBD", note = "use `Mtp` instead")]
#[doc(hidden)]
pub type Height = HeightInterval;
#[deprecated(since = "TBD", note = "use `Mtp` instead")]
#[doc(hidden)]
pub type Time = MtpInterval;
/// A relative lock time value, representing either a block height or time (512 second intervals). /// A relative lock time value, representing either a block height or time (512 second intervals).
/// ///
/// Used for sequence numbers (`nSequence` in Bitcoin Core and [`TxIn::sequence`] /// Used for sequence numbers (`nSequence` in Bitcoin Core and [`TxIn::sequence`]
@ -61,7 +69,7 @@ use units::mtp_height::MtpAndHeight;
/// ///
/// let chain_tip = MtpAndHeight::new(current_height, timestamps); /// let chain_tip = MtpAndHeight::new(current_height, timestamps);
/// let utxo_mined_at = MtpAndHeight::new(utxo_height, utxo_timestamps); /// let utxo_mined_at = MtpAndHeight::new(utxo_height, utxo_timestamps);
/// let locktime = relative::LockTime::Time(relative::Time::from_512_second_intervals(10)); /// let locktime = relative::LockTime::Time(relative::MtpInterval::from_512_second_intervals(10));
/// ///
/// // Check if locktime is satisfied /// // Check if locktime is satisfied
/// assert!(locktime.is_satisfied_by(chain_tip, utxo_mined_at)); /// assert!(locktime.is_satisfied_by(chain_tip, utxo_mined_at));
@ -70,15 +78,15 @@ use units::mtp_height::MtpAndHeight;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum LockTime { pub enum LockTime {
/// A block height lock time value. /// A block height lock time value.
Blocks(Height), Blocks(HeightInterval),
/// A 512 second time interval value. /// A 512 second time interval value.
Time(Time), Time(MtpInterval),
} }
impl LockTime { impl LockTime {
/// A relative locktime of 0 is always valid, and is assumed valid for inputs that /// A relative locktime of 0 is always valid, and is assumed valid for inputs that
/// are not yet confirmed. /// are not yet confirmed.
pub const ZERO: LockTime = LockTime::Blocks(Height::ZERO); pub const ZERO: LockTime = LockTime::Blocks(HeightInterval::ZERO);
/// The number of bytes that the locktime contributes to the size of a transaction. /// The number of bytes that the locktime contributes to the size of a transaction.
pub const SIZE: usize = 4; // Serialized length of a u32. pub const SIZE: usize = 4; // Serialized length of a u32.
@ -124,8 +132,9 @@ impl LockTime {
#[inline] #[inline]
pub fn to_consensus_u32(self) -> u32 { pub fn to_consensus_u32(self) -> u32 {
match self { match self {
LockTime::Blocks(ref h) => h.to_consensus_u32(), LockTime::Blocks(ref h) => u32::from(h.to_height()),
LockTime::Time(ref t) => t.to_consensus_u32(), LockTime::Time(ref t) =>
Sequence::LOCK_TYPE_MASK | u32::from(t.to_512_second_intervals()),
} }
} }
@ -156,7 +165,7 @@ impl LockTime {
/// Constructs a new `LockTime` from `n`, expecting `n` to be a 16-bit count of blocks. /// Constructs a new `LockTime` from `n`, expecting `n` to be a 16-bit count of blocks.
#[inline] #[inline]
pub const fn from_height(n: u16) -> Self { LockTime::Blocks(Height::from_height(n)) } pub const fn from_height(n: u16) -> Self { LockTime::Blocks(HeightInterval::from_height(n)) }
/// Constructs a new `LockTime` from `n`, expecting `n` to be a count of 512-second intervals. /// Constructs a new `LockTime` from `n`, expecting `n` to be a count of 512-second intervals.
/// ///
@ -164,7 +173,7 @@ impl LockTime {
/// [`Self::from_seconds_floor`] or [`Self::from_seconds_ceil`]. /// [`Self::from_seconds_floor`] or [`Self::from_seconds_ceil`].
#[inline] #[inline]
pub const fn from_512_second_intervals(intervals: u16) -> Self { pub const fn from_512_second_intervals(intervals: u16) -> Self {
LockTime::Time(Time::from_512_second_intervals(intervals)) LockTime::Time(MtpInterval::from_512_second_intervals(intervals))
} }
/// Construct a new [`LockTime`] from seconds, converting the seconds into 512 second interval /// Construct a new [`LockTime`] from seconds, converting the seconds into 512 second interval
@ -175,7 +184,7 @@ impl LockTime {
/// Will return an error if the input cannot be encoded in 16 bits. /// Will return an error if the input cannot be encoded in 16 bits.
#[inline] #[inline]
pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> { pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
match Time::from_seconds_floor(seconds) { match MtpInterval::from_seconds_floor(seconds) {
Ok(time) => Ok(LockTime::Time(time)), Ok(time) => Ok(LockTime::Time(time)),
Err(e) => Err(e), Err(e) => Err(e),
} }
@ -189,7 +198,7 @@ impl LockTime {
/// Will return an error if the input cannot be encoded in 16 bits. /// Will return an error if the input cannot be encoded in 16 bits.
#[inline] #[inline]
pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> { pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
match Time::from_seconds_ceil(seconds) { match MtpInterval::from_seconds_ceil(seconds) {
Ok(time) => Ok(LockTime::Time(time)), Ok(time) => Ok(LockTime::Time(time)),
Err(e) => Err(e), Err(e) => Err(e),
} }
@ -216,7 +225,7 @@ impl LockTime {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use bitcoin_primitives::locktime::relative::Height; /// # use bitcoin_primitives::locktime::relative::HeightInterval;
/// # use bitcoin_primitives::relative::Time; /// # use bitcoin_primitives::relative::Time;
/// # use units::mtp_height::MtpAndHeight; /// # use units::mtp_height::MtpAndHeight;
/// # use bitcoin_primitives::BlockHeight; /// # use bitcoin_primitives::BlockHeight;
@ -285,8 +294,8 @@ impl LockTime {
use LockTime as L; use LockTime as L;
match (self, other) { match (self, other) {
(L::Blocks(this), L::Blocks(other)) => this.value() <= other.value(), (L::Blocks(this), L::Blocks(other)) => this <= other,
(L::Time(this), L::Time(other)) => this.value() <= other.value(), (L::Time(this), L::Time(other)) => this <= other,
_ => false, // Not the same units. _ => false, // Not the same units.
} }
} }
@ -320,7 +329,7 @@ impl LockTime {
} }
} }
/// Returns true if this [`relative::LockTime`] is satisfied by [`Height`]. /// Returns true if this [`relative::LockTime`] is satisfied by [`HeightInterval`].
/// ///
/// # Errors /// # Errors
/// ///
@ -334,14 +343,17 @@ impl LockTime {
/// ///
/// let required_height: u16 = 100; /// let required_height: u16 = 100;
/// let lock = Sequence::from_height(required_height).to_relative_lock_time().expect("valid height"); /// let lock = Sequence::from_height(required_height).to_relative_lock_time().expect("valid height");
/// assert!(lock.is_satisfied_by_height(relative::Height::from(required_height + 1)).expect("a height")); /// assert!(lock.is_satisfied_by_height(relative::HeightInterval::from(required_height + 1)).expect("a height"));
/// ``` /// ```
#[inline] #[inline]
pub fn is_satisfied_by_height(self, height: Height) -> Result<bool, IncompatibleHeightError> { pub fn is_satisfied_by_height(
self,
height: HeightInterval,
) -> Result<bool, IncompatibleHeightError> {
use LockTime as L; use LockTime as L;
match self { match self {
L::Blocks(ref required_height) => Ok(required_height.value() <= height.value()), L::Blocks(required_height) => Ok(required_height <= height),
L::Time(time) => Err(IncompatibleHeightError { height, time }), L::Time(time) => Err(IncompatibleHeightError { height, time }),
} }
} }
@ -363,24 +375,24 @@ impl LockTime {
/// assert!(lock.is_satisfied_by_time(relative::Time::from_512_second_intervals(intervals + 10)).expect("a time")); /// assert!(lock.is_satisfied_by_time(relative::Time::from_512_second_intervals(intervals + 10)).expect("a time"));
/// ``` /// ```
#[inline] #[inline]
pub fn is_satisfied_by_time(self, time: Time) -> Result<bool, IncompatibleTimeError> { pub fn is_satisfied_by_time(self, time: MtpInterval) -> Result<bool, IncompatibleTimeError> {
use LockTime as L; use LockTime as L;
match self { match self {
L::Time(ref t) => Ok(t.value() <= time.value()), L::Time(ref t) => Ok(t.to_512_second_intervals() <= time.to_512_second_intervals()),
L::Blocks(height) => Err(IncompatibleTimeError { time, height }), L::Blocks(height) => Err(IncompatibleTimeError { time, height }),
} }
} }
} }
impl From<Height> for LockTime { impl From<HeightInterval> for LockTime {
#[inline] #[inline]
fn from(h: Height) -> Self { LockTime::Blocks(h) } fn from(h: HeightInterval) -> Self { LockTime::Blocks(h) }
} }
impl From<Time> for LockTime { impl From<MtpInterval> for LockTime {
#[inline] #[inline]
fn from(t: Time) -> Self { LockTime::Time(t) } fn from(t: MtpInterval) -> Self { LockTime::Time(t) }
} }
impl fmt::Display for LockTime { impl fmt::Display for LockTime {
@ -440,17 +452,17 @@ impl std::error::Error for DisabledLockTimeError {}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct IncompatibleHeightError { pub struct IncompatibleHeightError {
/// Attempted to satisfy a lock-by-blocktime lock with this height. /// Attempted to satisfy a lock-by-blocktime lock with this height.
height: Height, height: HeightInterval,
/// The inner time value of the lock-by-blocktime lock. /// The inner time value of the lock-by-blocktime lock.
time: Time, time: MtpInterval,
} }
impl IncompatibleHeightError { impl IncompatibleHeightError {
/// Returns the height that was erroneously used to try and satisfy a lock-by-blocktime lock. /// Returns the height that was erroneously used to try and satisfy a lock-by-blocktime lock.
pub fn incompatible(&self) -> Height { self.height } pub fn incompatible(&self) -> HeightInterval { self.height }
/// Returns the time value of the lock-by-blocktime lock. /// Returns the time value of the lock-by-blocktime lock.
pub fn expected(&self) -> Time { self.time } pub fn expected(&self) -> MtpInterval { self.time }
} }
impl fmt::Display for IncompatibleHeightError { impl fmt::Display for IncompatibleHeightError {
@ -471,17 +483,17 @@ impl std::error::Error for IncompatibleHeightError {}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct IncompatibleTimeError { pub struct IncompatibleTimeError {
/// Attempted to satisfy a lock-by-blockheight lock with this time. /// Attempted to satisfy a lock-by-blockheight lock with this time.
time: Time, time: MtpInterval,
/// The inner height value of the lock-by-blockheight lock. /// The inner height value of the lock-by-blockheight lock.
height: Height, height: HeightInterval,
} }
impl IncompatibleTimeError { impl IncompatibleTimeError {
/// Returns the time that was erroneously used to try and satisfy a lock-by-blockheight lock. /// Returns the time that was erroneously used to try and satisfy a lock-by-blockheight lock.
pub fn incompatible(&self) -> Time { self.time } pub fn incompatible(&self) -> MtpInterval { self.time }
/// Returns the height value of the lock-by-blockheight lock. /// Returns the height value of the lock-by-blockheight lock.
pub fn expected(&self) -> Height { self.height } pub fn expected(&self) -> HeightInterval { self.height }
} }
impl fmt::Display for IncompatibleTimeError { impl fmt::Display for IncompatibleTimeError {
@ -536,10 +548,10 @@ mod tests {
#[test] #[test]
fn parses_correctly_to_height_or_time() { fn parses_correctly_to_height_or_time() {
let height1 = Height::from(10); let height1 = HeightInterval::from(10);
let height2 = Height::from(11); let height2 = HeightInterval::from(11);
let time1 = Time::from_512_second_intervals(70); let time1 = MtpInterval::from_512_second_intervals(70);
let time2 = Time::from_512_second_intervals(71); let time2 = MtpInterval::from_512_second_intervals(71);
let lock_by_height1 = LockTime::from(height1); let lock_by_height1 = LockTime::from(height1);
let lock_by_height2 = LockTime::from(height2); let lock_by_height2 = LockTime::from(height2);
@ -561,28 +573,34 @@ mod tests {
#[test] #[test]
fn height_correctly_implies() { fn height_correctly_implies() {
let height = Height::from(10); let height = HeightInterval::from(10);
let lock_by_height = LockTime::from(height); let lock_by_height = LockTime::from(height);
assert!(!lock_by_height.is_implied_by(LockTime::from(Height::from(9)))); assert!(!lock_by_height.is_implied_by(LockTime::from(HeightInterval::from(9))));
assert!(lock_by_height.is_implied_by(LockTime::from(Height::from(10)))); assert!(lock_by_height.is_implied_by(LockTime::from(HeightInterval::from(10))));
assert!(lock_by_height.is_implied_by(LockTime::from(Height::from(11)))); assert!(lock_by_height.is_implied_by(LockTime::from(HeightInterval::from(11))));
} }
#[test] #[test]
fn time_correctly_implies() { fn time_correctly_implies() {
let time = Time::from_512_second_intervals(70); let time = MtpInterval::from_512_second_intervals(70);
let lock_by_time = LockTime::from(time); let lock_by_time = LockTime::from(time);
assert!(!lock_by_time.is_implied_by(LockTime::from(Time::from_512_second_intervals(69)))); assert!(
assert!(lock_by_time.is_implied_by(LockTime::from(Time::from_512_second_intervals(70)))); !lock_by_time.is_implied_by(LockTime::from(MtpInterval::from_512_second_intervals(69)))
assert!(lock_by_time.is_implied_by(LockTime::from(Time::from_512_second_intervals(71)))); );
assert!(
lock_by_time.is_implied_by(LockTime::from(MtpInterval::from_512_second_intervals(70)))
);
assert!(
lock_by_time.is_implied_by(LockTime::from(MtpInterval::from_512_second_intervals(71)))
);
} }
#[test] #[test]
fn sequence_correctly_implies() { fn sequence_correctly_implies() {
let height = Height::from(10); let height = HeightInterval::from(10);
let time = Time::from_512_second_intervals(70); let time = MtpInterval::from_512_second_intervals(70);
let lock_by_height = LockTime::from(height); let lock_by_height = LockTime::from(height);
let lock_by_time = LockTime::from(time); let lock_by_time = LockTime::from(time);
@ -603,8 +621,8 @@ mod tests {
#[test] #[test]
fn incorrect_units_do_not_imply() { fn incorrect_units_do_not_imply() {
let time = Time::from_512_second_intervals(70); let time = MtpInterval::from_512_second_intervals(70);
let height = Height::from(10); let height = HeightInterval::from(10);
let lock_by_time = LockTime::from(time); let lock_by_time = LockTime::from(time);
assert!(!lock_by_time.is_implied_by(LockTime::from(height))); assert!(!lock_by_time.is_implied_by(LockTime::from(height)));
@ -643,8 +661,8 @@ mod tests {
#[test] #[test]
fn incompatible_height_error() { fn incompatible_height_error() {
let height = Height::from(10); let height = HeightInterval::from(10);
let time = Time::from_512_second_intervals(70); let time = MtpInterval::from_512_second_intervals(70);
let lock_by_time = LockTime::from(time); let lock_by_time = LockTime::from(time);
let err = lock_by_time.is_satisfied_by_height(height).unwrap_err(); let err = lock_by_time.is_satisfied_by_height(height).unwrap_err();
@ -655,8 +673,8 @@ mod tests {
#[test] #[test]
fn incompatible_time_error() { fn incompatible_time_error() {
let height = Height::from(10); let height = HeightInterval::from(10);
let time = Time::from_512_second_intervals(70); let time = MtpInterval::from_512_second_intervals(70);
let lock_by_height = LockTime::from(height); let lock_by_height = LockTime::from(height);
let err = lock_by_height.is_satisfied_by_time(time).unwrap_err(); let err = lock_by_height.is_satisfied_by_time(time).unwrap_err();
@ -681,16 +699,16 @@ mod tests {
let chain_tip = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps); let chain_tip = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps);
let utxo_mined_at = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps); let utxo_mined_at = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
let lock1 = LockTime::Blocks(Height::from(10)); let lock1 = LockTime::Blocks(HeightInterval::from(10));
assert!(lock1.is_satisfied_by(chain_tip, utxo_mined_at)); assert!(lock1.is_satisfied_by(chain_tip, utxo_mined_at));
let lock2 = LockTime::Blocks(Height::from(21)); let lock2 = LockTime::Blocks(HeightInterval::from(21));
assert!(!lock2.is_satisfied_by(chain_tip, utxo_mined_at)); assert!(!lock2.is_satisfied_by(chain_tip, utxo_mined_at));
let lock3 = LockTime::Time(Time::from_512_second_intervals(10)); let lock3 = LockTime::Time(MtpInterval::from_512_second_intervals(10));
assert!(lock3.is_satisfied_by(chain_tip, utxo_mined_at)); assert!(lock3.is_satisfied_by(chain_tip, utxo_mined_at));
let lock4 = LockTime::Time(Time::from_512_second_intervals(20000)); let lock4 = LockTime::Time(MtpInterval::from_512_second_intervals(20000));
assert!(!lock4.is_satisfied_by(chain_tip, utxo_mined_at)); assert!(!lock4.is_satisfied_by(chain_tip, utxo_mined_at));
assert!(LockTime::ZERO.is_satisfied_by(chain_tip, utxo_mined_at)); assert!(LockTime::ZERO.is_satisfied_by(chain_tip, utxo_mined_at));
@ -699,10 +717,10 @@ mod tests {
let lock6 = LockTime::from_seconds_floor(5000).unwrap(); let lock6 = LockTime::from_seconds_floor(5000).unwrap();
assert!(lock6.is_satisfied_by(chain_tip, utxo_mined_at)); assert!(lock6.is_satisfied_by(chain_tip, utxo_mined_at));
let max_height_lock = LockTime::Blocks(Height::MAX); let max_height_lock = LockTime::Blocks(HeightInterval::MAX);
assert!(!max_height_lock.is_satisfied_by(chain_tip, utxo_mined_at)); assert!(!max_height_lock.is_satisfied_by(chain_tip, utxo_mined_at));
let max_time_lock = LockTime::Time(Time::MAX); let max_time_lock = LockTime::Time(MtpInterval::MAX);
assert!(!max_time_lock.is_satisfied_by(chain_tip, utxo_mined_at)); assert!(!max_time_lock.is_satisfied_by(chain_tip, utxo_mined_at));
let max_chain_tip = let max_chain_tip =

View File

@ -70,7 +70,7 @@ impl Sequence {
/// BIP-68 relative lock time disable flag mask. /// BIP-68 relative lock time disable flag mask.
const LOCK_TIME_DISABLE_FLAG_MASK: u32 = 0x8000_0000; const LOCK_TIME_DISABLE_FLAG_MASK: u32 = 0x8000_0000;
/// BIP-68 relative lock time type flag mask. /// BIP-68 relative lock time type flag mask.
const LOCK_TYPE_MASK: u32 = 0x0040_0000; pub(super) const LOCK_TYPE_MASK: u32 = 0x0040_0000;
/// Returns `true` if the sequence number enables absolute lock-time ([`Transaction::lock_time`]). /// Returns `true` if the sequence number enables absolute lock-time ([`Transaction::lock_time`]).
#[inline] #[inline]
@ -187,7 +187,7 @@ impl Sequence {
/// Constructs a new [`relative::LockTime`] from this [`Sequence`] number. /// Constructs a new [`relative::LockTime`] from this [`Sequence`] number.
#[inline] #[inline]
pub fn to_relative_lock_time(self) -> Option<relative::LockTime> { pub fn to_relative_lock_time(self) -> Option<relative::LockTime> {
use crate::locktime::relative::{Height, LockTime, Time}; use crate::locktime::relative::{HeightInterval, LockTime, MtpInterval};
if !self.is_relative_lock_time() { if !self.is_relative_lock_time() {
return None; return None;
@ -196,9 +196,9 @@ impl Sequence {
let lock_value = self.low_u16(); let lock_value = self.low_u16();
if self.is_time_locked() { if self.is_time_locked() {
Some(LockTime::from(Time::from_512_second_intervals(lock_value))) Some(LockTime::from(MtpInterval::from_512_second_intervals(lock_value)))
} else { } else {
Some(LockTime::from(Height::from(lock_value))) Some(LockTime::from(HeightInterval::from(lock_value)))
} }
} }
@ -261,10 +261,16 @@ impl<'a> Arbitrary<'a> for Sequence {
1 => Ok(Sequence::ZERO), 1 => Ok(Sequence::ZERO),
2 => Ok(Sequence::MIN_NO_RBF), 2 => Ok(Sequence::MIN_NO_RBF),
3 => Ok(Sequence::ENABLE_LOCKTIME_AND_RBF), 3 => Ok(Sequence::ENABLE_LOCKTIME_AND_RBF),
4 => Ok(Sequence::from_consensus(relative::Height::MIN.to_consensus_u32())), 4 => Ok(Sequence::from_consensus(u32::from(relative::HeightInterval::MIN.to_height()))),
5 => Ok(Sequence::from_consensus(relative::Height::MAX.to_consensus_u32())), 5 => Ok(Sequence::from_consensus(u32::from(relative::HeightInterval::MAX.to_height()))),
6 => Ok(Sequence::from_consensus(relative::Time::MIN.to_consensus_u32())), 6 => Ok(Sequence::from_consensus(
7 => Ok(Sequence::from_consensus(relative::Time::MAX.to_consensus_u32())), Sequence::LOCK_TYPE_MASK
| u32::from(relative::MtpInterval::MIN.to_512_second_intervals()),
)),
7 => Ok(Sequence::from_consensus(
Sequence::LOCK_TYPE_MASK
| u32::from(relative::MtpInterval::MAX.to_512_second_intervals()),
)),
_ => Ok(Sequence(u.arbitrary()?)), _ => Ok(Sequence(u.arbitrary()?)),
} }
} }

View File

@ -70,7 +70,7 @@ impl From<absolute::Height> for BlockHeight {
/// An absolute locktime block height has a maximum value of [`absolute::LOCK_TIME_THRESHOLD`] /// An absolute locktime block height has a maximum value of [`absolute::LOCK_TIME_THRESHOLD`]
/// (500,000,000) where as a [`BlockHeight`] is a thin wrapper around a `u32`, the two types are /// (500,000,000) where as a [`BlockHeight`] is a thin wrapper around a `u32`, the two types are
/// not interchangeable. /// not interchangeable.
fn from(h: absolute::Height) -> Self { Self::from_u32(h.to_consensus_u32()) } fn from(h: absolute::Height) -> Self { Self::from_u32(h.to_u32()) }
} }
impl TryFrom<BlockHeight> for absolute::Height { impl TryFrom<BlockHeight> for absolute::Height {
@ -82,7 +82,7 @@ impl TryFrom<BlockHeight> for absolute::Height {
/// (500,000,000) where as a [`BlockHeight`] is a thin wrapper around a `u32`, the two types are /// (500,000,000) where as a [`BlockHeight`] is a thin wrapper around a `u32`, the two types are
/// not interchangeable. /// not interchangeable.
fn try_from(h: BlockHeight) -> Result<Self, Self::Error> { fn try_from(h: BlockHeight) -> Result<Self, Self::Error> {
absolute::Height::from_consensus(h.to_u32()) absolute::Height::from_u32(h.to_u32())
} }
} }
@ -129,47 +129,48 @@ impl From<BlockInterval> for u32 {
fn from(height: BlockInterval) -> Self { height.to_u32() } fn from(height: BlockInterval) -> Self { height.to_u32() }
} }
impl From<relative::Height> for BlockInterval { impl From<relative::HeightInterval> for BlockInterval {
/// Converts a [`locktime::relative::Height`] to a [`BlockInterval`]. /// Converts a [`locktime::relative::HeightInterval`] to a [`BlockInterval`].
/// ///
/// A relative locktime block height has a maximum value of `u16::MAX` where as a /// A relative locktime block height has a maximum value of `u16::MAX` where as a
/// [`BlockInterval`] is a thin wrapper around a `u32`, the two types are not interchangeable. /// [`BlockInterval`] is a thin wrapper around a `u32`, the two types are not interchangeable.
fn from(h: relative::Height) -> Self { Self::from_u32(h.value().into()) } fn from(h: relative::HeightInterval) -> Self { Self::from_u32(h.to_height().into()) }
} }
impl TryFrom<BlockInterval> for relative::Height { impl TryFrom<BlockInterval> for relative::HeightInterval {
type Error = TooBigForRelativeBlockHeightError; type Error = TooBigForRelativeBlockHeightIntervalError;
/// Converts a [`BlockInterval`] to a [`locktime::relative::Height`]. /// Converts a [`BlockInterval`] to a [`locktime::relative::HeightInterval`].
/// ///
/// A relative locktime block height has a maximum value of `u16::MAX` where as a /// A relative locktime block height has a maximum value of `u16::MAX` where as a
/// [`BlockInterval`] is a thin wrapper around a `u32`, the two types are not interchangeable. /// [`BlockInterval`] is a thin wrapper around a `u32`, the two types are not interchangeable.
fn try_from(h: BlockInterval) -> Result<Self, Self::Error> { fn try_from(h: BlockInterval) -> Result<Self, Self::Error> {
let h = h.to_u32(); let h = h.to_u32();
if h > u32::from(u16::MAX) { if h > u32::from(u16::MAX) {
return Err(TooBigForRelativeBlockHeightError(h)); return Err(TooBigForRelativeBlockHeightIntervalError(h));
} }
Ok(relative::Height::from(h as u16)) // Cast ok, value checked above. Ok(relative::HeightInterval::from(h as u16)) // Cast ok, value checked above.
} }
} }
/// Error returned when the block interval is too big to be used as a relative lock time. /// Error returned when the block interval is too big to be used as a relative lock time.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct TooBigForRelativeBlockHeightError(u32); pub struct TooBigForRelativeBlockHeightIntervalError(u32);
impl fmt::Display for TooBigForRelativeBlockHeightError { impl fmt::Display for TooBigForRelativeBlockHeightIntervalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
"block interval is too big to be used as a relative lock time: {} (max: {})", "block interval is too big to be used as a relative lock time: {} (max: {})",
self.0, self.0,
relative::Height::MAX relative::HeightInterval::MAX
) )
} }
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl std::error::Error for TooBigForRelativeBlockHeightError {} impl std::error::Error for TooBigForRelativeBlockHeightIntervalError {}
crate::internal_macros::impl_op_for_references! { crate::internal_macros::impl_op_for_references! {
// height - height = interval // height - height = interval
@ -279,14 +280,14 @@ mod tests {
let interval: u32 = BlockInterval(100).into(); let interval: u32 = BlockInterval(100).into();
assert_eq!(interval, 100); assert_eq!(interval, 100);
let interval_from_height: BlockInterval = relative::Height::from(10u16).into(); let interval_from_height: BlockInterval = relative::HeightInterval::from(10u16).into();
assert_eq!(interval_from_height.to_u32(), 10u32); assert_eq!(interval_from_height.to_u32(), 10u32);
let invalid_height_greater = let invalid_height_greater =
relative::Height::try_from(BlockInterval(u32::from(u16::MAX) + 1)); relative::HeightInterval::try_from(BlockInterval(u32::from(u16::MAX) + 1));
assert!(invalid_height_greater.is_err()); assert!(invalid_height_greater.is_err());
let valid_height = relative::Height::try_from(BlockInterval(u32::from(u16::MAX))); let valid_height = relative::HeightInterval::try_from(BlockInterval(u32::from(u16::MAX)));
assert!(valid_height.is_ok()); assert!(valid_height.is_ok());
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
//! Provides [`Height`] and [`Time`] types used by the `rust-bitcoin` `absolute::LockTime` type. //! Provides [`Height`] and [`Mtp`] types used by the `rust-bitcoin` `absolute::LockTime` type.
use core::convert::Infallible; use core::convert::Infallible;
use core::fmt; use core::fmt;
@ -45,15 +45,22 @@ impl Height {
/// # Errors /// # Errors
/// ///
/// If the input string is not a valid hex representation of a block height. /// If the input string is not a valid hex representation of a block height.
pub fn from_hex(s: &str) -> Result<Self, ParseHeightError> { pub fn from_hex(s: &str) -> Result<Self, ParseHeightError> { parse_hex(s, Self::from_u32) }
parse_hex(s, Self::from_consensus)
}
/// Constructs a new block height. #[deprecated(since = "TBD", note = "use `from_u32` instead")]
#[doc(hidden)]
pub const fn from_consensus(n: u32) -> Result<Self, ConversionError> { Self::from_u32(n) }
#[deprecated(since = "TBD", note = "use `to_u32` instead")]
#[doc(hidden)]
pub const fn to_consensus_u32(self) -> u32 { self.to_u32() }
/// Constructs a new block height directly from a `u32` value.
/// ///
/// # Errors /// # Errors
/// ///
/// If `n` does not represent a valid block height value. /// If `n` does not represent a block height within the valid range for a locktime:
/// `[0, 499_999_999]`.
/// ///
/// # Examples /// # Examples
/// ///
@ -61,11 +68,11 @@ impl Height {
/// use bitcoin_units::locktime::absolute::Height; /// use bitcoin_units::locktime::absolute::Height;
/// ///
/// let h: u32 = 741521; /// let h: u32 = 741521;
/// let height = Height::from_consensus(h).expect("invalid height value"); /// let height = Height::from_u32(h).expect("invalid height value");
/// assert_eq!(height.to_consensus_u32(), h); /// assert_eq!(height.to_consensus_u32(), h);
/// ``` /// ```
#[inline] #[inline]
pub const fn from_consensus(n: u32) -> Result<Height, ConversionError> { pub const fn from_u32(n: u32) -> Result<Height, ConversionError> {
if is_block_height(n) { if is_block_height(n) {
Ok(Self(n)) Ok(Self(n))
} else { } else {
@ -73,16 +80,16 @@ impl Height {
} }
} }
/// Converts this [`Height`] to its inner `u32` value. /// Converts this [`Height`] to a raw `u32` value.
#[inline] #[inline]
pub const fn to_consensus_u32(self) -> u32 { self.0 } pub const fn to_u32(self) -> u32 { self.0 }
} }
impl fmt::Display for Height { impl fmt::Display for Height {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
} }
crate::impl_parse_str!(Height, ParseHeightError, parser(Height::from_consensus)); crate::impl_parse_str!(Height, ParseHeightError, parser(Height::from_u32));
/// Error returned when parsing block height fails. /// Error returned when parsing block height fails.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -111,7 +118,7 @@ impl<'de> serde::Deserialize<'de> for Height {
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
let u = u32::deserialize(deserializer)?; let u = u32::deserialize(deserializer)?;
Height::from_consensus(u).map_err(serde::de::Error::custom) Height::from_u32(u).map_err(serde::de::Error::custom)
} }
} }
@ -121,51 +128,69 @@ impl serde::Serialize for Height {
where where
S: serde::Serializer, S: serde::Serializer,
{ {
self.to_consensus_u32().serialize(serializer) self.to_u32().serialize(serializer)
} }
} }
/// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value. #[deprecated(since = "TBD", note = "use `Mtp` instead")]
#[doc(hidden)]
pub type Time = Mtp;
/// The median timestamp of 11 consecutive blocks, representing "the timestamp" of the
/// final block for locktime-checking purposes.
/// ///
/// Note that there is no manipulation of the inner value during construction or when using /// Time-based locktimes are not measured against the timestamps in individual block
/// `to_consensus_u32()`. Said another way, `Time(x)` means 'x seconds since epoch' _not_ '(x - /// headers, since these are not monotone and may be subject to miner manipulation.
/// threshold) seconds since epoch'. /// Instead, locktimes use the "median-time-past" (MTP) of the most recent 11 blocks,
/// a quantity which is required by consensus to be monotone and which is difficult
/// for any individual miner to manipulate.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Time(u32); pub struct Mtp(u32);
impl Time { impl Mtp {
/// The minimum absolute block time (Tue Nov 05 1985 00:53:20 GMT+0000). /// The minimum MTP allowable in a locktime (Tue Nov 05 1985 00:53:20 GMT+0000).
pub const MIN: Self = Time(LOCK_TIME_THRESHOLD); pub const MIN: Self = Mtp(LOCK_TIME_THRESHOLD);
/// The maximum absolute block time (Sun Feb 07 2106 06:28:15 GMT+0000). /// The maximum MTP allowable in a locktime (Sun Feb 07 2106 06:28:15 GMT+0000).
pub const MAX: Self = Time(u32::MAX); pub const MAX: Self = Mtp(u32::MAX);
/// Constructs a new [`Time`] from a hex string. /// Constructs a new [`Mtp`] from a big-endian hex-encoded `u32`.
/// ///
/// The input string may or may not contain a typical hex prefix e.g., `0x`. /// The input string may or may not contain a typical hex prefix e.g., `0x`.
/// ///
/// # Errors /// # Errors
/// ///
/// If the input string is not a valid hex representation of a block time. /// If the input string is not a valid hex representation of a block time.
pub fn from_hex(s: &str) -> Result<Self, ParseTimeError> { parse_hex(s, Self::from_consensus) } pub fn from_hex(s: &str) -> Result<Self, ParseTimeError> { parse_hex(s, Self::from_u32) }
/// Constructs a new block time. #[deprecated(since = "TBD", note = "use `from_u32` instead")]
#[doc(hidden)]
pub const fn from_consensus(n: u32) -> Result<Self, ConversionError> { Self::from_u32(n) }
#[deprecated(since = "TBD", note = "use `to_u32` instead")]
#[doc(hidden)]
pub const fn to_consensus_u32(self) -> u32 { self.to_u32() }
/// Constructs a new MTP directly from a `u32` value.
///
/// This function, with [`Mtp::to_u32`], is used to obtain a raw MTP value. It is
/// **not** used to convert to or from a block timestamp, which is not a MTP.
/// ///
/// # Errors /// # Errors
/// ///
/// If `n` does not encode a valid UNIX time stamp. /// If `n` is not in the allowable range of MTPs in a locktime: `[500_000_000, 2^32 - 1]`.
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use bitcoin_units::locktime::absolute::Time; /// use bitcoin_units::locktime::absolute::Mtp;
/// ///
/// let t: u32 = 1653195600; // May 22nd, 5am UTC. /// let t: u32 = 1653195600; // May 22nd, 5am UTC.
/// let time = Time::from_consensus(t).expect("invalid time value"); /// let time = Mtp::from_u32(t).expect("invalid time value");
/// assert_eq!(time.to_consensus_u32(), t); /// assert_eq!(time.to_consensus_u32(), t);
/// ``` /// ```
#[inline] #[inline]
pub const fn from_consensus(n: u32) -> Result<Time, ConversionError> { pub const fn from_u32(n: u32) -> Result<Self, ConversionError> {
if is_block_time(n) { if is_block_time(n) {
Ok(Self(n)) Ok(Self(n))
} else { } else {
@ -173,35 +198,35 @@ impl Time {
} }
} }
/// Converts this [`Time`] to its inner `u32` value. /// Converts this [`Mtp`] to a raw `u32` value.
#[inline] #[inline]
pub const fn to_consensus_u32(self) -> u32 { self.0 } pub const fn to_u32(self) -> u32 { self.0 }
} }
impl fmt::Display for Time { impl fmt::Display for Mtp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
} }
crate::impl_parse_str!(Time, ParseTimeError, parser(Time::from_consensus)); crate::impl_parse_str!(Mtp, ParseTimeError, parser(Mtp::from_u32));
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Time { impl<'de> serde::Deserialize<'de> for Mtp {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
let u = u32::deserialize(deserializer)?; let u = u32::deserialize(deserializer)?;
Time::from_consensus(u).map_err(serde::de::Error::custom) Mtp::from_u32(u).map_err(serde::de::Error::custom)
} }
} }
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
impl serde::Serialize for Time { impl serde::Serialize for Mtp {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: serde::Serializer, S: serde::Serializer,
{ {
self.to_consensus_u32().serialize(serializer) self.to_u32().serialize(serializer)
} }
} }
@ -342,11 +367,11 @@ impl ParseError {
E::ParseInt(ParseIntError { input, bits: _, is_signed: _, source }) E::ParseInt(ParseIntError { input, bits: _, is_signed: _, source })
if *source.kind() == IntErrorKind::PosOverflow => if *source.kind() == IntErrorKind::PosOverflow =>
{ {
// Outputs "failed to parse <input_string> as absolute Height/Time (<subject> is above limit <upper_bound>)" // Outputs "failed to parse <input_string> as absolute Height/Mtp (<subject> is above limit <upper_bound>)"
write!( write!(
f, f,
"{} ({} is above limit {})", "{} ({} is above limit {})",
input.display_cannot_parse("absolute Height/Time"), input.display_cannot_parse("absolute Height/Mtp"),
subject, subject,
upper_bound upper_bound
) )
@ -354,17 +379,17 @@ impl ParseError {
E::ParseInt(ParseIntError { input, bits: _, is_signed: _, source }) E::ParseInt(ParseIntError { input, bits: _, is_signed: _, source })
if *source.kind() == IntErrorKind::NegOverflow => if *source.kind() == IntErrorKind::NegOverflow =>
{ {
// Outputs "failed to parse <input_string> as absolute Height/Time (<subject> is below limit <lower_bound>)" // Outputs "failed to parse <input_string> as absolute Height/Mtp (<subject> is below limit <lower_bound>)"
write!( write!(
f, f,
"{} ({} is below limit {})", "{} ({} is below limit {})",
input.display_cannot_parse("absolute Height/Time"), input.display_cannot_parse("absolute Height/Mtp"),
subject, subject,
lower_bound lower_bound
) )
} }
E::ParseInt(ParseIntError { input, bits: _, is_signed: _, source: _ }) => { E::ParseInt(ParseIntError { input, bits: _, is_signed: _, source: _ }) => {
write!(f, "{} ({})", input.display_cannot_parse("absolute Height/Time"), subject) write!(f, "{} ({})", input.display_cannot_parse("absolute Height/Mtp"), subject)
} }
E::Conversion(value) if *value < i64::from(lower_bound) => { E::Conversion(value) if *value < i64::from(lower_bound) => {
write!(f, "{} {} is below limit {}", subject, value, lower_bound) write!(f, "{} {} is below limit {}", subject, value, lower_bound)
@ -407,27 +432,27 @@ impl<'a> Arbitrary<'a> for Height {
0 => Ok(Height::MIN), 0 => Ok(Height::MIN),
1 => Ok(Height::MAX), 1 => Ok(Height::MAX),
_ => { _ => {
let min = Height::MIN.to_consensus_u32(); let min = Height::MIN.to_u32();
let max = Height::MAX.to_consensus_u32(); let max = Height::MAX.to_u32();
let h = u.int_in_range(min..=max)?; let h = u.int_in_range(min..=max)?;
Ok(Height::from_consensus(h).unwrap()) Ok(Height::from_u32(h).unwrap())
} }
} }
} }
} }
#[cfg(feature = "arbitrary")] #[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Time { impl<'a> Arbitrary<'a> for Mtp {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let choice = u.int_in_range(0..=2)?; let choice = u.int_in_range(0..=2)?;
match choice { match choice {
0 => Ok(Time::MIN), 0 => Ok(Mtp::MIN),
1 => Ok(Time::MAX), 1 => Ok(Mtp::MAX),
_ => { _ => {
let min = Time::MIN.to_consensus_u32(); let min = Mtp::MIN.to_u32();
let max = Time::MAX.to_consensus_u32(); let max = Mtp::MAX.to_u32();
let t = u.int_in_range(min..=max)?; let t = u.int_in_range(min..=max)?;
Ok(Time::from_consensus(t).unwrap()) Ok(Mtp::from_u32(t).unwrap())
} }
} }
} }
@ -442,21 +467,21 @@ mod tests {
#[test] #[test]
fn time_from_str_hex_happy_path() { fn time_from_str_hex_happy_path() {
let actual = Time::from_hex("0x6289C350").unwrap(); let actual = Mtp::from_hex("0x6289C350").unwrap();
let expected = Time::from_consensus(0x6289_C350).unwrap(); let expected = Mtp::from_u32(0x6289_C350).unwrap();
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn time_from_str_hex_no_prefix_happy_path() { fn time_from_str_hex_no_prefix_happy_path() {
let time = Time::from_hex("6289C350").unwrap(); let time = Mtp::from_hex("6289C350").unwrap();
assert_eq!(time, Time(0x6289_C350)); assert_eq!(time, Mtp(0x6289_C350));
} }
#[test] #[test]
fn time_from_str_hex_invalid_hex_should_err() { fn time_from_str_hex_invalid_hex_should_err() {
let hex = "0xzb93"; let hex = "0xzb93";
let result = Time::from_hex(hex); let result = Mtp::from_hex(hex);
assert!(result.is_err()); assert!(result.is_err());
} }
@ -500,8 +525,8 @@ mod tests {
#[test] #[test]
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
pub fn encode_decode_time() { pub fn encode_decode_time() {
serde_round_trip!(Time::MIN); serde_round_trip!(Mtp::MIN);
serde_round_trip!(Time::MAX); serde_round_trip!(Mtp::MAX);
} }
#[test] #[test]

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
//! Provides [`Height`] and [`Time`] types used by the `rust-bitcoin` `relative::LockTime` type. //! Provides [`Height`] and [`MtpInterval`] types used by the `rust-bitcoin` `relative::LockTime` type.
use core::fmt; use core::fmt;
@ -11,33 +11,47 @@ use serde::{Deserialize, Serialize};
use crate::mtp_height::MtpAndHeight; use crate::mtp_height::MtpAndHeight;
#[deprecated(since = "TBD", note = "use `HeightIterval` instead")]
#[doc(hidden)]
pub type Height = HeightInterval;
/// A relative lock time lock-by-blockheight value. /// A relative lock time lock-by-blockheight value.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Height(u16); pub struct HeightInterval(u16);
impl Height { impl HeightInterval {
/// Relative block height 0, can be included in any block. /// Relative block height 0, can be included in any block.
pub const ZERO: Self = Height(0); pub const ZERO: Self = Self(0);
/// The minimum relative block height (0), can be included in any block. /// The minimum relative block height (0), can be included in any block.
pub const MIN: Self = Self::ZERO; pub const MIN: Self = Self::ZERO;
/// The maximum relative block height. /// The maximum relative block height.
pub const MAX: Self = Height(u16::MAX); pub const MAX: Self = Self(u16::MAX);
/// Constructs a new [`Height`] using a count of blocks. /// Constructs a new [`HeightInterval`] using a count of blocks.
#[inline] #[inline]
pub const fn from_height(blocks: u16) -> Self { Height(blocks) } pub const fn from_height(blocks: u16) -> Self { Self(blocks) }
/// Express the [`Height`] as a count of blocks.
#[inline]
#[must_use]
pub const fn to_height(self) -> u16 { self.0 }
/// Returns the inner `u16` value. /// Returns the inner `u16` value.
#[inline] #[inline]
#[must_use] #[must_use]
#[deprecated(since = "TBD", note = "use `to_height` instead")]
#[doc(hidden)]
pub const fn value(self) -> u16 { self.0 } pub const fn value(self) -> u16 { self.0 }
/// Returns the `u32` value used to encode this locktime in an nSequence field or /// Returns the `u32` value used to encode this locktime in an nSequence field or
/// argument to `OP_CHECKSEQUENCEVERIFY`. /// argument to `OP_CHECKSEQUENCEVERIFY`.
#[inline] #[deprecated(
since = "TBD",
note = "use `LockTime::from` followed by `to_consensus_u32` instead"
)]
pub const fn to_consensus_u32(self) -> u32 { pub const fn to_consensus_u32(self) -> u32 {
self.0 as u32 // cast safety: u32 is wider than u16 on all architectures self.0 as u32 // cast safety: u32 is wider than u16 on all architectures
} }
@ -56,48 +70,57 @@ impl Height {
pub fn is_satisfied_by(self, chain_tip: MtpAndHeight, utxo_mined_at: MtpAndHeight) -> bool { pub fn is_satisfied_by(self, chain_tip: MtpAndHeight, utxo_mined_at: MtpAndHeight) -> bool {
// let chain_tip_height = BlockHeight::from(chain_tip); // let chain_tip_height = BlockHeight::from(chain_tip);
// let utxo_mined_at_height = BlockHeight::from(utxo_mined_at); // let utxo_mined_at_height = BlockHeight::from(utxo_mined_at);
match self.to_consensus_u32().checked_add(utxo_mined_at.to_height().to_u32()) { match u32::from(self.to_height()).checked_add(utxo_mined_at.to_height().to_u32()) {
Some(target_height) => chain_tip.to_height().to_u32() >= target_height, Some(target_height) => chain_tip.to_height().to_u32() >= target_height,
None => false, None => false,
} }
} }
} }
impl From<u16> for Height { impl From<u16> for HeightInterval {
#[inline] #[inline]
fn from(value: u16) -> Self { Height(value) } fn from(value: u16) -> Self { HeightInterval(value) }
} }
crate::impl_parse_str_from_int_infallible!(Height, u16, from); crate::impl_parse_str_from_int_infallible!(HeightInterval, u16, from);
impl fmt::Display for Height { impl fmt::Display for HeightInterval {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
} }
#[deprecated(since = "TBD", note = "use `Mtp` instead")]
#[doc(hidden)]
pub type Time = MtpInterval;
/// A relative lock time lock-by-blocktime value. /// A relative lock time lock-by-blocktime value.
/// ///
/// For BIP 68 relative lock-by-blocktime locks, time is measured in 512 second intervals. /// For BIP 68 relative lock-by-blocktime locks, time is measured in 512 second intervals.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Time(u16); pub struct MtpInterval(u16);
impl Time { impl MtpInterval {
/// Relative block time 0, can be included in any block. /// Relative block time 0, can be included in any block.
pub const ZERO: Self = Time(0); pub const ZERO: Self = MtpInterval(0);
/// The minimum relative block time (0), can be included in any block. /// The minimum relative block time (0), can be included in any block.
pub const MIN: Self = Time::ZERO; pub const MIN: Self = MtpInterval::ZERO;
/// The maximum relative block time (33,554,432 seconds or approx 388 days). /// The maximum relative block time (33,554,432 seconds or approx 388 days).
pub const MAX: Self = Time(u16::MAX); pub const MAX: Self = MtpInterval(u16::MAX);
/// Constructs a new [`Time`] using time intervals where each interval is equivalent to 512 seconds. /// Constructs a new [`MtpInterval`] using time intervals where each interval is equivalent to 512 seconds.
/// ///
/// Encoding finer granularity of time for relative lock-times is not supported in Bitcoin. /// Encoding finer granularity of time for relative lock-times is not supported in Bitcoin.
#[inline] #[inline]
pub const fn from_512_second_intervals(intervals: u16) -> Self { Time(intervals) } pub const fn from_512_second_intervals(intervals: u16) -> Self { MtpInterval(intervals) }
/// Constructs a new [`Time`] from seconds, converting the seconds into 512 second interval with /// Express the [`MtpInterval`] as an integer number of 512-second intervals.
#[inline]
#[must_use]
pub const fn to_512_second_intervals(self) -> u16 { self.0 }
/// Constructs a new [`MtpInterval`] from seconds, converting the seconds into 512 second interval with
/// truncating division. /// truncating division.
/// ///
/// # Errors /// # Errors
@ -108,13 +131,13 @@ impl Time {
pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> { pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
let interval = seconds / 512; let interval = seconds / 512;
if interval <= u16::MAX as u32 { // infallible cast, needed by const code if interval <= u16::MAX as u32 { // infallible cast, needed by const code
Ok(Time::from_512_second_intervals(interval as u16)) // Cast checked above, needed by const code. Ok(MtpInterval::from_512_second_intervals(interval as u16)) // Cast checked above, needed by const code.
} else { } else {
Err(TimeOverflowError { seconds }) Err(TimeOverflowError { seconds })
} }
} }
/// Constructs a new [`Time`] from seconds, converting the seconds into 512 second intervals with /// Constructs a new [`MtpInterval`] from seconds, converting the seconds into 512 second intervals with
/// ceiling division. /// ceiling division.
/// ///
/// # Errors /// # Errors
@ -125,7 +148,7 @@ impl Time {
pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> { pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
if seconds <= u16::MAX as u32 * 512 { if seconds <= u16::MAX as u32 * 512 {
let interval = (seconds + 511) / 512; let interval = (seconds + 511) / 512;
Ok(Time::from_512_second_intervals(interval as u16)) // Cast checked above, needed by const code. Ok(MtpInterval::from_512_second_intervals(interval as u16)) // Cast checked above, needed by const code.
} else { } else {
Err(TimeOverflowError { seconds }) Err(TimeOverflowError { seconds })
} }
@ -134,11 +157,16 @@ impl Time {
/// Returns the inner `u16` value. /// Returns the inner `u16` value.
#[inline] #[inline]
#[must_use] #[must_use]
#[deprecated(since = "TBD", note = "use `to_512_second_intervals` instead")]
#[doc(hidden)]
pub const fn value(self) -> u16 { self.0 } pub const fn value(self) -> u16 { self.0 }
/// Returns the `u32` value used to encode this locktime in an nSequence field or /// Returns the `u32` value used to encode this locktime in an nSequence field or
/// argument to `OP_CHECKSEQUENCEVERIFY`. /// argument to `OP_CHECKSEQUENCEVERIFY`.
#[inline] #[deprecated(
since = "TBD",
note = "use `LockTime::from` followed by `to_consensus_u32` instead"
)]
pub const fn to_consensus_u32(self) -> u32 { pub const fn to_consensus_u32(self) -> u32 {
(1u32 << 22) | self.0 as u32 // cast safety: u32 is wider than u16 on all architectures (1u32 << 22) | self.0 as u32 // cast safety: u32 is wider than u16 on all architectures
} }
@ -155,7 +183,7 @@ impl Time {
/// - `true` if the relativetime lock has expired by the tips MTP /// - `true` if the relativetime lock has expired by the tips MTP
/// - `false` if the lock has not yet expired by the tips MTP /// - `false` if the lock has not yet expired by the tips MTP
pub fn is_satisfied_by(self, chain_tip: MtpAndHeight, utxo_mined_at: MtpAndHeight) -> bool { pub fn is_satisfied_by(self, chain_tip: MtpAndHeight, utxo_mined_at: MtpAndHeight) -> bool {
match u32::from(self.value()).checked_mul(512) { match u32::from(self.to_512_second_intervals()).checked_mul(512) {
Some(seconds) => match seconds.checked_add(utxo_mined_at.to_mtp().to_u32()) { Some(seconds) => match seconds.checked_add(utxo_mined_at.to_mtp().to_u32()) {
Some(required_seconds) => chain_tip.to_mtp().to_u32() >= required_seconds, Some(required_seconds) => chain_tip.to_mtp().to_u32() >= required_seconds,
None => false, None => false,
@ -165,16 +193,16 @@ impl Time {
} }
} }
crate::impl_parse_str_from_int_infallible!(Time, u16, from_512_second_intervals); crate::impl_parse_str_from_int_infallible!(MtpInterval, u16, from_512_second_intervals);
impl fmt::Display for Time { impl fmt::Display for MtpInterval {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
} }
/// Error returned when the input time in seconds was too large to be encoded to a 16 bit 512 second interval. /// Error returned when the input time in seconds was too large to be encoded to a 16 bit 512 second interval.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct TimeOverflowError { pub struct TimeOverflowError {
/// Time value in seconds that overflowed. /// Time interval value in seconds that overflowed.
// Private because we maintain an invariant that the `seconds` value does actually overflow. // Private because we maintain an invariant that the `seconds` value does actually overflow.
pub(crate) seconds: u32, pub(crate) seconds: u32,
} }
@ -205,27 +233,27 @@ impl fmt::Display for TimeOverflowError {
impl std::error::Error for TimeOverflowError {} impl std::error::Error for TimeOverflowError {}
#[cfg(feature = "arbitrary")] #[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Height { impl<'a> Arbitrary<'a> for HeightInterval {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let choice = u.int_in_range(0..=2)?; let choice = u.int_in_range(0..=2)?;
match choice { match choice {
0 => Ok(Height::MIN), 0 => Ok(HeightInterval::MIN),
1 => Ok(Height::MAX), 1 => Ok(HeightInterval::MAX),
_ => Ok(Height::from_height(u16::arbitrary(u)?)), _ => Ok(HeightInterval::from_height(u16::arbitrary(u)?)),
} }
} }
} }
#[cfg(feature = "arbitrary")] #[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Time { impl<'a> Arbitrary<'a> for MtpInterval {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let choice = u.int_in_range(0..=2)?; let choice = u.int_in_range(0..=2)?;
match choice { match choice {
0 => Ok(Time::MIN), 0 => Ok(MtpInterval::MIN),
1 => Ok(Time::MAX), 1 => Ok(MtpInterval::MAX),
_ => Ok(Time::from_512_second_intervals(u16::arbitrary(u)?)), _ => Ok(MtpInterval::from_512_second_intervals(u16::arbitrary(u)?)),
} }
} }
} }
@ -241,73 +269,74 @@ mod tests {
const MAXIMUM_ENCODABLE_SECONDS: u32 = u16::MAX as u32 * 512; const MAXIMUM_ENCODABLE_SECONDS: u32 = u16::MAX as u32 * 512;
#[test] #[test]
#[allow(deprecated_in_future)]
fn sanity_check() { fn sanity_check() {
assert_eq!(Height::MAX.to_consensus_u32(), u32::from(u16::MAX)); assert_eq!(HeightInterval::MAX.to_consensus_u32(), u32::from(u16::MAX));
assert_eq!(Time::from_512_second_intervals(100).value(), 100u16); assert_eq!(MtpInterval::from_512_second_intervals(100).value(), 100u16);
assert_eq!(Time::from_512_second_intervals(100).to_consensus_u32(), 4_194_404u32); // 0x400064 assert_eq!(MtpInterval::from_512_second_intervals(100).to_consensus_u32(), 4_194_404u32); // 0x400064
} }
#[test] #[test]
fn from_seconds_ceil_success() { fn from_seconds_ceil_success() {
let actual = Time::from_seconds_ceil(100).unwrap(); let actual = MtpInterval::from_seconds_ceil(100).unwrap();
let expected = Time(1_u16); let expected = MtpInterval(1_u16);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn from_seconds_ceil_with_maximum_encodable_seconds_success() { fn from_seconds_ceil_with_maximum_encodable_seconds_success() {
let actual = Time::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS).unwrap(); let actual = MtpInterval::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS).unwrap();
let expected = Time(u16::MAX); let expected = MtpInterval(u16::MAX);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn from_seconds_ceil_causes_time_overflow_error() { fn from_seconds_ceil_causes_time_overflow_error() {
let result = Time::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS + 1); let result = MtpInterval::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS + 1);
assert!(result.is_err()); assert!(result.is_err());
} }
#[test] #[test]
fn from_seconds_floor_success() { fn from_seconds_floor_success() {
let actual = Time::from_seconds_floor(100).unwrap(); let actual = MtpInterval::from_seconds_floor(100).unwrap();
let expected = Time(0_u16); let expected = MtpInterval(0_u16);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn from_seconds_floor_with_exact_interval() { fn from_seconds_floor_with_exact_interval() {
let actual = Time::from_seconds_floor(512).unwrap(); let actual = MtpInterval::from_seconds_floor(512).unwrap();
let expected = Time(1_u16); let expected = MtpInterval(1_u16);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn from_seconds_floor_with_maximum_encodable_seconds_success() { fn from_seconds_floor_with_maximum_encodable_seconds_success() {
let actual = Time::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 511).unwrap(); let actual = MtpInterval::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 511).unwrap();
let expected = Time(u16::MAX); let expected = MtpInterval(u16::MAX);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn from_seconds_floor_causes_time_overflow_error() { fn from_seconds_floor_causes_time_overflow_error() {
let result = Time::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 512); let result = MtpInterval::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 512);
assert!(result.is_err()); assert!(result.is_err());
} }
#[test] #[test]
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
pub fn encode_decode_height() { pub fn encode_decode_height() {
serde_round_trip!(Height::ZERO); serde_round_trip!(HeightInterval::ZERO);
serde_round_trip!(Height::MIN); serde_round_trip!(HeightInterval::MIN);
serde_round_trip!(Height::MAX); serde_round_trip!(HeightInterval::MAX);
} }
#[test] #[test]
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
pub fn encode_decode_time() { pub fn encode_decode_time() {
serde_round_trip!(Time::ZERO); serde_round_trip!(MtpInterval::ZERO);
serde_round_trip!(Time::MIN); serde_round_trip!(MtpInterval::MIN);
serde_round_trip!(Time::MAX); serde_round_trip!(MtpInterval::MAX);
} }
fn generate_timestamps(start: u32, step: u16) -> [BlockTime; 11] { fn generate_timestamps(start: u32, step: u16) -> [BlockTime; 11] {
@ -331,7 +360,7 @@ mod tests {
// Test case 1: Satisfaction (current_mtp >= utxo_mtp + required_seconds) // Test case 1: Satisfaction (current_mtp >= utxo_mtp + required_seconds)
// 10 intervals × 512 seconds = 5120 seconds // 10 intervals × 512 seconds = 5120 seconds
let time_lock = Time::from_512_second_intervals(10); let time_lock = MtpInterval::from_512_second_intervals(10);
let chain_state1 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps); let chain_state1 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps);
let utxo_state1 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps); let utxo_state1 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
assert!(time_lock.is_satisfied_by(chain_state1, utxo_state1)); assert!(time_lock.is_satisfied_by(chain_state1, utxo_state1));
@ -342,13 +371,13 @@ mod tests {
assert!(!time_lock.is_satisfied_by(chain_state2, utxo_state2)); assert!(!time_lock.is_satisfied_by(chain_state2, utxo_state2));
// Test case 3: Test with a larger value (100 intervals = 51200 seconds) // Test case 3: Test with a larger value (100 intervals = 51200 seconds)
let larger_lock = Time::from_512_second_intervals(100); let larger_lock = MtpInterval::from_512_second_intervals(100);
let chain_state3 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps3); let chain_state3 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps3);
let utxo_state3 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps3); let utxo_state3 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps3);
assert!(larger_lock.is_satisfied_by(chain_state3, utxo_state3)); assert!(larger_lock.is_satisfied_by(chain_state3, utxo_state3));
// Test case 4: Overflow handling - tests that is_satisfied_by handles overflow gracefully // Test case 4: Overflow handling - tests that is_satisfied_by handles overflow gracefully
let max_time_lock = Time::MAX; let max_time_lock = MtpInterval::MAX;
let chain_state4 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps); let chain_state4 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps);
let utxo_state4 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps); let utxo_state4 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
assert!(!max_time_lock.is_satisfied_by(chain_state4, utxo_state4)); assert!(!max_time_lock.is_satisfied_by(chain_state4, utxo_state4));
@ -359,7 +388,7 @@ mod tests {
let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200); let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200);
let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200); let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
let height_lock = Height(10); let height_lock = HeightInterval(10);
// Test case 1: Satisfaction (current_height >= utxo_height + required) // Test case 1: Satisfaction (current_height >= utxo_height + required)
let chain_state1 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps); let chain_state1 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps);
@ -372,7 +401,7 @@ mod tests {
assert!(!height_lock.is_satisfied_by(chain_state2, utxo_state2)); assert!(!height_lock.is_satisfied_by(chain_state2, utxo_state2));
// Test case 3: Overflow handling - tests that is_satisfied_by handles overflow gracefully // Test case 3: Overflow handling - tests that is_satisfied_by handles overflow gracefully
let max_height_lock = Height::MAX; let max_height_lock = HeightInterval::MAX;
let chain_state3 = MtpAndHeight::new(BlockHeight::from_u32(1000), timestamps); let chain_state3 = MtpAndHeight::new(BlockHeight::from_u32(1000), timestamps);
let utxo_state3 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps); let utxo_state3 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
assert!(!max_height_lock.is_satisfied_by(chain_state3, utxo_state3)); assert!(!max_height_lock.is_satisfied_by(chain_state3, utxo_state3));

View File

@ -38,7 +38,7 @@ struct Structs {
e: BlockInterval, e: BlockInterval,
f: FeeRate, f: FeeRate,
g: absolute::Height, g: absolute::Height,
h: absolute::Time, h: absolute::Mtp,
i: relative::Height, i: relative::Height,
j: relative::Time, j: relative::Time,
k: Weight, k: Weight,
@ -55,7 +55,7 @@ impl Structs {
e: BlockInterval::MAX, e: BlockInterval::MAX,
f: FeeRate::MAX, f: FeeRate::MAX,
g: absolute::Height::MAX, g: absolute::Height::MAX,
h: absolute::Time::MAX, h: absolute::Mtp::MAX,
i: relative::Height::MAX, i: relative::Height::MAX,
j: relative::Time::MAX, j: relative::Time::MAX,
k: Weight::MAX, k: Weight::MAX,
@ -85,7 +85,7 @@ struct CommonTraits {
e: BlockInterval, e: BlockInterval,
f: FeeRate, f: FeeRate,
g: absolute::Height, g: absolute::Height,
h: absolute::Time, h: absolute::Mtp,
i: relative::Height, i: relative::Height,
j: relative::Time, j: relative::Time,
k: Weight, k: Weight,
@ -128,7 +128,7 @@ struct Errors {
t: amount::PossiblyConfusingDenominationError, t: amount::PossiblyConfusingDenominationError,
u: amount::TooPreciseError, u: amount::TooPreciseError,
v: amount::UnknownDenominationError, v: amount::UnknownDenominationError,
w: block::TooBigForRelativeBlockHeightError, w: block::TooBigForRelativeBlockHeightIntervalError,
x: locktime::absolute::ConversionError, x: locktime::absolute::ConversionError,
y: locktime::absolute::Height, y: locktime::absolute::Height,
z: locktime::absolute::ParseHeightError, z: locktime::absolute::ParseHeightError,
@ -163,7 +163,9 @@ fn api_can_use_all_types_from_module_amount() {
#[test] #[test]
fn api_can_use_all_types_from_module_block() { fn api_can_use_all_types_from_module_block() {
use bitcoin_units::block::{BlockHeight, BlockInterval, TooBigForRelativeBlockHeightError}; use bitcoin_units::block::{
BlockHeight, BlockInterval, TooBigForRelativeBlockHeightIntervalError,
};
} }
#[test] #[test]
@ -174,7 +176,7 @@ fn api_can_use_all_types_from_module_fee_rate() {
#[test] #[test]
fn api_can_use_all_types_from_module_locktime_absolute() { fn api_can_use_all_types_from_module_locktime_absolute() {
use bitcoin_units::locktime::absolute::{ use bitcoin_units::locktime::absolute::{
ConversionError, Height, ParseHeightError, ParseTimeError, Time, ConversionError, Height, Mtp, ParseHeightError, ParseTimeError,
}; };
} }
@ -291,7 +293,7 @@ impl<'a> Arbitrary<'a> for Structs {
e: BlockInterval::arbitrary(u)?, e: BlockInterval::arbitrary(u)?,
f: FeeRate::arbitrary(u)?, f: FeeRate::arbitrary(u)?,
g: absolute::Height::arbitrary(u)?, g: absolute::Height::arbitrary(u)?,
h: absolute::Time::arbitrary(u)?, h: absolute::Mtp::arbitrary(u)?,
i: relative::Height::arbitrary(u)?, i: relative::Height::arbitrary(u)?,
j: relative::Time::arbitrary(u)?, j: relative::Time::arbitrary(u)?,
k: Weight::arbitrary(u)?, k: Weight::arbitrary(u)?,

View File

@ -47,7 +47,7 @@ struct Serde {
a: BlockHeight, a: BlockHeight,
b: BlockInterval, b: BlockInterval,
c: absolute::Height, c: absolute::Height,
d: absolute::Time, d: absolute::Mtp,
e: relative::Height, e: relative::Height,
f: relative::Time, f: relative::Time,
g: Weight, g: Weight,
@ -80,7 +80,7 @@ impl Serde {
a: BlockHeight::MAX, a: BlockHeight::MAX,
b: BlockInterval::MAX, b: BlockInterval::MAX,
c: absolute::Height::MAX, c: absolute::Height::MAX,
d: absolute::Time::MAX, d: absolute::Mtp::MAX,
e: relative::Height::MAX, e: relative::Height::MAX,
f: relative::Time::MAX, f: relative::Time::MAX,
g: Weight::MAX, g: Weight::MAX,