Merge rust-bitcoin/rust-bitcoin#4458: locktimes: replace `MtpAndHeight` type with pair of `BlockMtp` and `BlockHeight`
47c77afaac
units: delete MtpAndHeight type (Andrew Poelstra)d82b8c0bcb
primitives: stop using MtpAndHeight (Andrew Poelstra)72d5fbad73
units: stop using MtpAndHeight in locktime::relative is_satisfied_by methods (Andrew Poelstra)d933c754f5
units: change type of MtpHeight::to_mtp to BlockMtp (Andrew Poelstra)dcbdb7ca8a
units: add checked arithmetic to Block{Height,Mtp}{Interval,} (Andrew Poelstra)4300271f0c
units: add constructor for absolute::Mtp from timestamps (Andrew Poelstra)4e4601b3d5
units: rename BlockInterval to BlockHeightInterval (Andrew Poelstra)cb882c5ce1
units: add global `BlockMtpInterval` type (Andrew Poelstra)4e3af5162f
units: add global `BlockMtp` type (Andrew Poelstra)a3228d4636
units: pull u32 conversions for BlockHeight/BlockInterval into macro (Andrew Poelstra) Pull request description: This is a more involved PR than I'd expected but hopefully the individual commits make sense and are well-motivated. Essentially, my goal was to replace `MtpAndHeight` as used by relative locktimes with a pair of `Mtp` and `Height`. However, relative locktimes, when given a MTP/Height for the UTXO creation and the chain tip, are roughly modeled as "take a diff of MTPs to get a `relative::MtpInterval`, a diff of heights to get a `relative::HeightInterval`, and compare to the locktimes". *However*, we have no standalone MTP type to "take a diff of", and also there are failure modes when creating the diffs (e.g. if the diff would exceed the range of `MtpInterval` or `HeightInterval`). So I backed up and decided to use the existing `BlockHeight`/`BlockInterval` as the type to "take a diff of". I needed to introduce a `BlockMtp`/`BlockMtpInterval` to work with MTPs. These types have full-u32 range, unlike the similarly-named types in `units::locktimes::absolute`. I then needed to add some conversion methods. Along the way, I cleaned up the APIs and documentation, added checked arithmetic, etc., as needed. See the individual commit messages for more detail. I believe the resulting API is much more consistent and discoverable, even though it has more surface than the old API. I considered splitting this into 2 PRs but I think the first half of the changes aren't well-motivated with out the second half. Let me know. ACKs for top commit: tcharding: ACK47c77afaac
Tree-SHA512: ebe19a5b1684db8c2d913274347c994026aaa0dcdd79349c237920a82fe55560777278efdbbc7f1b1424c9391d9bbd891ae844db885deea75288000437a8a287
This commit is contained in:
commit
4ca6cd6065
|
@ -30,7 +30,13 @@ 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, TooBigForRelativeBlockHeightIntervalError};
|
||||
pub use units::block::{
|
||||
BlockHeight, BlockHeightInterval, TooBigForRelativeBlockHeightIntervalError,
|
||||
};
|
||||
|
||||
#[deprecated(since = "TBD", note = "use `BlockHeightInterval` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type BlockInterval = BlockHeightInterval;
|
||||
|
||||
impl_hashencode!(BlockHash);
|
||||
|
||||
|
|
|
@ -135,12 +135,16 @@ pub use primitives::{
|
|||
#[doc(inline)]
|
||||
pub use units::{
|
||||
amount::{Amount, Denomination, SignedAmount},
|
||||
block::{BlockHeight, BlockInterval},
|
||||
block::{BlockHeight, BlockHeightInterval, BlockMtp},
|
||||
fee_rate::FeeRate,
|
||||
time::{self, BlockTime},
|
||||
weight::Weight,
|
||||
};
|
||||
|
||||
#[deprecated(since = "TBD", note = "use `BlockHeightInterval` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type BlockInterval = BlockHeightInterval;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::{
|
||||
address::{Address, AddressType, KnownHrp},
|
||||
|
@ -243,7 +247,7 @@ mod encode_impls {
|
|||
//! Encodable/Decodable implementations.
|
||||
// While we are deprecating, re-exporting, and generally moving things around just put these here.
|
||||
|
||||
use units::{BlockHeight, BlockInterval};
|
||||
use units::{BlockHeight, BlockHeightInterval};
|
||||
|
||||
use crate::consensus::{encode, Decodable, Encodable};
|
||||
use crate::io::{BufRead, Write};
|
||||
|
@ -275,5 +279,5 @@ mod encode_impls {
|
|||
}
|
||||
|
||||
impl_encodable_for_u32_wrapper!(BlockHeight);
|
||||
impl_encodable_for_u32_wrapper!(BlockInterval);
|
||||
impl_encodable_for_u32_wrapper!(BlockHeightInterval);
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
//! # }
|
||||
//! ```
|
||||
|
||||
use units::{BlockHeight, BlockInterval};
|
||||
use units::{BlockHeight, BlockHeightInterval};
|
||||
|
||||
use crate::network::Network;
|
||||
#[cfg(doc)]
|
||||
|
@ -92,9 +92,9 @@ pub struct Params {
|
|||
/// Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period,
|
||||
/// (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
|
||||
/// Examples: 1916 for 95%, 1512 for testchains.
|
||||
pub rule_change_activation_threshold: BlockInterval,
|
||||
pub rule_change_activation_threshold: BlockHeightInterval,
|
||||
/// Number of blocks with the same set of rules.
|
||||
pub miner_confirmation_window: BlockInterval,
|
||||
pub miner_confirmation_window: BlockHeightInterval,
|
||||
/// Proof of work limit value. It contains the lowest possible difficulty.
|
||||
#[deprecated(since = "0.32.0", note = "use `max_attainable_target` instead")]
|
||||
pub pow_limit: Target,
|
||||
|
@ -152,8 +152,8 @@ impl Params {
|
|||
bip34_height: BlockHeight::from_u32(227931), // 000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8
|
||||
bip65_height: BlockHeight::from_u32(388381), // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
|
||||
bip66_height: BlockHeight::from_u32(363725), // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
|
||||
rule_change_activation_threshold: BlockInterval::from_u32(1916), // 95%
|
||||
miner_confirmation_window: BlockInterval::from_u32(2016),
|
||||
rule_change_activation_threshold: BlockHeightInterval::from_u32(1916), // 95%
|
||||
miner_confirmation_window: BlockHeightInterval::from_u32(2016),
|
||||
pow_limit: Target::MAX_ATTAINABLE_MAINNET,
|
||||
max_attainable_target: Target::MAX_ATTAINABLE_MAINNET,
|
||||
pow_target_spacing: 10 * 60, // 10 minutes.
|
||||
|
@ -170,8 +170,8 @@ impl Params {
|
|||
bip34_height: BlockHeight::from_u32(21111), // 0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8
|
||||
bip65_height: BlockHeight::from_u32(581885), // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
|
||||
bip66_height: BlockHeight::from_u32(330776), // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
|
||||
rule_change_activation_threshold: BlockInterval::from_u32(1512), // 75%
|
||||
miner_confirmation_window: BlockInterval::from_u32(2016),
|
||||
rule_change_activation_threshold: BlockHeightInterval::from_u32(1512), // 75%
|
||||
miner_confirmation_window: BlockHeightInterval::from_u32(2016),
|
||||
pow_limit: Target::MAX_ATTAINABLE_TESTNET,
|
||||
max_attainable_target: Target::MAX_ATTAINABLE_TESTNET,
|
||||
pow_target_spacing: 10 * 60, // 10 minutes.
|
||||
|
@ -187,8 +187,8 @@ impl Params {
|
|||
bip34_height: BlockHeight::from_u32(21111), // 0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8
|
||||
bip65_height: BlockHeight::from_u32(581885), // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
|
||||
bip66_height: BlockHeight::from_u32(330776), // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
|
||||
rule_change_activation_threshold: BlockInterval::from_u32(1512), // 75%
|
||||
miner_confirmation_window: BlockInterval::from_u32(2016),
|
||||
rule_change_activation_threshold: BlockHeightInterval::from_u32(1512), // 75%
|
||||
miner_confirmation_window: BlockHeightInterval::from_u32(2016),
|
||||
pow_limit: Target::MAX_ATTAINABLE_TESTNET,
|
||||
max_attainable_target: Target::MAX_ATTAINABLE_TESTNET,
|
||||
pow_target_spacing: 10 * 60, // 10 minutes.
|
||||
|
@ -204,8 +204,8 @@ impl Params {
|
|||
bip34_height: BlockHeight::from_u32(1),
|
||||
bip65_height: BlockHeight::from_u32(1),
|
||||
bip66_height: BlockHeight::from_u32(1),
|
||||
rule_change_activation_threshold: BlockInterval::from_u32(1512), // 75%
|
||||
miner_confirmation_window: BlockInterval::from_u32(2016),
|
||||
rule_change_activation_threshold: BlockHeightInterval::from_u32(1512), // 75%
|
||||
miner_confirmation_window: BlockHeightInterval::from_u32(2016),
|
||||
pow_limit: Target::MAX_ATTAINABLE_TESTNET,
|
||||
max_attainable_target: Target::MAX_ATTAINABLE_TESTNET,
|
||||
pow_target_spacing: 10 * 60, // 10 minutes.
|
||||
|
@ -221,8 +221,8 @@ impl Params {
|
|||
bip34_height: BlockHeight::from_u32(1),
|
||||
bip65_height: BlockHeight::from_u32(1),
|
||||
bip66_height: BlockHeight::from_u32(1),
|
||||
rule_change_activation_threshold: BlockInterval::from_u32(1916), // 95%
|
||||
miner_confirmation_window: BlockInterval::from_u32(2016),
|
||||
rule_change_activation_threshold: BlockHeightInterval::from_u32(1916), // 95%
|
||||
miner_confirmation_window: BlockHeightInterval::from_u32(2016),
|
||||
pow_limit: Target::MAX_ATTAINABLE_SIGNET,
|
||||
max_attainable_target: Target::MAX_ATTAINABLE_SIGNET,
|
||||
pow_target_spacing: 10 * 60, // 10 minutes.
|
||||
|
@ -238,8 +238,8 @@ impl Params {
|
|||
bip34_height: BlockHeight::from_u32(100000000), // not activated on regtest
|
||||
bip65_height: BlockHeight::from_u32(1351),
|
||||
bip66_height: BlockHeight::from_u32(1251), // used only in rpc tests
|
||||
rule_change_activation_threshold: BlockInterval::from_u32(108), // 75%
|
||||
miner_confirmation_window: BlockInterval::from_u32(144),
|
||||
rule_change_activation_threshold: BlockHeightInterval::from_u32(108), // 75%
|
||||
miner_confirmation_window: BlockHeightInterval::from_u32(144),
|
||||
pow_limit: Target::MAX_ATTAINABLE_REGTEST,
|
||||
max_attainable_target: Target::MAX_ATTAINABLE_REGTEST,
|
||||
pow_target_spacing: 10 * 60, // 10 minutes.
|
||||
|
|
|
@ -52,12 +52,16 @@ pub mod witness;
|
|||
#[doc(inline)]
|
||||
pub use units::{
|
||||
amount::{self, Amount, SignedAmount},
|
||||
block::{BlockHeight, BlockInterval},
|
||||
block::{BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval},
|
||||
fee_rate::{self, FeeRate},
|
||||
time::{self, BlockTime},
|
||||
weight::{self, Weight},
|
||||
};
|
||||
|
||||
#[deprecated(since = "TBD", note = "use `BlockHeightInterval` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type BlockInterval = BlockHeightInterval;
|
||||
|
||||
#[doc(inline)]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use self::{
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{relative, TxIn};
|
|||
#[rustfmt::skip] // Keep public re-exports separate.
|
||||
#[doc(inline)]
|
||||
pub use units::locktime::relative::{HeightInterval, MtpInterval, TimeOverflowError};
|
||||
use units::mtp_height::MtpAndHeight;
|
||||
use units::{BlockHeight, BlockMtp};
|
||||
|
||||
#[deprecated(since = "TBD", note = "use `Mtp` instead")]
|
||||
#[doc(hidden)]
|
||||
|
@ -44,9 +44,7 @@ pub type Time = MtpInterval;
|
|||
///
|
||||
/// ```
|
||||
/// use bitcoin_primitives::relative;
|
||||
/// use bitcoin_primitives::BlockTime;
|
||||
/// use bitcoin_primitives::BlockHeight;
|
||||
/// use units::mtp_height::MtpAndHeight;
|
||||
/// use bitcoin_primitives::{BlockHeight, BlockMtp, BlockTime};
|
||||
/// let lock_by_height = relative::LockTime::from_height(144); // 144 blocks, approx 24h.
|
||||
/// assert!(lock_by_height.is_block_height());
|
||||
///
|
||||
|
@ -65,14 +63,15 @@ pub type Time = MtpInterval;
|
|||
/// let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
|
||||
///
|
||||
/// let current_height = BlockHeight::from(100);
|
||||
/// let utxo_height = BlockHeight::from(80);
|
||||
/// let current_mtp = BlockMtp::new(timestamps);
|
||||
///
|
||||
/// let utxo_height = BlockHeight::from(80);
|
||||
/// let utxo_mtp = BlockMtp::new(utxo_timestamps);
|
||||
///
|
||||
/// let chain_tip = MtpAndHeight::new(current_height, timestamps);
|
||||
/// let utxo_mined_at = MtpAndHeight::new(utxo_height, utxo_timestamps);
|
||||
/// 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));
|
||||
/// assert!(locktime.is_satisfied_by(current_height, current_mtp, utxo_height, utxo_mtp));
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
|
@ -226,10 +225,8 @@ impl LockTime {
|
|||
///
|
||||
/// ```rust
|
||||
/// # use bitcoin_primitives::relative::Time;
|
||||
/// # use units::mtp_height::MtpAndHeight;
|
||||
/// # use bitcoin_primitives::BlockHeight;
|
||||
/// # use bitcoin_primitives::{BlockHeight, BlockMtp, BlockTime};
|
||||
/// # use bitcoin_primitives::relative::LockTime;
|
||||
/// # use bitcoin_primitives::BlockTime;
|
||||
///
|
||||
/// fn generate_timestamps(start: u32, step: u16) -> [BlockTime; 11] {
|
||||
/// let mut timestamps = [BlockTime::from_u32(0); 11];
|
||||
|
@ -243,19 +240,26 @@ impl LockTime {
|
|||
/// let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
|
||||
///
|
||||
/// let current_height = BlockHeight::from_u32(100);
|
||||
/// let current_mtp = BlockMtp::new(timestamps);
|
||||
/// let utxo_height = BlockHeight::from_u32(80);
|
||||
/// let utxo_mtp = BlockMtp::new(utxo_timestamps);
|
||||
///
|
||||
/// let chain_tip = MtpAndHeight::new(current_height, timestamps);
|
||||
/// let utxo_mined_at = MtpAndHeight::new(utxo_height, utxo_timestamps);
|
||||
/// let locktime = LockTime::Time(Time::from_512_second_intervals(10));
|
||||
///
|
||||
/// // Check if locktime is satisfied
|
||||
/// assert!(locktime.is_satisfied_by(chain_tip, utxo_mined_at));
|
||||
/// assert!(locktime.is_satisfied_by(current_height, current_mtp, utxo_height, utxo_mtp));
|
||||
/// ```
|
||||
pub fn is_satisfied_by(self, chain_tip: MtpAndHeight, utxo_mined_at: MtpAndHeight) -> bool {
|
||||
pub fn is_satisfied_by(
|
||||
self,
|
||||
chain_tip_height: BlockHeight,
|
||||
chain_tip_mtp: BlockMtp,
|
||||
utxo_mined_at_height: BlockHeight,
|
||||
utxo_mined_at_mtp: BlockMtp,
|
||||
) -> bool {
|
||||
match self {
|
||||
LockTime::Blocks(blocks) => blocks.is_satisfied_by(chain_tip, utxo_mined_at),
|
||||
LockTime::Time(time) => time.is_satisfied_by(chain_tip, utxo_mined_at),
|
||||
LockTime::Blocks(blocks) =>
|
||||
blocks.is_satisfied_by(chain_tip_height, utxo_mined_at_height),
|
||||
LockTime::Time(time) => time.is_satisfied_by(chain_tip_mtp, utxo_mined_at_mtp),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -695,38 +699,55 @@ 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 chain_tip = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps);
|
||||
let utxo_mined_at = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
|
||||
let chain_height = BlockHeight::from_u32(100);
|
||||
let chain_mtp = BlockMtp::new(timestamps);
|
||||
let utxo_height = BlockHeight::from_u32(80);
|
||||
let utxo_mtp = BlockMtp::new(utxo_timestamps);
|
||||
|
||||
let lock1 = LockTime::Blocks(HeightInterval::from(10));
|
||||
assert!(lock1.is_satisfied_by(chain_tip, utxo_mined_at));
|
||||
assert!(lock1.is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp));
|
||||
|
||||
let lock2 = LockTime::Blocks(HeightInterval::from(21));
|
||||
assert!(!lock2.is_satisfied_by(chain_tip, utxo_mined_at));
|
||||
assert!(!lock2.is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp));
|
||||
|
||||
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_height, chain_mtp, utxo_height, utxo_mtp));
|
||||
|
||||
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_height, chain_mtp, utxo_height, utxo_mtp));
|
||||
|
||||
assert!(LockTime::ZERO.is_satisfied_by(chain_tip, utxo_mined_at));
|
||||
assert!(LockTime::from_512_second_intervals(0).is_satisfied_by(chain_tip, utxo_mined_at));
|
||||
assert!(LockTime::ZERO.is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp));
|
||||
assert!(LockTime::from_512_second_intervals(0).is_satisfied_by(
|
||||
chain_height,
|
||||
chain_mtp,
|
||||
utxo_height,
|
||||
utxo_mtp
|
||||
));
|
||||
|
||||
let lock6 = LockTime::from_seconds_floor(5000).unwrap();
|
||||
assert!(lock6.is_satisfied_by(chain_tip, utxo_mined_at));
|
||||
assert!(lock6.is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp));
|
||||
|
||||
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_height, chain_mtp, utxo_height, utxo_mtp));
|
||||
|
||||
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_height, chain_mtp, utxo_height, utxo_mtp));
|
||||
|
||||
let max_chain_tip =
|
||||
MtpAndHeight::new(BlockHeight::from_u32(u32::MAX), generate_timestamps(u32::MAX, 100));
|
||||
let max_utxo_mined_at =
|
||||
MtpAndHeight::new(BlockHeight::MAX, generate_timestamps(u32::MAX, 100));
|
||||
assert!(!max_height_lock.is_satisfied_by(max_chain_tip, max_utxo_mined_at));
|
||||
assert!(!max_time_lock.is_satisfied_by(max_chain_tip, max_utxo_mined_at));
|
||||
let max_chain_height = BlockHeight::from_u32(u32::MAX);
|
||||
let max_chain_mtp = BlockMtp::new(generate_timestamps(u32::MAX, 100));
|
||||
let max_utxo_height = BlockHeight::MAX;
|
||||
let max_utxo_mtp = max_chain_mtp;
|
||||
assert!(!max_height_lock.is_satisfied_by(
|
||||
max_chain_height,
|
||||
max_chain_mtp,
|
||||
max_utxo_height,
|
||||
max_utxo_mtp
|
||||
));
|
||||
assert!(!max_time_lock.is_satisfied_by(
|
||||
max_chain_height,
|
||||
max_chain_mtp,
|
||||
max_utxo_height,
|
||||
max_utxo_mtp
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,54 +22,90 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::locktime;
|
||||
use crate::locktime::{absolute, relative};
|
||||
|
||||
/// The block height, zero denotes the genesis block.
|
||||
///
|
||||
/// This type is not meant for constructing height based timelocks, this is a general purpose block
|
||||
/// height abstraction. For locktimes please see [`locktime::absolute::Height`].
|
||||
///
|
||||
/// This is a thin wrapper around a `u32` that may take on all values of a `u32`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
// Public to try and make it really clear that there are no invariants.
|
||||
pub struct BlockHeight(pub u32);
|
||||
macro_rules! impl_u32_wrapper {
|
||||
{
|
||||
$(#[$($type_attrs:tt)*])*
|
||||
$type_vis:vis struct $newtype:ident($inner_vis:vis u32);
|
||||
} => {
|
||||
$(#[$($type_attrs)*])*
|
||||
$type_vis struct $newtype($inner_vis u32);
|
||||
|
||||
impl BlockHeight {
|
||||
impl $newtype {
|
||||
/// Block height 0, the genesis block.
|
||||
pub const ZERO: Self = BlockHeight(0);
|
||||
pub const ZERO: Self = Self(0);
|
||||
|
||||
/// The minimum block height (0), the genesis block.
|
||||
pub const MIN: Self = Self::ZERO;
|
||||
|
||||
/// The maximum block height.
|
||||
pub const MAX: Self = BlockHeight(u32::MAX);
|
||||
pub const MAX: Self = Self(u32::MAX);
|
||||
|
||||
/// Constructs a new block height from a `u32`.
|
||||
pub const fn from_u32(inner: u32) -> Self { Self(inner) }
|
||||
|
||||
/// Returns block height as a `u32`.
|
||||
pub const fn to_u32(self) -> u32 { self.0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BlockHeight {
|
||||
impl fmt::Display for $newtype {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
|
||||
}
|
||||
}
|
||||
|
||||
crate::impl_parse_str_from_int_infallible!(BlockHeight, u32, from);
|
||||
crate::impl_parse_str_from_int_infallible!($newtype, u32, from);
|
||||
|
||||
impl From<u32> for BlockHeight {
|
||||
impl From<u32> for $newtype {
|
||||
fn from(inner: u32) -> Self { Self::from_u32(inner) }
|
||||
}
|
||||
|
||||
impl From<$newtype> for u32 {
|
||||
fn from(height: $newtype) -> Self { height.to_u32() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<'a> Arbitrary<'a> for $newtype {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let choice = u.int_in_range(0..=2)?;
|
||||
match choice {
|
||||
0 => Ok(Self::ZERO),
|
||||
1 => Ok(Self::MIN),
|
||||
2 => Ok(Self::MAX),
|
||||
_ => Ok(Self::from_u32(u32::arbitrary(u)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockHeight> for u32 {
|
||||
fn from(height: BlockHeight) -> Self { height.to_u32() }
|
||||
impl_u32_wrapper! {
|
||||
/// A block height. Zero denotes the genesis block.
|
||||
///
|
||||
/// This type is not meant for constructing height based timelocks. It is a general purpose
|
||||
/// blockheight abstraction. For locktimes please see [`locktime::absolute::Height`].
|
||||
///
|
||||
/// This is a thin wrapper around a `u32` that may take on all values of a `u32`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
// Public to try and make it really clear that there are no invariants.
|
||||
pub struct BlockHeight(pub u32);
|
||||
}
|
||||
|
||||
impl BlockHeight {
|
||||
/// Attempt to subtract two [`BlockHeight`]s, returning `None` in case of overflow.
|
||||
pub fn checked_sub(self, other: Self) -> Option<BlockHeightInterval> {
|
||||
self.0.checked_sub(other.0).map(BlockHeightInterval)
|
||||
}
|
||||
|
||||
/// Attempt to add an interval to this [`BlockHeight`], returning `None` in case of overflow.
|
||||
pub fn checked_add(self, other: BlockHeightInterval) -> Option<Self> {
|
||||
self.0.checked_add(other.0).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<absolute::Height> for BlockHeight {
|
||||
/// Converts a [`locktime::absolute::Height`] to a [`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.
|
||||
/// minus one, while [`BlockHeight`] may take the full range of `u32`.
|
||||
fn from(h: absolute::Height) -> Self { Self::from_u32(h.to_u32()) }
|
||||
}
|
||||
|
||||
|
@ -79,72 +115,49 @@ impl TryFrom<BlockHeight> for absolute::Height {
|
|||
/// Converts a [`BlockHeight`] to a [`locktime::absolute::Height`].
|
||||
///
|
||||
/// 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.
|
||||
/// minus one, while [`BlockHeight`] may take the full range of `u32`.
|
||||
fn try_from(h: BlockHeight) -> Result<Self, Self::Error> {
|
||||
absolute::Height::from_u32(h.to_u32())
|
||||
}
|
||||
}
|
||||
|
||||
/// The block interval.
|
||||
///
|
||||
/// Block interval is an integer type denoting the number of blocks that has passed since some point
|
||||
/// i.e., this type is meant for usage as a relative block measure.
|
||||
///
|
||||
/// This type is not meant for constructing relative height based timelocks, this is a general
|
||||
/// purpose block interval abstraction. For locktimes please see [`locktime::relative::Height`].
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
// Public to try and make it really clear that there are no invariants.
|
||||
pub struct BlockInterval(pub u32);
|
||||
|
||||
impl BlockInterval {
|
||||
/// Block interval 0 i.e., the current block.
|
||||
pub const ZERO: Self = BlockInterval(0);
|
||||
|
||||
/// The minimum block interval (0).
|
||||
pub const MIN: Self = Self::ZERO;
|
||||
|
||||
/// The maximum block interval.
|
||||
pub const MAX: Self = BlockInterval(u32::MAX);
|
||||
|
||||
/// Constructs a new block interval from a `u32`.
|
||||
pub const fn from_u32(inner: u32) -> Self { Self(inner) }
|
||||
|
||||
/// Returns block interval as a `u32`.
|
||||
pub const fn to_u32(self) -> u32 { self.0 }
|
||||
impl_u32_wrapper! {
|
||||
/// An unsigned block interval.
|
||||
///
|
||||
/// Block interval is an integer type representing a difference between the heights of two blocks.
|
||||
///
|
||||
/// This type is not meant for constructing relative height based timelocks. It is a general
|
||||
/// purpose block interval abstraction. For locktimes please see [`locktime::relative::Height`].
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
// Public to try and make it really clear that there are no invariants.
|
||||
pub struct BlockHeightInterval(pub u32);
|
||||
}
|
||||
|
||||
impl fmt::Display for BlockInterval {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
|
||||
impl BlockHeightInterval {
|
||||
/// Attempt to subtract two [`BlockHeightInterval`]s, returning `None` in case of overflow.
|
||||
pub fn checked_sub(self, other: Self) -> Option<Self> { self.0.checked_sub(other.0).map(Self) }
|
||||
|
||||
/// Attempt to add two [`BlockHeightInterval`]s, returning `None` in case of overflow.
|
||||
pub fn checked_add(self, other: Self) -> Option<Self> { self.0.checked_add(other.0).map(Self) }
|
||||
}
|
||||
|
||||
crate::impl_parse_str_from_int_infallible!(BlockInterval, u32, from);
|
||||
|
||||
impl From<u32> for BlockInterval {
|
||||
fn from(inner: u32) -> Self { Self::from_u32(inner) }
|
||||
}
|
||||
|
||||
impl From<BlockInterval> for u32 {
|
||||
fn from(height: BlockInterval) -> Self { height.to_u32() }
|
||||
}
|
||||
|
||||
impl From<relative::HeightInterval> for BlockInterval {
|
||||
/// Converts a [`locktime::relative::HeightInterval`] to a [`BlockInterval`].
|
||||
impl From<relative::HeightInterval> for BlockHeightInterval {
|
||||
/// Converts a [`locktime::relative::HeightInterval`] to a [`BlockHeightInterval`].
|
||||
///
|
||||
/// 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.
|
||||
/// [`BlockHeightInterval`] is a thin wrapper around a `u32`, the two types are not interchangeable.
|
||||
fn from(h: relative::HeightInterval) -> Self { Self::from_u32(h.to_height().into()) }
|
||||
}
|
||||
|
||||
impl TryFrom<BlockInterval> for relative::HeightInterval {
|
||||
impl TryFrom<BlockHeightInterval> for relative::HeightInterval {
|
||||
type Error = TooBigForRelativeBlockHeightIntervalError;
|
||||
|
||||
/// Converts a [`BlockInterval`] to a [`locktime::relative::HeightInterval`].
|
||||
/// Converts a [`BlockHeightInterval`] 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> {
|
||||
/// [`BlockHeightInterval`] is a thin wrapper around a `u32`, the two types are not interchangeable.
|
||||
fn try_from(h: BlockHeightInterval) -> Result<Self, Self::Error> {
|
||||
let h = h.to_u32();
|
||||
|
||||
if h > u32::from(u16::MAX) {
|
||||
|
@ -154,6 +167,121 @@ impl TryFrom<BlockInterval> for relative::HeightInterval {
|
|||
}
|
||||
}
|
||||
|
||||
impl_u32_wrapper! {
|
||||
/// The median timestamp of 11 consecutive blocks.
|
||||
///
|
||||
/// This type is not meant for constructing time-based timelocks. It is a general purpose
|
||||
/// MTP abstraction. For locktimes please see [`locktime::absolute::Mtp`].
|
||||
///
|
||||
/// This is a thin wrapper around a `u32` that may take on all values of a `u32`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
// Public to try and make it really clear that there are no invariants.
|
||||
pub struct BlockMtp(pub u32);
|
||||
}
|
||||
|
||||
impl BlockMtp {
|
||||
/// Constructs a [`BlockMtp`] by computing the median‐time‐past from the last 11 block timestamps
|
||||
///
|
||||
/// Because block timestamps are not monotonic, this function internally sorts them;
|
||||
/// it is therefore not important what order they appear in the array; use whatever
|
||||
/// is most convenient.
|
||||
pub fn new(mut timestamps: [crate::BlockTime; 11]) -> Self {
|
||||
timestamps.sort_unstable();
|
||||
Self::from_u32(u32::from(timestamps[5]))
|
||||
}
|
||||
|
||||
/// Attempt to subtract two [`BlockMtp`]s, returning `None` in case of overflow.
|
||||
pub fn checked_sub(self, other: Self) -> Option<BlockMtpInterval> {
|
||||
self.0.checked_sub(other.0).map(BlockMtpInterval)
|
||||
}
|
||||
|
||||
/// Attempt to add an interval to this [`BlockMtp`], returning `None` in case of overflow.
|
||||
pub fn checked_add(self, other: BlockMtpInterval) -> Option<Self> {
|
||||
self.0.checked_add(other.0).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<absolute::Mtp> for BlockMtp {
|
||||
/// Converts a [`locktime::absolute::Mtp`] to a [`BlockMtp`].
|
||||
///
|
||||
/// An absolute locktime MTP has a minimum value of [`absolute::LOCK_TIME_THRESHOLD`],
|
||||
/// while [`BlockMtp`] may take the full range of `u32`.
|
||||
fn from(h: absolute::Mtp) -> Self { Self::from_u32(h.to_u32()) }
|
||||
}
|
||||
|
||||
impl TryFrom<BlockMtp> for absolute::Mtp {
|
||||
type Error = absolute::ConversionError;
|
||||
|
||||
/// Converts a [`BlockHeight`] to a [`locktime::absolute::Height`].
|
||||
///
|
||||
/// An absolute locktime MTP has a minimum value of [`absolute::LOCK_TIME_THRESHOLD`],
|
||||
/// while [`BlockMtp`] may take the full range of `u32`.
|
||||
fn try_from(h: BlockMtp) -> Result<Self, Self::Error> { absolute::Mtp::from_u32(h.to_u32()) }
|
||||
}
|
||||
|
||||
impl_u32_wrapper! {
|
||||
/// An unsigned difference between two [`BlockMtp`]s.
|
||||
///
|
||||
/// This type is not meant for constructing time-based timelocks. It is a general purpose
|
||||
/// MTP abstraction. For locktimes please see [`locktime::relative::MtpInterval`].
|
||||
///
|
||||
/// This is a thin wrapper around a `u32` that may take on all values of a `u32`.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
// Public to try and make it really clear that there are no invariants.
|
||||
pub struct BlockMtpInterval(pub u32);
|
||||
}
|
||||
|
||||
impl BlockMtpInterval {
|
||||
/// Converts a [`BlockMtpInterval`] to a [`locktime::relative::MtpInterval`], rounding down.
|
||||
///
|
||||
/// Relative timelock MTP intervals have a resolution of 512 seconds, while
|
||||
/// [`BlockMtpInterval`], like all block timestamp types, has a one-second resolution.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Errors if the MTP is out-of-range (in excess of 512 times `u16::MAX` seconds, or about
|
||||
/// 388 days) for a time-based relative locktime.
|
||||
#[inline]
|
||||
pub const fn to_relative_mtp_interval_floor(
|
||||
self,
|
||||
) -> Result<relative::MtpInterval, relative::TimeOverflowError> {
|
||||
relative::MtpInterval::from_seconds_floor(self.to_u32())
|
||||
}
|
||||
|
||||
/// Converts a [`BlockMtpInterval`] to a [`locktime::relative::MtpInterval`], rounding up.
|
||||
///
|
||||
/// Relative timelock MTP intervals have a resolution of 512 seconds, while
|
||||
/// [`BlockMtpInterval`], like all block timestamp types, has a one-second resolution.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Errors if the MTP is out-of-range (in excess of 512 times `u16::MAX` seconds, or about
|
||||
/// 388 days) for a time-based relative locktime.
|
||||
#[inline]
|
||||
pub const fn to_relative_mtp_interval_ceil(
|
||||
self,
|
||||
) -> Result<relative::MtpInterval, relative::TimeOverflowError> {
|
||||
relative::MtpInterval::from_seconds_ceil(self.to_u32())
|
||||
}
|
||||
|
||||
/// Attempt to subtract two [`BlockMtpInterval`]s, returning `None` in case of overflow.
|
||||
pub fn checked_sub(self, other: Self) -> Option<Self> { self.0.checked_sub(other.0).map(Self) }
|
||||
|
||||
/// Attempt to add two [`BlockMtpInterval`]s, returning `None` in case of overflow.
|
||||
pub fn checked_add(self, other: Self) -> Option<Self> { self.0.checked_add(other.0).map(Self) }
|
||||
}
|
||||
|
||||
impl From<relative::MtpInterval> for BlockMtpInterval {
|
||||
/// Converts a [`locktime::relative::MtpInterval`] to a [`BlockMtpInterval `].
|
||||
///
|
||||
/// A relative locktime MTP interval has a resolution of 512 seconds, and a maximum value
|
||||
/// of `u16::MAX` 512-second intervals. [`BlockMtpInterval`] may take the full range of
|
||||
/// `u32`.
|
||||
fn from(h: relative::MtpInterval) -> Self { Self::from_u32(h.to_seconds()) }
|
||||
}
|
||||
|
||||
/// Error returned when the block interval is too big to be used as a relative lock time.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TooBigForRelativeBlockHeightIntervalError(u32);
|
||||
|
@ -175,96 +303,141 @@ impl std::error::Error for TooBigForRelativeBlockHeightIntervalError {}
|
|||
crate::internal_macros::impl_op_for_references! {
|
||||
// height - height = interval
|
||||
impl ops::Sub<BlockHeight> for BlockHeight {
|
||||
type Output = BlockInterval;
|
||||
type Output = BlockHeightInterval;
|
||||
|
||||
fn sub(self, rhs: BlockHeight) -> Self::Output {
|
||||
let interval = self.to_u32() - rhs.to_u32();
|
||||
BlockInterval::from_u32(interval)
|
||||
BlockHeightInterval::from_u32(interval)
|
||||
}
|
||||
}
|
||||
|
||||
// height + interval = height
|
||||
impl ops::Add<BlockInterval> for BlockHeight {
|
||||
impl ops::Add<BlockHeightInterval> for BlockHeight {
|
||||
type Output = BlockHeight;
|
||||
|
||||
fn add(self, rhs: BlockInterval) -> Self::Output {
|
||||
fn add(self, rhs: BlockHeightInterval) -> Self::Output {
|
||||
let height = self.to_u32() + rhs.to_u32();
|
||||
BlockHeight::from_u32(height)
|
||||
}
|
||||
}
|
||||
|
||||
// height - interval = height
|
||||
impl ops::Sub<BlockInterval> for BlockHeight {
|
||||
impl ops::Sub<BlockHeightInterval> for BlockHeight {
|
||||
type Output = BlockHeight;
|
||||
|
||||
fn sub(self, rhs: BlockInterval) -> Self::Output {
|
||||
fn sub(self, rhs: BlockHeightInterval) -> Self::Output {
|
||||
let height = self.to_u32() - rhs.to_u32();
|
||||
BlockHeight::from_u32(height)
|
||||
}
|
||||
}
|
||||
|
||||
// interval + interval = interval
|
||||
impl ops::Add<BlockInterval> for BlockInterval {
|
||||
type Output = BlockInterval;
|
||||
impl ops::Add<BlockHeightInterval> for BlockHeightInterval {
|
||||
type Output = BlockHeightInterval;
|
||||
|
||||
fn add(self, rhs: BlockInterval) -> Self::Output {
|
||||
fn add(self, rhs: BlockHeightInterval) -> Self::Output {
|
||||
let height = self.to_u32() + rhs.to_u32();
|
||||
BlockInterval::from_u32(height)
|
||||
BlockHeightInterval::from_u32(height)
|
||||
}
|
||||
}
|
||||
|
||||
// interval - interval = interval
|
||||
impl ops::Sub<BlockInterval> for BlockInterval {
|
||||
type Output = BlockInterval;
|
||||
impl ops::Sub<BlockHeightInterval> for BlockHeightInterval {
|
||||
type Output = BlockHeightInterval;
|
||||
|
||||
fn sub(self, rhs: BlockInterval) -> Self::Output {
|
||||
fn sub(self, rhs: BlockHeightInterval) -> Self::Output {
|
||||
let height = self.to_u32() - rhs.to_u32();
|
||||
BlockInterval::from_u32(height)
|
||||
BlockHeightInterval::from_u32(height)
|
||||
}
|
||||
}
|
||||
|
||||
// height - height = interval
|
||||
impl ops::Sub<BlockMtp> for BlockMtp {
|
||||
type Output = BlockMtpInterval;
|
||||
|
||||
fn sub(self, rhs: BlockMtp) -> Self::Output {
|
||||
let interval = self.to_u32() - rhs.to_u32();
|
||||
BlockMtpInterval::from_u32(interval)
|
||||
}
|
||||
}
|
||||
|
||||
// height + interval = height
|
||||
impl ops::Add<BlockMtpInterval> for BlockMtp {
|
||||
type Output = BlockMtp;
|
||||
|
||||
fn add(self, rhs: BlockMtpInterval) -> Self::Output {
|
||||
let height = self.to_u32() + rhs.to_u32();
|
||||
BlockMtp::from_u32(height)
|
||||
}
|
||||
}
|
||||
|
||||
// height - interval = height
|
||||
impl ops::Sub<BlockMtpInterval> for BlockMtp {
|
||||
type Output = BlockMtp;
|
||||
|
||||
fn sub(self, rhs: BlockMtpInterval) -> Self::Output {
|
||||
let height = self.to_u32() - rhs.to_u32();
|
||||
BlockMtp::from_u32(height)
|
||||
}
|
||||
}
|
||||
|
||||
// interval + interval = interval
|
||||
impl ops::Add<BlockMtpInterval> for BlockMtpInterval {
|
||||
type Output = BlockMtpInterval;
|
||||
|
||||
fn add(self, rhs: BlockMtpInterval) -> Self::Output {
|
||||
let height = self.to_u32() + rhs.to_u32();
|
||||
BlockMtpInterval::from_u32(height)
|
||||
}
|
||||
}
|
||||
|
||||
// interval - interval = interval
|
||||
impl ops::Sub<BlockMtpInterval> for BlockMtpInterval {
|
||||
type Output = BlockMtpInterval;
|
||||
|
||||
fn sub(self, rhs: BlockMtpInterval) -> Self::Output {
|
||||
let height = self.to_u32() - rhs.to_u32();
|
||||
BlockMtpInterval::from_u32(height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::internal_macros::impl_add_assign!(BlockInterval);
|
||||
crate::internal_macros::impl_sub_assign!(BlockInterval);
|
||||
crate::internal_macros::impl_add_assign!(BlockHeightInterval);
|
||||
crate::internal_macros::impl_sub_assign!(BlockHeightInterval);
|
||||
crate::internal_macros::impl_add_assign!(BlockMtpInterval);
|
||||
crate::internal_macros::impl_sub_assign!(BlockMtpInterval);
|
||||
|
||||
impl core::iter::Sum for BlockInterval {
|
||||
impl core::iter::Sum for BlockHeightInterval {
|
||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||
let sum = iter.map(|interval| interval.0).sum();
|
||||
BlockInterval::from_u32(sum)
|
||||
BlockHeightInterval::from_u32(sum)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> core::iter::Sum<&'a BlockInterval> for BlockInterval {
|
||||
impl<'a> core::iter::Sum<&'a BlockHeightInterval> for BlockHeightInterval {
|
||||
fn sum<I>(iter: I) -> Self
|
||||
where
|
||||
I: Iterator<Item = &'a BlockInterval>,
|
||||
I: Iterator<Item = &'a BlockHeightInterval>,
|
||||
{
|
||||
let sum = iter.map(|interval| interval.0).sum();
|
||||
BlockInterval::from_u32(sum)
|
||||
BlockHeightInterval::from_u32(sum)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<'a> Arbitrary<'a> for BlockHeight {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let choice = u.int_in_range(0..=2)?;
|
||||
match choice {
|
||||
0 => Ok(BlockHeight::MIN),
|
||||
1 => Ok(BlockHeight::MAX),
|
||||
_ => Ok(BlockHeight::from_u32(u32::arbitrary(u)?)),
|
||||
}
|
||||
impl core::iter::Sum for BlockMtpInterval {
|
||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||
let sum = iter.map(|interval| interval.0).sum();
|
||||
BlockMtpInterval::from_u32(sum)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<'a> Arbitrary<'a> for BlockInterval {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let choice = u.int_in_range(0..=2)?;
|
||||
match choice {
|
||||
0 => Ok(BlockInterval::MIN),
|
||||
1 => Ok(BlockInterval::MAX),
|
||||
_ => Ok(BlockInterval::from_u32(u32::arbitrary(u)?)),
|
||||
}
|
||||
impl<'a> core::iter::Sum<&'a BlockMtpInterval> for BlockMtpInterval {
|
||||
fn sum<I>(iter: I) -> Self
|
||||
where
|
||||
I: Iterator<Item = &'a BlockMtpInterval>,
|
||||
{
|
||||
let sum = iter.map(|interval| interval.0).sum();
|
||||
BlockMtpInterval::from_u32(sum)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,17 +450,19 @@ mod tests {
|
|||
let height: u32 = BlockHeight(100).into();
|
||||
assert_eq!(height, 100);
|
||||
|
||||
let interval: u32 = BlockInterval(100).into();
|
||||
let interval: u32 = BlockHeightInterval(100).into();
|
||||
assert_eq!(interval, 100);
|
||||
|
||||
let interval_from_height: BlockInterval = relative::HeightInterval::from(10u16).into();
|
||||
let interval_from_height: BlockHeightInterval =
|
||||
relative::HeightInterval::from(10u16).into();
|
||||
assert_eq!(interval_from_height.to_u32(), 10u32);
|
||||
|
||||
let invalid_height_greater =
|
||||
relative::HeightInterval::try_from(BlockInterval(u32::from(u16::MAX) + 1));
|
||||
relative::HeightInterval::try_from(BlockHeightInterval(u32::from(u16::MAX) + 1));
|
||||
assert!(invalid_height_greater.is_err());
|
||||
|
||||
let valid_height = relative::HeightInterval::try_from(BlockInterval(u32::from(u16::MAX)));
|
||||
let valid_height =
|
||||
relative::HeightInterval::try_from(BlockHeightInterval(u32::from(u16::MAX)));
|
||||
assert!(valid_height.is_ok());
|
||||
}
|
||||
|
||||
|
@ -295,39 +470,41 @@ mod tests {
|
|||
#[test]
|
||||
fn all_available_ops() {
|
||||
// height - height = interval
|
||||
assert!(BlockHeight(10) - BlockHeight(7) == BlockInterval(3));
|
||||
assert!(BlockHeight(10) - BlockHeight(7) == BlockHeightInterval(3));
|
||||
|
||||
// height + interval = height
|
||||
assert!(BlockHeight(100) + BlockInterval(1) == BlockHeight(101));
|
||||
assert!(BlockHeight(100) + BlockHeightInterval(1) == BlockHeight(101));
|
||||
|
||||
// height - interval == height
|
||||
assert!(BlockHeight(100) - BlockInterval(1) == BlockHeight(99));
|
||||
assert!(BlockHeight(100) - BlockHeightInterval(1) == BlockHeight(99));
|
||||
|
||||
// interval + interval = interval
|
||||
assert!(BlockInterval(1) + BlockInterval(2) == BlockInterval(3));
|
||||
assert!(BlockHeightInterval(1) + BlockHeightInterval(2) == BlockHeightInterval(3));
|
||||
|
||||
// interval - interval = interval
|
||||
assert!(BlockInterval(10) - BlockInterval(7) == BlockInterval(3));
|
||||
assert!(BlockHeightInterval(10) - BlockHeightInterval(7) == BlockHeightInterval(3));
|
||||
|
||||
assert!(
|
||||
[BlockInterval(1), BlockInterval(2), BlockInterval(3)].iter().sum::<BlockInterval>()
|
||||
== BlockInterval(6)
|
||||
[BlockHeightInterval(1), BlockHeightInterval(2), BlockHeightInterval(3)]
|
||||
.iter()
|
||||
.sum::<BlockHeightInterval>()
|
||||
== BlockHeightInterval(6)
|
||||
);
|
||||
assert!(
|
||||
[BlockInterval(4), BlockInterval(5), BlockInterval(6)]
|
||||
[BlockHeightInterval(4), BlockHeightInterval(5), BlockHeightInterval(6)]
|
||||
.into_iter()
|
||||
.sum::<BlockInterval>()
|
||||
== BlockInterval(15)
|
||||
.sum::<BlockHeightInterval>()
|
||||
== BlockHeightInterval(15)
|
||||
);
|
||||
|
||||
// interval += interval
|
||||
let mut int = BlockInterval(1);
|
||||
int += BlockInterval(2);
|
||||
assert_eq!(int, BlockInterval(3));
|
||||
let mut int = BlockHeightInterval(1);
|
||||
int += BlockHeightInterval(2);
|
||||
assert_eq!(int, BlockHeightInterval(3));
|
||||
|
||||
// interval -= interval
|
||||
let mut int = BlockInterval(10);
|
||||
int -= BlockInterval(7);
|
||||
assert_eq!(int, BlockInterval(3));
|
||||
let mut int = BlockHeightInterval(10);
|
||||
int -= BlockHeightInterval(7);
|
||||
assert_eq!(int, BlockHeightInterval(3));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ pub mod amount;
|
|||
pub mod block;
|
||||
pub mod fee_rate;
|
||||
pub mod locktime;
|
||||
pub mod mtp_height;
|
||||
pub mod parse;
|
||||
pub mod time;
|
||||
pub mod weight;
|
||||
|
@ -59,10 +58,14 @@ pub mod weight;
|
|||
#[rustfmt::skip]
|
||||
pub use self::{
|
||||
amount::{Amount, SignedAmount},
|
||||
block::{BlockHeight, BlockInterval},
|
||||
block::{BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval},
|
||||
fee_rate::FeeRate,
|
||||
result::{NumOpError, NumOpResult, MathOp},
|
||||
time::BlockTime,
|
||||
weight::Weight
|
||||
};
|
||||
pub(crate) use self::result::OptionExt;
|
||||
|
||||
#[deprecated(since = "TBD", note = "use `BlockHeightInterval` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type BlockInterval = BlockHeightInterval;
|
||||
|
|
|
@ -154,6 +154,22 @@ impl Mtp {
|
|||
/// The maximum MTP allowable in a locktime (Sun Feb 07 2106 06:28:15 GMT+0000).
|
||||
pub const MAX: Self = Mtp(u32::MAX);
|
||||
|
||||
/// Constructs an [`Mtp`] by computing the median‐time‐past from the last 11 block timestamps
|
||||
///
|
||||
/// Because block timestamps are not monotonic, this function internally sorts them;
|
||||
/// it is therefore not important what order they appear in the array; use whatever
|
||||
/// is most convenient.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the median block timestamp is not in the allowable range of MTPs in a
|
||||
/// locktime: `[500_000_000, 2^32 - 1]`. Because there is a consensus rule that MTP
|
||||
/// be monotonically increasing, and the MTP of the first 11 blocks exceeds `500_000_000`
|
||||
/// for every real-life chain, this error typically cannot be hit in practice.
|
||||
pub fn new(timestamps: [crate::BlockTime; 11]) -> Result<Self, ConversionError> {
|
||||
crate::BlockMtp::new(timestamps).try_into()
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
|
@ -539,4 +555,32 @@ mod tests {
|
|||
assert_eq!(format!("{}", blocks), "expected lock-by-blockheight (must be < 500000000)");
|
||||
assert_eq!(format!("{}", seconds), "expected lock-by-blocktime (must be >= 500000000)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_chain_computes_mtp() {
|
||||
use crate::BlockTime;
|
||||
|
||||
let mut timestamps = [
|
||||
BlockTime::from_u32(500_000_010),
|
||||
BlockTime::from_u32(500_000_003),
|
||||
BlockTime::from_u32(500_000_005),
|
||||
BlockTime::from_u32(500_000_008),
|
||||
BlockTime::from_u32(500_000_001),
|
||||
BlockTime::from_u32(500_000_004),
|
||||
BlockTime::from_u32(500_000_006),
|
||||
BlockTime::from_u32(500_000_009),
|
||||
BlockTime::from_u32(500_000_002),
|
||||
BlockTime::from_u32(500_000_007),
|
||||
BlockTime::from_u32(500_000_000),
|
||||
];
|
||||
|
||||
// Try various reorderings
|
||||
assert_eq!(Mtp::new(timestamps).unwrap().to_u32(), 500_000_005);
|
||||
timestamps.reverse();
|
||||
assert_eq!(Mtp::new(timestamps).unwrap().to_u32(), 500_000_005);
|
||||
timestamps.sort();
|
||||
assert_eq!(Mtp::new(timestamps).unwrap().to_u32(), 500_000_005);
|
||||
timestamps.reverse();
|
||||
assert_eq!(Mtp::new(timestamps).unwrap().to_u32(), 500_000_005);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ use arbitrary::{Arbitrary, Unstructured};
|
|||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::mtp_height::MtpAndHeight;
|
||||
|
||||
#[deprecated(since = "TBD", note = "use `HeightIterval` instead")]
|
||||
#[doc(hidden)]
|
||||
pub type Height = HeightInterval;
|
||||
|
@ -59,21 +57,26 @@ impl HeightInterval {
|
|||
/// Determines whether a relative‐height locktime has matured, taking into account
|
||||
/// both the chain tip and the height at which the UTXO was confirmed.
|
||||
///
|
||||
/// If you have two height intervals `x` and `y`, and want to know whether `x`
|
||||
/// is satisfied by `y`, use `x >= y`.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `self` – The relative block‐height delay (`h`) required after confirmation.
|
||||
/// - `chain_tip` – The current chain state (contains the tip height).
|
||||
/// - `utxo_mined_at` – The chain state at the UTXO’s confirmation block (contains that height).
|
||||
/// - `self` – the relative block‐height delay (`h`) required after confirmation.
|
||||
/// - `chain_tip` – the height of the current chain tip
|
||||
/// - `utxo_mined_at` – the height of the UTXO’s confirmation block
|
||||
///
|
||||
/// # Returns
|
||||
/// - `true` if a UTXO locked by `self` can be spent in a block after `chain_tip`.
|
||||
/// - `false` if the UTXO is still locked at `chain_tip`.
|
||||
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 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,
|
||||
}
|
||||
pub fn is_satisfied_by(
|
||||
self,
|
||||
chain_tip: crate::BlockHeight,
|
||||
utxo_mined_at: crate::BlockHeight,
|
||||
) -> bool {
|
||||
chain_tip
|
||||
.checked_sub(utxo_mined_at)
|
||||
.and_then(|diff: crate::BlockHeightInterval| diff.try_into().ok())
|
||||
.map_or(false, |diff: Self| diff >= self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,6 +157,12 @@ impl MtpInterval {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents the [`MtpInterval`] as an integer number of seconds.
|
||||
#[inline]
|
||||
pub const fn to_seconds(self) -> u32 {
|
||||
self.0 as u32 * 512 // u16->u32 cast ok, const context
|
||||
}
|
||||
|
||||
/// Returns the inner `u16` value.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
|
@ -174,22 +183,26 @@ impl MtpInterval {
|
|||
/// Determines whether a relative‑time lock has matured, taking into account both
|
||||
/// the UTXO’s Median Time Past at confirmation and the required delay.
|
||||
///
|
||||
/// If you have two MTP intervals `x` and `y`, and want to know whether `x`
|
||||
/// is satisfied by `y`, use `x >= y`.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `self` – The relative time delay (`t`) in 512‑second intervals.
|
||||
/// - `chain_tip` – The current chain state, providing the tip’s MTP.
|
||||
/// - `utxo_mined_at` – The chain state at the UTXO’s confirmation, providing its MTP.
|
||||
/// - `self` – the relative time delay (`t`) in 512‑second intervals.
|
||||
/// - `chain_tip` – the MTP of the current chain tip
|
||||
/// - `utxo_mined_at` – the MTP of the UTXO’s confirmation block
|
||||
///
|
||||
/// # Returns
|
||||
/// - `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.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,
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
pub fn is_satisfied_by(
|
||||
self,
|
||||
chain_tip: crate::BlockMtp,
|
||||
utxo_mined_at: crate::BlockMtp,
|
||||
) -> bool {
|
||||
chain_tip
|
||||
.checked_sub(utxo_mined_at)
|
||||
.and_then(|diff: crate::BlockMtpInterval| diff.to_relative_mtp_interval_floor().ok())
|
||||
.map_or(false, |diff: Self| diff >= self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,7 +277,7 @@ mod tests {
|
|||
use internals::serde_round_trip;
|
||||
|
||||
use super::*;
|
||||
use crate::{BlockHeight, BlockTime};
|
||||
use crate::BlockTime;
|
||||
|
||||
const MAXIMUM_ENCODABLE_SECONDS: u32 = u16::MAX as u32 * 512;
|
||||
|
||||
|
@ -349,6 +362,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_time_chain_state() {
|
||||
use crate::BlockMtp;
|
||||
|
||||
let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200);
|
||||
let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
|
||||
|
||||
|
@ -361,49 +376,48 @@ mod tests {
|
|||
// Test case 1: Satisfaction (current_mtp >= utxo_mtp + required_seconds)
|
||||
// 10 intervals × 512 seconds = 5120 seconds
|
||||
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);
|
||||
let chain_state1 = BlockMtp::new(timestamps);
|
||||
let utxo_state1 = BlockMtp::new(utxo_timestamps);
|
||||
assert!(time_lock.is_satisfied_by(chain_state1, utxo_state1));
|
||||
|
||||
// Test case 2: Not satisfied (current_mtp < utxo_mtp + required_seconds)
|
||||
let chain_state2 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps2);
|
||||
let utxo_state2 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps2);
|
||||
let chain_state2 = BlockMtp::new(timestamps2);
|
||||
let utxo_state2 = BlockMtp::new(utxo_timestamps2);
|
||||
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 = 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);
|
||||
let chain_state3 = BlockMtp::new(timestamps3);
|
||||
let utxo_state3 = BlockMtp::new(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 = MtpInterval::MAX;
|
||||
let chain_state4 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps);
|
||||
let utxo_state4 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
|
||||
let chain_state4 = BlockMtp::new(timestamps);
|
||||
let utxo_state4 = BlockMtp::new(utxo_timestamps);
|
||||
assert!(!max_time_lock.is_satisfied_by(chain_state4, utxo_state4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_height_chain_state() {
|
||||
let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200);
|
||||
let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
|
||||
use crate::BlockHeight;
|
||||
|
||||
let height_lock = HeightInterval(10);
|
||||
|
||||
// Test case 1: Satisfaction (current_height >= utxo_height + required)
|
||||
let chain_state1 = MtpAndHeight::new(BlockHeight::from_u32(100), timestamps);
|
||||
let utxo_state1 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
|
||||
let chain_state1 = BlockHeight::from_u32(100);
|
||||
let utxo_state1 = BlockHeight::from_u32(80);
|
||||
assert!(height_lock.is_satisfied_by(chain_state1, utxo_state1));
|
||||
|
||||
// Test case 2: Not satisfied (current_height < utxo_height + required)
|
||||
let chain_state2 = MtpAndHeight::new(BlockHeight::from_u32(89), timestamps);
|
||||
let utxo_state2 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
|
||||
let chain_state2 = BlockHeight::from_u32(89);
|
||||
let utxo_state2 = BlockHeight::from_u32(80);
|
||||
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 = HeightInterval::MAX;
|
||||
let chain_state3 = MtpAndHeight::new(BlockHeight::from_u32(1000), timestamps);
|
||||
let utxo_state3 = MtpAndHeight::new(BlockHeight::from_u32(80), utxo_timestamps);
|
||||
let chain_state3 = BlockHeight::from_u32(1000);
|
||||
let utxo_state3 = BlockHeight::from_u32(80);
|
||||
assert!(!max_height_lock.is_satisfied_by(chain_state3, utxo_state3));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
//! Median Time Past (MTP) and height - used for working lock times.
|
||||
|
||||
use crate::{BlockHeight, BlockTime};
|
||||
|
||||
/// A structure containing both Median Time Past (MTP) and current
|
||||
/// absolute block height, used for validating relative locktimes.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct MtpAndHeight {
|
||||
/// The Median Time Past (median of the last 11 blocks' timestamps)
|
||||
mtp: BlockTime,
|
||||
/// The current block height,
|
||||
height: BlockHeight,
|
||||
}
|
||||
|
||||
impl MtpAndHeight {
|
||||
/// Constructs an [`MtpAndHeight`] by computing the median‐time‐past from the last 11 block timestamps
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `height` - The absolute height of the chain tip
|
||||
/// * `timestamps` - An array of timestamps from the most recent 11 blocks, where
|
||||
/// - `timestamps[0]` is the timestamp at height `height - 10`
|
||||
/// - `timestamps[1]` is the timestamp at height `height - 9`
|
||||
/// - …
|
||||
/// - `timestamps[10]` is the timestamp at height `height`
|
||||
pub fn new(height: BlockHeight, timestamps: [BlockTime; 11]) -> Self {
|
||||
let mut mtp_timestamps = timestamps;
|
||||
mtp_timestamps.sort_unstable();
|
||||
let mtp = mtp_timestamps[5];
|
||||
|
||||
MtpAndHeight { mtp, height }
|
||||
}
|
||||
|
||||
/// Returns the median-time-past component.
|
||||
pub fn to_mtp(self) -> BlockTime { self.mtp }
|
||||
|
||||
/// Returns the block-height component.
|
||||
pub fn to_height(self) -> BlockHeight { self.height }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn valid_chain_computes_mtp() {
|
||||
let height = BlockHeight::from_u32(100);
|
||||
let timestamps = [
|
||||
BlockTime::from_u32(10),
|
||||
BlockTime::from_u32(3),
|
||||
BlockTime::from_u32(5),
|
||||
BlockTime::from_u32(8),
|
||||
BlockTime::from_u32(1),
|
||||
BlockTime::from_u32(4),
|
||||
BlockTime::from_u32(6),
|
||||
BlockTime::from_u32(9),
|
||||
BlockTime::from_u32(2),
|
||||
BlockTime::from_u32(7),
|
||||
BlockTime::from_u32(0),
|
||||
];
|
||||
|
||||
let result = MtpAndHeight::new(height, timestamps);
|
||||
assert_eq!(result.height, height);
|
||||
assert_eq!(result.mtp.to_u32(), 5);
|
||||
}
|
||||
}
|
|
@ -14,8 +14,8 @@ use arbitrary::{Arbitrary, Unstructured};
|
|||
// These imports test "typical" usage by user code.
|
||||
use bitcoin_units::locktime::{absolute, relative}; // Typical usage is `absolute::Height`.
|
||||
use bitcoin_units::{
|
||||
amount, block, fee_rate, locktime, parse, weight, Amount, BlockHeight, BlockInterval,
|
||||
BlockTime, FeeRate, SignedAmount, Weight,
|
||||
amount, block, fee_rate, locktime, parse, weight, Amount, BlockHeight, BlockInterval, BlockMtp,
|
||||
BlockMtpInterval, BlockTime, FeeRate, SignedAmount, Weight,
|
||||
};
|
||||
|
||||
/// A struct that includes all public non-error enums.
|
||||
|
@ -43,6 +43,8 @@ struct Structs {
|
|||
j: relative::Time,
|
||||
k: Weight,
|
||||
l: BlockTime,
|
||||
m: BlockMtp,
|
||||
n: BlockMtpInterval,
|
||||
}
|
||||
|
||||
impl Structs {
|
||||
|
@ -60,6 +62,8 @@ impl Structs {
|
|||
j: relative::Time::MAX,
|
||||
k: Weight::MAX,
|
||||
l: BlockTime::from_u32(u32::MAX),
|
||||
m: BlockMtp::MAX,
|
||||
n: BlockMtpInterval::MAX,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +94,8 @@ struct CommonTraits {
|
|||
j: relative::Time,
|
||||
k: Weight,
|
||||
l: BlockTime,
|
||||
m: BlockMtp,
|
||||
n: BlockMtpInterval,
|
||||
}
|
||||
|
||||
/// A struct that includes all types that implement `Default`.
|
||||
|
@ -100,6 +106,7 @@ struct Default {
|
|||
c: BlockInterval,
|
||||
d: relative::Height,
|
||||
e: relative::Time,
|
||||
f: BlockMtpInterval,
|
||||
}
|
||||
|
||||
/// A struct that includes all public error types.
|
||||
|
@ -147,7 +154,8 @@ fn api_can_use_modules_from_crate_root() {
|
|||
#[test]
|
||||
fn api_can_use_types_from_crate_root() {
|
||||
use bitcoin_units::{
|
||||
Amount, BlockHeight, BlockInterval, BlockTime, FeeRate, SignedAmount, Weight,
|
||||
Amount, BlockHeight, BlockInterval, BlockMtp, BlockMtpInterval, BlockTime, FeeRate,
|
||||
SignedAmount, Weight,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -164,7 +172,7 @@ 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, TooBigForRelativeBlockHeightIntervalError,
|
||||
BlockHeight, BlockHeightInterval, TooBigForRelativeBlockHeightIntervalError,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -257,6 +265,7 @@ fn regression_default() {
|
|||
c: BlockInterval::ZERO,
|
||||
d: relative::Height::ZERO,
|
||||
e: relative::Time::ZERO,
|
||||
f: BlockMtpInterval::ZERO,
|
||||
};
|
||||
assert_eq!(got, want);
|
||||
}
|
||||
|
@ -298,6 +307,8 @@ impl<'a> Arbitrary<'a> for Structs {
|
|||
j: relative::Time::arbitrary(u)?,
|
||||
k: Weight::arbitrary(u)?,
|
||||
l: BlockTime::arbitrary(u)?,
|
||||
m: BlockMtp::arbitrary(u)?,
|
||||
n: BlockMtpInterval::arbitrary(u)?,
|
||||
};
|
||||
Ok(a)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue