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: ACK826acb8273
Tree-SHA512: 6e0491e17927625cde85c2cf92ff152a10613e632474122a626ee31b662d21c09fcb9fa3014c44708c97536535a33845cbbcd81e73dcdf98e9ee9fd6143c698f
This commit is contained in:
commit
1b04f5df65
|
@ -30,7 +30,7 @@ use crate::transaction::{Transaction, TransactionExt as _, Wtxid};
|
|||
#[doc(inline)]
|
||||
pub use primitives::block::{Block, Checked, Unchecked, Validation, Version, BlockHash, Header, WitnessCommitment};
|
||||
#[doc(inline)]
|
||||
pub use units::block::{BlockHeight, BlockInterval, TooBigForRelativeBlockHeightError};
|
||||
pub use units::block::{BlockHeight, BlockInterval, TooBigForRelativeBlockHeightIntervalError};
|
||||
|
||||
impl_hashencode!(BlockHash);
|
||||
|
||||
|
|
|
@ -41,7 +41,11 @@ pub mod locktime {
|
|||
|
||||
/// Re-export everything from the `primitives::locktime::absolute` module.
|
||||
#[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 {
|
||||
#[inline]
|
||||
|
@ -67,9 +71,17 @@ pub mod locktime {
|
|||
|
||||
/// Re-export everything from the `primitives::locktime::relative` module.
|
||||
pub use primitives::locktime::relative::{
|
||||
DisabledLockTimeError, Height, IncompatibleHeightError, IncompatibleTimeError,
|
||||
LockTime, Time, TimeOverflowError,
|
||||
DisabledLockTimeError, HeightInterval, IncompatibleHeightError, IncompatibleTimeError,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ use primitives::Sequence;
|
|||
use super::Weight;
|
||||
use crate::consensus::{self, encode, Decodable, Encodable};
|
||||
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::script::{Script, ScriptBuf, ScriptExt as _, ScriptExtPriv as _};
|
||||
#[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
|
||||
/// considered to be satisfied i.e., there are no timelock constraints restricting this
|
||||
/// 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]).
|
||||
///
|
||||
|
@ -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_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() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,11 @@ use crate::{absolute, Transaction};
|
|||
|
||||
#[rustfmt::skip] // Keep public re-exports separate.
|
||||
#[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
|
||||
/// since epoch).
|
||||
|
@ -79,7 +83,7 @@ pub enum LockTime {
|
|||
/// assert!(n.is_block_time());
|
||||
/// assert_eq!(n.to_consensus_u32(), seconds);
|
||||
/// ```
|
||||
Seconds(Time),
|
||||
Seconds(Mtp),
|
||||
}
|
||||
|
||||
impl LockTime {
|
||||
|
@ -141,9 +145,9 @@ impl LockTime {
|
|||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn from_consensus(n: u32) -> Self {
|
||||
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 {
|
||||
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]
|
||||
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))
|
||||
}
|
||||
|
||||
/// 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
|
||||
///
|
||||
/// If the locktime is set to a timestamp `T`,
|
||||
/// the transaction can be included in a block only if the median time past (MTP) of the
|
||||
/// last 11 blocks is greater than `T`.
|
||||
/// If the locktime is set to an MTP `T`, the transaction can be included in a block only if
|
||||
/// the MTP of last recent 11 blocks is greater than `T`.
|
||||
///
|
||||
/// 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)
|
||||
|
@ -191,8 +201,8 @@ impl LockTime {
|
|||
/// assert!(absolute::LockTime::from_time(741521).is_err());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_time(n: u32) -> Result<Self, ConversionError> {
|
||||
let time = Time::from_consensus(n)?;
|
||||
pub fn from_mtp(n: u32) -> Result<Self, ConversionError> {
|
||||
let time = Mtp::from_u32(n)?;
|
||||
Ok(LockTime::Seconds(time))
|
||||
}
|
||||
|
||||
|
@ -229,7 +239,7 @@ impl LockTime {
|
|||
/// # use bitcoin_primitives::absolute;
|
||||
/// // Can be implemented if block chain data is available.
|
||||
/// 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`.
|
||||
/// if n.is_satisfied_by(get_height(), get_time()) {
|
||||
|
@ -237,7 +247,7 @@ impl LockTime {
|
|||
/// }
|
||||
/// ````
|
||||
#[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;
|
||||
|
||||
match self {
|
||||
|
@ -310,8 +320,8 @@ impl LockTime {
|
|||
#[inline]
|
||||
pub fn to_consensus_u32(self) -> u32 {
|
||||
match self {
|
||||
LockTime::Blocks(ref h) => h.to_consensus_u32(),
|
||||
LockTime::Seconds(ref t) => t.to_consensus_u32(),
|
||||
LockTime::Blocks(ref h) => h.to_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) }
|
||||
}
|
||||
|
||||
impl From<Time> for LockTime {
|
||||
impl From<Mtp> for LockTime {
|
||||
#[inline]
|
||||
fn from(t: Time) -> Self { LockTime::Seconds(t) }
|
||||
fn from(t: Mtp) -> Self { LockTime::Seconds(t) }
|
||||
}
|
||||
|
||||
impl fmt::Debug for LockTime {
|
||||
|
@ -412,7 +422,7 @@ mod tests {
|
|||
#[test]
|
||||
fn display_and_alternate() {
|
||||
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), "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_001).is_err()); // Above the threshold.
|
||||
|
||||
assert!(LockTime::from_time(499_999_999).is_err()); // Below the threshold.
|
||||
assert!(LockTime::from_time(500_000_000).is_ok()); // The threshold.
|
||||
assert!(LockTime::from_time(500_000_001).is_ok()); // Above the threshold.
|
||||
assert!(LockTime::from_mtp(499_999_999).is_err()); // Below the threshold.
|
||||
assert!(LockTime::from_mtp(500_000_000).is_ok()); // The threshold.
|
||||
assert!(LockTime::from_mtp(500_000_001).is_ok()); // Above the threshold.
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -495,14 +505,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn satisfied_by_height() {
|
||||
let height_below = Height::from_consensus(700_000).unwrap();
|
||||
let height = Height::from_consensus(750_000).unwrap();
|
||||
let height_above = Height::from_consensus(800_000).unwrap();
|
||||
let height_below = Height::from_u32(700_000).unwrap();
|
||||
let height = Height::from_u32(750_000).unwrap();
|
||||
let height_above = Height::from_u32(800_000).unwrap();
|
||||
|
||||
let lock_by_height = LockTime::from(height);
|
||||
|
||||
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, time));
|
||||
|
@ -511,13 +521,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn satisfied_by_time() {
|
||||
let time_before = Time::from_consensus(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_after = Time::from_consensus(1_653_282_000).unwrap(); // "May 23rd 2022, 5am UTC.
|
||||
let time_before = Mtp::from_u32(1_653_109_200).unwrap(); // "May 21th 2022, 5am UTC.
|
||||
let time = Mtp::from_u32(1_653_195_600).unwrap(); // "May 22nd 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 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));
|
||||
|
|
|
@ -13,9 +13,17 @@ use crate::{relative, TxIn};
|
|||
|
||||
#[rustfmt::skip] // Keep public re-exports separate.
|
||||
#[doc(inline)]
|
||||
pub use units::locktime::relative::{Height, Time, TimeOverflowError};
|
||||
pub use units::locktime::relative::{HeightInterval, MtpInterval, TimeOverflowError};
|
||||
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).
|
||||
///
|
||||
/// 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 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
|
||||
/// 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))]
|
||||
pub enum LockTime {
|
||||
/// A block height lock time value.
|
||||
Blocks(Height),
|
||||
Blocks(HeightInterval),
|
||||
/// A 512 second time interval value.
|
||||
Time(Time),
|
||||
Time(MtpInterval),
|
||||
}
|
||||
|
||||
impl LockTime {
|
||||
/// A relative locktime of 0 is always valid, and is assumed valid for inputs that
|
||||
/// 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.
|
||||
pub const SIZE: usize = 4; // Serialized length of a u32.
|
||||
|
@ -124,8 +132,9 @@ impl LockTime {
|
|||
#[inline]
|
||||
pub fn to_consensus_u32(self) -> u32 {
|
||||
match self {
|
||||
LockTime::Blocks(ref h) => h.to_consensus_u32(),
|
||||
LockTime::Time(ref t) => t.to_consensus_u32(),
|
||||
LockTime::Blocks(ref h) => u32::from(h.to_height()),
|
||||
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.
|
||||
#[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.
|
||||
///
|
||||
|
@ -164,7 +173,7 @@ impl LockTime {
|
|||
/// [`Self::from_seconds_floor`] or [`Self::from_seconds_ceil`].
|
||||
#[inline]
|
||||
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
|
||||
|
@ -175,7 +184,7 @@ impl LockTime {
|
|||
/// Will return an error if the input cannot be encoded in 16 bits.
|
||||
#[inline]
|
||||
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)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
|
@ -189,7 +198,7 @@ impl LockTime {
|
|||
/// Will return an error if the input cannot be encoded in 16 bits.
|
||||
#[inline]
|
||||
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)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
|
@ -216,7 +225,7 @@ impl LockTime {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bitcoin_primitives::locktime::relative::Height;
|
||||
/// # use bitcoin_primitives::locktime::relative::HeightInterval;
|
||||
/// # use bitcoin_primitives::relative::Time;
|
||||
/// # use units::mtp_height::MtpAndHeight;
|
||||
/// # use bitcoin_primitives::BlockHeight;
|
||||
|
@ -285,8 +294,8 @@ impl LockTime {
|
|||
use LockTime as L;
|
||||
|
||||
match (self, other) {
|
||||
(L::Blocks(this), L::Blocks(other)) => this.value() <= other.value(),
|
||||
(L::Time(this), L::Time(other)) => this.value() <= other.value(),
|
||||
(L::Blocks(this), L::Blocks(other)) => this <= other,
|
||||
(L::Time(this), L::Time(other)) => this <= other,
|
||||
_ => 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
|
||||
///
|
||||
|
@ -334,14 +343,17 @@ impl LockTime {
|
|||
///
|
||||
/// let required_height: u16 = 100;
|
||||
/// 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]
|
||||
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;
|
||||
|
||||
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 }),
|
||||
}
|
||||
}
|
||||
|
@ -363,24 +375,24 @@ impl LockTime {
|
|||
/// assert!(lock.is_satisfied_by_time(relative::Time::from_512_second_intervals(intervals + 10)).expect("a time"));
|
||||
/// ```
|
||||
#[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;
|
||||
|
||||
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 }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Height> for LockTime {
|
||||
impl From<HeightInterval> for LockTime {
|
||||
#[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]
|
||||
fn from(t: Time) -> Self { LockTime::Time(t) }
|
||||
fn from(t: MtpInterval) -> Self { LockTime::Time(t) }
|
||||
}
|
||||
|
||||
impl fmt::Display for LockTime {
|
||||
|
@ -440,17 +452,17 @@ impl std::error::Error for DisabledLockTimeError {}
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct IncompatibleHeightError {
|
||||
/// 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.
|
||||
time: Time,
|
||||
time: MtpInterval,
|
||||
}
|
||||
|
||||
impl IncompatibleHeightError {
|
||||
/// 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.
|
||||
pub fn expected(&self) -> Time { self.time }
|
||||
pub fn expected(&self) -> MtpInterval { self.time }
|
||||
}
|
||||
|
||||
impl fmt::Display for IncompatibleHeightError {
|
||||
|
@ -471,17 +483,17 @@ impl std::error::Error for IncompatibleHeightError {}
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct IncompatibleTimeError {
|
||||
/// 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.
|
||||
height: Height,
|
||||
height: HeightInterval,
|
||||
}
|
||||
|
||||
impl IncompatibleTimeError {
|
||||
/// 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.
|
||||
pub fn expected(&self) -> Height { self.height }
|
||||
pub fn expected(&self) -> HeightInterval { self.height }
|
||||
}
|
||||
|
||||
impl fmt::Display for IncompatibleTimeError {
|
||||
|
@ -536,10 +548,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn parses_correctly_to_height_or_time() {
|
||||
let height1 = Height::from(10);
|
||||
let height2 = Height::from(11);
|
||||
let time1 = Time::from_512_second_intervals(70);
|
||||
let time2 = Time::from_512_second_intervals(71);
|
||||
let height1 = HeightInterval::from(10);
|
||||
let height2 = HeightInterval::from(11);
|
||||
let time1 = MtpInterval::from_512_second_intervals(70);
|
||||
let time2 = MtpInterval::from_512_second_intervals(71);
|
||||
|
||||
let lock_by_height1 = LockTime::from(height1);
|
||||
let lock_by_height2 = LockTime::from(height2);
|
||||
|
@ -561,28 +573,34 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn height_correctly_implies() {
|
||||
let height = Height::from(10);
|
||||
let height = HeightInterval::from(10);
|
||||
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(Height::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(9))));
|
||||
assert!(lock_by_height.is_implied_by(LockTime::from(HeightInterval::from(10))));
|
||||
assert!(lock_by_height.is_implied_by(LockTime::from(HeightInterval::from(11))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
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);
|
||||
|
||||
assert!(!lock_by_time.is_implied_by(LockTime::from(Time::from_512_second_intervals(69))));
|
||||
assert!(lock_by_time.is_implied_by(LockTime::from(Time::from_512_second_intervals(70))));
|
||||
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(69)))
|
||||
);
|
||||
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]
|
||||
fn sequence_correctly_implies() {
|
||||
let height = Height::from(10);
|
||||
let time = Time::from_512_second_intervals(70);
|
||||
let height = HeightInterval::from(10);
|
||||
let time = MtpInterval::from_512_second_intervals(70);
|
||||
|
||||
let lock_by_height = LockTime::from(height);
|
||||
let lock_by_time = LockTime::from(time);
|
||||
|
@ -603,8 +621,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn incorrect_units_do_not_imply() {
|
||||
let time = Time::from_512_second_intervals(70);
|
||||
let height = Height::from(10);
|
||||
let time = MtpInterval::from_512_second_intervals(70);
|
||||
let height = HeightInterval::from(10);
|
||||
|
||||
let lock_by_time = LockTime::from(time);
|
||||
assert!(!lock_by_time.is_implied_by(LockTime::from(height)));
|
||||
|
@ -643,8 +661,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn incompatible_height_error() {
|
||||
let height = Height::from(10);
|
||||
let time = Time::from_512_second_intervals(70);
|
||||
let height = HeightInterval::from(10);
|
||||
let time = MtpInterval::from_512_second_intervals(70);
|
||||
let lock_by_time = LockTime::from(time);
|
||||
let err = lock_by_time.is_satisfied_by_height(height).unwrap_err();
|
||||
|
||||
|
@ -655,8 +673,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn incompatible_time_error() {
|
||||
let height = Height::from(10);
|
||||
let time = Time::from_512_second_intervals(70);
|
||||
let height = HeightInterval::from(10);
|
||||
let time = MtpInterval::from_512_second_intervals(70);
|
||||
let lock_by_height = LockTime::from(height);
|
||||
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 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));
|
||||
|
||||
let lock2 = LockTime::Blocks(Height::from(21));
|
||||
let lock2 = LockTime::Blocks(HeightInterval::from(21));
|
||||
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));
|
||||
|
||||
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!(LockTime::ZERO.is_satisfied_by(chain_tip, utxo_mined_at));
|
||||
|
@ -699,10 +717,10 @@ mod tests {
|
|||
let lock6 = LockTime::from_seconds_floor(5000).unwrap();
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
let max_chain_tip =
|
||||
|
|
|
@ -70,7 +70,7 @@ impl Sequence {
|
|||
/// BIP-68 relative lock time disable flag mask.
|
||||
const LOCK_TIME_DISABLE_FLAG_MASK: u32 = 0x8000_0000;
|
||||
/// 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`]).
|
||||
#[inline]
|
||||
|
@ -187,7 +187,7 @@ impl Sequence {
|
|||
/// Constructs a new [`relative::LockTime`] from this [`Sequence`] number.
|
||||
#[inline]
|
||||
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() {
|
||||
return None;
|
||||
|
@ -196,9 +196,9 @@ impl Sequence {
|
|||
let lock_value = self.low_u16();
|
||||
|
||||
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 {
|
||||
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),
|
||||
2 => Ok(Sequence::MIN_NO_RBF),
|
||||
3 => Ok(Sequence::ENABLE_LOCKTIME_AND_RBF),
|
||||
4 => Ok(Sequence::from_consensus(relative::Height::MIN.to_consensus_u32())),
|
||||
5 => Ok(Sequence::from_consensus(relative::Height::MAX.to_consensus_u32())),
|
||||
6 => Ok(Sequence::from_consensus(relative::Time::MIN.to_consensus_u32())),
|
||||
7 => Ok(Sequence::from_consensus(relative::Time::MAX.to_consensus_u32())),
|
||||
4 => Ok(Sequence::from_consensus(u32::from(relative::HeightInterval::MIN.to_height()))),
|
||||
5 => Ok(Sequence::from_consensus(u32::from(relative::HeightInterval::MAX.to_height()))),
|
||||
6 => Ok(Sequence::from_consensus(
|
||||
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()?)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ impl From<absolute::Height> for BlockHeight {
|
|||
/// 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
|
||||
/// 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 {
|
||||
|
@ -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
|
||||
/// not interchangeable.
|
||||
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() }
|
||||
}
|
||||
|
||||
impl From<relative::Height> for BlockInterval {
|
||||
/// Converts a [`locktime::relative::Height`] to a [`BlockInterval`].
|
||||
impl From<relative::HeightInterval> for BlockInterval {
|
||||
/// Converts a [`locktime::relative::HeightInterval`] to a [`BlockInterval`].
|
||||
///
|
||||
/// 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.
|
||||
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 {
|
||||
type Error = TooBigForRelativeBlockHeightError;
|
||||
impl TryFrom<BlockInterval> for relative::HeightInterval {
|
||||
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
|
||||
/// [`BlockInterval`] is a thin wrapper around a `u32`, the two types are not interchangeable.
|
||||
fn try_from(h: BlockInterval) -> Result<Self, Self::Error> {
|
||||
let h = h.to_u32();
|
||||
|
||||
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.
|
||||
#[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 {
|
||||
write!(
|
||||
f,
|
||||
"block interval is too big to be used as a relative lock time: {} (max: {})",
|
||||
self.0,
|
||||
relative::Height::MAX
|
||||
relative::HeightInterval::MAX
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for TooBigForRelativeBlockHeightError {}
|
||||
impl std::error::Error for TooBigForRelativeBlockHeightIntervalError {}
|
||||
|
||||
crate::internal_macros::impl_op_for_references! {
|
||||
// height - height = interval
|
||||
|
@ -279,14 +280,14 @@ mod tests {
|
|||
let interval: u32 = BlockInterval(100).into();
|
||||
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);
|
||||
|
||||
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());
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// 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::fmt;
|
||||
|
@ -45,15 +45,22 @@ impl Height {
|
|||
/// # Errors
|
||||
///
|
||||
/// If the input string is not a valid hex representation of a block height.
|
||||
pub fn from_hex(s: &str) -> Result<Self, ParseHeightError> {
|
||||
parse_hex(s, Self::from_consensus)
|
||||
}
|
||||
pub fn from_hex(s: &str) -> Result<Self, ParseHeightError> { parse_hex(s, Self::from_u32) }
|
||||
|
||||
/// 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
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
|
@ -61,11 +68,11 @@ impl Height {
|
|||
/// use bitcoin_units::locktime::absolute::Height;
|
||||
///
|
||||
/// 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);
|
||||
/// ```
|
||||
#[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) {
|
||||
Ok(Self(n))
|
||||
} else {
|
||||
|
@ -73,16 +80,16 @@ impl Height {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts this [`Height`] to its inner `u32` value.
|
||||
/// Converts this [`Height`] to a raw `u32` value.
|
||||
#[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 {
|
||||
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.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
@ -111,7 +118,7 @@ impl<'de> serde::Deserialize<'de> for Height {
|
|||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
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
|
||||
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
|
||||
/// `to_consensus_u32()`. Said another way, `Time(x)` means 'x seconds since epoch' _not_ '(x -
|
||||
/// threshold) seconds since epoch'.
|
||||
/// Time-based locktimes are not measured against the timestamps in individual block
|
||||
/// headers, since these are not monotone and may be subject to miner manipulation.
|
||||
/// 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)]
|
||||
pub struct Time(u32);
|
||||
pub struct Mtp(u32);
|
||||
|
||||
impl Time {
|
||||
/// The minimum absolute block time (Tue Nov 05 1985 00:53:20 GMT+0000).
|
||||
pub const MIN: Self = Time(LOCK_TIME_THRESHOLD);
|
||||
impl Mtp {
|
||||
/// The minimum MTP allowable in a locktime (Tue Nov 05 1985 00:53:20 GMT+0000).
|
||||
pub const MIN: Self = Mtp(LOCK_TIME_THRESHOLD);
|
||||
|
||||
/// The maximum absolute block time (Sun Feb 07 2106 06:28:15 GMT+0000).
|
||||
pub const MAX: Self = Time(u32::MAX);
|
||||
/// The maximum MTP allowable in a locktime (Sun Feb 07 2106 06:28:15 GMT+0000).
|
||||
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`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```rust
|
||||
/// use bitcoin_units::locktime::absolute::Time;
|
||||
/// use bitcoin_units::locktime::absolute::Mtp;
|
||||
///
|
||||
/// 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);
|
||||
/// ```
|
||||
#[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) {
|
||||
Ok(Self(n))
|
||||
} else {
|
||||
|
@ -173,35 +198,35 @@ impl Time {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts this [`Time`] to its inner `u32` value.
|
||||
/// Converts this [`Mtp`] to a raw `u32` value.
|
||||
#[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) }
|
||||
}
|
||||
|
||||
crate::impl_parse_str!(Time, ParseTimeError, parser(Time::from_consensus));
|
||||
crate::impl_parse_str!(Mtp, ParseTimeError, parser(Mtp::from_u32));
|
||||
|
||||
#[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>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
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")]
|
||||
impl serde::Serialize for Time {
|
||||
impl serde::Serialize for Mtp {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
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 })
|
||||
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!(
|
||||
f,
|
||||
"{} ({} is above limit {})",
|
||||
input.display_cannot_parse("absolute Height/Time"),
|
||||
input.display_cannot_parse("absolute Height/Mtp"),
|
||||
subject,
|
||||
upper_bound
|
||||
)
|
||||
|
@ -354,17 +379,17 @@ impl ParseError {
|
|||
E::ParseInt(ParseIntError { input, bits: _, is_signed: _, source })
|
||||
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!(
|
||||
f,
|
||||
"{} ({} is below limit {})",
|
||||
input.display_cannot_parse("absolute Height/Time"),
|
||||
input.display_cannot_parse("absolute Height/Mtp"),
|
||||
subject,
|
||||
lower_bound
|
||||
)
|
||||
}
|
||||
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) => {
|
||||
write!(f, "{} {} is below limit {}", subject, value, lower_bound)
|
||||
|
@ -407,27 +432,27 @@ impl<'a> Arbitrary<'a> for Height {
|
|||
0 => Ok(Height::MIN),
|
||||
1 => Ok(Height::MAX),
|
||||
_ => {
|
||||
let min = Height::MIN.to_consensus_u32();
|
||||
let max = Height::MAX.to_consensus_u32();
|
||||
let min = Height::MIN.to_u32();
|
||||
let max = Height::MAX.to_u32();
|
||||
let h = u.int_in_range(min..=max)?;
|
||||
Ok(Height::from_consensus(h).unwrap())
|
||||
Ok(Height::from_u32(h).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<'a> Arbitrary<'a> for Time {
|
||||
impl<'a> Arbitrary<'a> for Mtp {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let choice = u.int_in_range(0..=2)?;
|
||||
match choice {
|
||||
0 => Ok(Time::MIN),
|
||||
1 => Ok(Time::MAX),
|
||||
0 => Ok(Mtp::MIN),
|
||||
1 => Ok(Mtp::MAX),
|
||||
_ => {
|
||||
let min = Time::MIN.to_consensus_u32();
|
||||
let max = Time::MAX.to_consensus_u32();
|
||||
let min = Mtp::MIN.to_u32();
|
||||
let max = Mtp::MAX.to_u32();
|
||||
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]
|
||||
fn time_from_str_hex_happy_path() {
|
||||
let actual = Time::from_hex("0x6289C350").unwrap();
|
||||
let expected = Time::from_consensus(0x6289_C350).unwrap();
|
||||
let actual = Mtp::from_hex("0x6289C350").unwrap();
|
||||
let expected = Mtp::from_u32(0x6289_C350).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time_from_str_hex_no_prefix_happy_path() {
|
||||
let time = Time::from_hex("6289C350").unwrap();
|
||||
assert_eq!(time, Time(0x6289_C350));
|
||||
let time = Mtp::from_hex("6289C350").unwrap();
|
||||
assert_eq!(time, Mtp(0x6289_C350));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time_from_str_hex_invalid_hex_should_err() {
|
||||
let hex = "0xzb93";
|
||||
let result = Time::from_hex(hex);
|
||||
let result = Mtp::from_hex(hex);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
|
@ -500,8 +525,8 @@ mod tests {
|
|||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
pub fn encode_decode_time() {
|
||||
serde_round_trip!(Time::MIN);
|
||||
serde_round_trip!(Time::MAX);
|
||||
serde_round_trip!(Mtp::MIN);
|
||||
serde_round_trip!(Mtp::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// 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;
|
||||
|
||||
|
@ -11,33 +11,47 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
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.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[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.
|
||||
pub const ZERO: Self = Height(0);
|
||||
pub const ZERO: Self = Self(0);
|
||||
|
||||
/// The minimum relative block height (0), can be included in any block.
|
||||
pub const MIN: Self = Self::ZERO;
|
||||
|
||||
/// 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]
|
||||
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.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[deprecated(since = "TBD", note = "use `to_height` instead")]
|
||||
#[doc(hidden)]
|
||||
pub const fn value(self) -> u16 { self.0 }
|
||||
|
||||
/// Returns the `u32` value used to encode this locktime in an nSequence field or
|
||||
/// 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 {
|
||||
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 {
|
||||
// let chain_tip_height = BlockHeight::from(chain_tip);
|
||||
// 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,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for Height {
|
||||
impl From<u16> for HeightInterval {
|
||||
#[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) }
|
||||
}
|
||||
|
||||
#[deprecated(since = "TBD", note = "use `Mtp` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type Time = MtpInterval;
|
||||
|
||||
/// A relative lock time lock-by-blocktime value.
|
||||
///
|
||||
/// 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)]
|
||||
#[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.
|
||||
pub const ZERO: Self = Time(0);
|
||||
pub const ZERO: Self = MtpInterval(0);
|
||||
|
||||
/// 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).
|
||||
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.
|
||||
#[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.
|
||||
///
|
||||
/// # Errors
|
||||
|
@ -108,13 +131,13 @@ impl Time {
|
|||
pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
|
||||
let interval = seconds / 512;
|
||||
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 {
|
||||
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.
|
||||
///
|
||||
/// # Errors
|
||||
|
@ -125,7 +148,7 @@ impl Time {
|
|||
pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
|
||||
if seconds <= u16::MAX as u32 * 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 {
|
||||
Err(TimeOverflowError { seconds })
|
||||
}
|
||||
|
@ -134,11 +157,16 @@ impl Time {
|
|||
/// Returns the inner `u16` value.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[deprecated(since = "TBD", note = "use `to_512_second_intervals` instead")]
|
||||
#[doc(hidden)]
|
||||
pub const fn value(self) -> u16 { self.0 }
|
||||
|
||||
/// Returns the `u32` value used to encode this locktime in an nSequence field or
|
||||
/// 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 {
|
||||
(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 relative‐time lock has expired by the tip’s MTP
|
||||
/// - `false` if the lock has not yet expired by the tip’s MTP
|
||||
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(required_seconds) => chain_tip.to_mtp().to_u32() >= required_seconds,
|
||||
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) }
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
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.
|
||||
pub(crate) seconds: u32,
|
||||
}
|
||||
|
@ -205,27 +233,27 @@ impl fmt::Display for TimeOverflowError {
|
|||
impl std::error::Error for TimeOverflowError {}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<'a> Arbitrary<'a> for Height {
|
||||
impl<'a> Arbitrary<'a> for HeightInterval {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let choice = u.int_in_range(0..=2)?;
|
||||
|
||||
match choice {
|
||||
0 => Ok(Height::MIN),
|
||||
1 => Ok(Height::MAX),
|
||||
_ => Ok(Height::from_height(u16::arbitrary(u)?)),
|
||||
0 => Ok(HeightInterval::MIN),
|
||||
1 => Ok(HeightInterval::MAX),
|
||||
_ => Ok(HeightInterval::from_height(u16::arbitrary(u)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<'a> Arbitrary<'a> for Time {
|
||||
impl<'a> Arbitrary<'a> for MtpInterval {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let choice = u.int_in_range(0..=2)?;
|
||||
|
||||
match choice {
|
||||
0 => Ok(Time::MIN),
|
||||
1 => Ok(Time::MAX),
|
||||
_ => Ok(Time::from_512_second_intervals(u16::arbitrary(u)?)),
|
||||
0 => Ok(MtpInterval::MIN),
|
||||
1 => Ok(MtpInterval::MAX),
|
||||
_ => 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;
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated_in_future)]
|
||||
fn sanity_check() {
|
||||
assert_eq!(Height::MAX.to_consensus_u32(), u32::from(u16::MAX));
|
||||
assert_eq!(Time::from_512_second_intervals(100).value(), 100u16);
|
||||
assert_eq!(Time::from_512_second_intervals(100).to_consensus_u32(), 4_194_404u32); // 0x400064
|
||||
assert_eq!(HeightInterval::MAX.to_consensus_u32(), u32::from(u16::MAX));
|
||||
assert_eq!(MtpInterval::from_512_second_intervals(100).value(), 100u16);
|
||||
assert_eq!(MtpInterval::from_512_second_intervals(100).to_consensus_u32(), 4_194_404u32); // 0x400064
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_seconds_ceil_success() {
|
||||
let actual = Time::from_seconds_ceil(100).unwrap();
|
||||
let expected = Time(1_u16);
|
||||
let actual = MtpInterval::from_seconds_ceil(100).unwrap();
|
||||
let expected = MtpInterval(1_u16);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_seconds_ceil_with_maximum_encodable_seconds_success() {
|
||||
let actual = Time::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS).unwrap();
|
||||
let expected = Time(u16::MAX);
|
||||
let actual = MtpInterval::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS).unwrap();
|
||||
let expected = MtpInterval(u16::MAX);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_seconds_floor_success() {
|
||||
let actual = Time::from_seconds_floor(100).unwrap();
|
||||
let expected = Time(0_u16);
|
||||
let actual = MtpInterval::from_seconds_floor(100).unwrap();
|
||||
let expected = MtpInterval(0_u16);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_seconds_floor_with_exact_interval() {
|
||||
let actual = Time::from_seconds_floor(512).unwrap();
|
||||
let expected = Time(1_u16);
|
||||
let actual = MtpInterval::from_seconds_floor(512).unwrap();
|
||||
let expected = MtpInterval(1_u16);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_seconds_floor_with_maximum_encodable_seconds_success() {
|
||||
let actual = Time::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 511).unwrap();
|
||||
let expected = Time(u16::MAX);
|
||||
let actual = MtpInterval::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 511).unwrap();
|
||||
let expected = MtpInterval(u16::MAX);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
pub fn encode_decode_height() {
|
||||
serde_round_trip!(Height::ZERO);
|
||||
serde_round_trip!(Height::MIN);
|
||||
serde_round_trip!(Height::MAX);
|
||||
serde_round_trip!(HeightInterval::ZERO);
|
||||
serde_round_trip!(HeightInterval::MIN);
|
||||
serde_round_trip!(HeightInterval::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
pub fn encode_decode_time() {
|
||||
serde_round_trip!(Time::ZERO);
|
||||
serde_round_trip!(Time::MIN);
|
||||
serde_round_trip!(Time::MAX);
|
||||
serde_round_trip!(MtpInterval::ZERO);
|
||||
serde_round_trip!(MtpInterval::MIN);
|
||||
serde_round_trip!(MtpInterval::MAX);
|
||||
}
|
||||
|
||||
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)
|
||||
// 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 utxo_state1 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
|
||||
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));
|
||||
|
||||
// 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 utxo_state3 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps3);
|
||||
assert!(larger_lock.is_satisfied_by(chain_state3, utxo_state3));
|
||||
|
||||
// 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 utxo_state4 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
|
||||
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 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)
|
||||
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));
|
||||
|
||||
// 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 utxo_state3 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
|
||||
assert!(!max_height_lock.is_satisfied_by(chain_state3, utxo_state3));
|
||||
|
|
|
@ -38,7 +38,7 @@ struct Structs {
|
|||
e: BlockInterval,
|
||||
f: FeeRate,
|
||||
g: absolute::Height,
|
||||
h: absolute::Time,
|
||||
h: absolute::Mtp,
|
||||
i: relative::Height,
|
||||
j: relative::Time,
|
||||
k: Weight,
|
||||
|
@ -55,7 +55,7 @@ impl Structs {
|
|||
e: BlockInterval::MAX,
|
||||
f: FeeRate::MAX,
|
||||
g: absolute::Height::MAX,
|
||||
h: absolute::Time::MAX,
|
||||
h: absolute::Mtp::MAX,
|
||||
i: relative::Height::MAX,
|
||||
j: relative::Time::MAX,
|
||||
k: Weight::MAX,
|
||||
|
@ -85,7 +85,7 @@ struct CommonTraits {
|
|||
e: BlockInterval,
|
||||
f: FeeRate,
|
||||
g: absolute::Height,
|
||||
h: absolute::Time,
|
||||
h: absolute::Mtp,
|
||||
i: relative::Height,
|
||||
j: relative::Time,
|
||||
k: Weight,
|
||||
|
@ -128,7 +128,7 @@ struct Errors {
|
|||
t: amount::PossiblyConfusingDenominationError,
|
||||
u: amount::TooPreciseError,
|
||||
v: amount::UnknownDenominationError,
|
||||
w: block::TooBigForRelativeBlockHeightError,
|
||||
w: block::TooBigForRelativeBlockHeightIntervalError,
|
||||
x: locktime::absolute::ConversionError,
|
||||
y: locktime::absolute::Height,
|
||||
z: locktime::absolute::ParseHeightError,
|
||||
|
@ -163,7 +163,9 @@ fn api_can_use_all_types_from_module_amount() {
|
|||
|
||||
#[test]
|
||||
fn api_can_use_all_types_from_module_block() {
|
||||
use bitcoin_units::block::{BlockHeight, BlockInterval, TooBigForRelativeBlockHeightError};
|
||||
use bitcoin_units::block::{
|
||||
BlockHeight, BlockInterval, TooBigForRelativeBlockHeightIntervalError,
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -174,7 +176,7 @@ fn api_can_use_all_types_from_module_fee_rate() {
|
|||
#[test]
|
||||
fn api_can_use_all_types_from_module_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)?,
|
||||
f: FeeRate::arbitrary(u)?,
|
||||
g: absolute::Height::arbitrary(u)?,
|
||||
h: absolute::Time::arbitrary(u)?,
|
||||
h: absolute::Mtp::arbitrary(u)?,
|
||||
i: relative::Height::arbitrary(u)?,
|
||||
j: relative::Time::arbitrary(u)?,
|
||||
k: Weight::arbitrary(u)?,
|
||||
|
|
|
@ -47,7 +47,7 @@ struct Serde {
|
|||
a: BlockHeight,
|
||||
b: BlockInterval,
|
||||
c: absolute::Height,
|
||||
d: absolute::Time,
|
||||
d: absolute::Mtp,
|
||||
e: relative::Height,
|
||||
f: relative::Time,
|
||||
g: Weight,
|
||||
|
@ -80,7 +80,7 @@ impl Serde {
|
|||
a: BlockHeight::MAX,
|
||||
b: BlockInterval::MAX,
|
||||
c: absolute::Height::MAX,
|
||||
d: absolute::Time::MAX,
|
||||
d: absolute::Mtp::MAX,
|
||||
e: relative::Height::MAX,
|
||||
f: relative::Time::MAX,
|
||||
g: Weight::MAX,
|
||||
|
|
Loading…
Reference in New Issue