units: add global `BlockMtp` type
For our relative locktime API, we are going to want to take differences of arbitrary MTPs in order to check whether they meet some relative timelock threshold. However, the `locktime::absolute::Mtp` type can only represent MTPs that exceed 500 million. In practice this is a non-issue; by consensus MTPs must be monotonic and every real chain (even test chains) have initial real MTPs well above 500 million, which as a UNIX timestamp corresponds to November 5, 1985. But in theory this is a big problem: if we were to treat relative MTPs as "differences of absolute-timelock MTPs" then we will be unable to construct relative timelocks on chains with weird timestamps (and on legitimate chains, we'd have .unwrap()s everywhere that would be hard to justify). But we need to treat them as a "difference of MTPs" in *some* sense, because otherwise they'd be very hard to construct.
This commit is contained in:
parent
a3228d4636
commit
4e3af5162f
|
@ -134,7 +134,7 @@ pub use primitives::{
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use units::{
|
pub use units::{
|
||||||
amount::{Amount, Denomination, SignedAmount},
|
amount::{Amount, Denomination, SignedAmount},
|
||||||
block::{BlockHeight, BlockInterval},
|
block::{BlockHeight, BlockInterval, BlockMtp},
|
||||||
fee_rate::FeeRate,
|
fee_rate::FeeRate,
|
||||||
time::{self, BlockTime},
|
time::{self, BlockTime},
|
||||||
weight::Weight,
|
weight::Weight,
|
||||||
|
|
|
@ -50,7 +50,7 @@ pub mod witness;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use units::{
|
pub use units::{
|
||||||
amount::{self, Amount, SignedAmount},
|
amount::{self, Amount, SignedAmount},
|
||||||
block::{BlockHeight, BlockInterval},
|
block::{BlockHeight, BlockInterval, BlockMtp},
|
||||||
fee_rate::{self, FeeRate},
|
fee_rate::{self, FeeRate},
|
||||||
time::{self, BlockTime},
|
time::{self, BlockTime},
|
||||||
weight::{self, Weight},
|
weight::{self, Weight},
|
||||||
|
|
|
@ -93,8 +93,7 @@ impl From<absolute::Height> for BlockHeight {
|
||||||
/// Converts a [`locktime::absolute::Height`] to a [`BlockHeight`].
|
/// Converts a [`locktime::absolute::Height`] to a [`BlockHeight`].
|
||||||
///
|
///
|
||||||
/// An absolute locktime block height has a maximum value of [`absolute::LOCK_TIME_THRESHOLD`]
|
/// An absolute locktime block height has a maximum value of [`absolute::LOCK_TIME_THRESHOLD`]
|
||||||
/// (500,000,000) where as a [`BlockHeight`] is a thin wrapper around a `u32`, the two types are
|
/// minus one, while [`BlockHeight`] may take the full range of `u32`.
|
||||||
/// not interchangeable.
|
|
||||||
fn from(h: absolute::Height) -> Self { Self::from_u32(h.to_u32()) }
|
fn from(h: absolute::Height) -> Self { Self::from_u32(h.to_u32()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +103,7 @@ impl TryFrom<BlockHeight> for absolute::Height {
|
||||||
/// Converts a [`BlockHeight`] to a [`locktime::absolute::Height`].
|
/// Converts a [`BlockHeight`] to a [`locktime::absolute::Height`].
|
||||||
///
|
///
|
||||||
/// An absolute locktime block height has a maximum value of [`absolute::LOCK_TIME_THRESHOLD`]
|
/// An absolute locktime block height has a maximum value of [`absolute::LOCK_TIME_THRESHOLD`]
|
||||||
/// (500,000,000) where as a [`BlockHeight`] is a thin wrapper around a `u32`, the two types are
|
/// minus one, while [`BlockHeight`] may take the full range of `u32`.
|
||||||
/// not interchangeable.
|
|
||||||
fn try_from(h: BlockHeight) -> Result<Self, Self::Error> {
|
fn try_from(h: BlockHeight) -> Result<Self, Self::Error> {
|
||||||
absolute::Height::from_u32(h.to_u32())
|
absolute::Height::from_u32(h.to_u32())
|
||||||
}
|
}
|
||||||
|
@ -149,6 +147,49 @@ 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]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Error returned when the block interval is too big to be used as a relative lock time.
|
/// Error returned when the block interval is too big to be used as a relative lock time.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct TooBigForRelativeBlockHeightIntervalError(u32);
|
pub struct TooBigForRelativeBlockHeightIntervalError(u32);
|
||||||
|
|
|
@ -57,7 +57,7 @@ pub mod weight;
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub use self::{
|
pub use self::{
|
||||||
amount::{Amount, SignedAmount},
|
amount::{Amount, SignedAmount},
|
||||||
block::{BlockHeight, BlockInterval},
|
block::{BlockHeight, BlockInterval, BlockMtp},
|
||||||
fee_rate::FeeRate,
|
fee_rate::FeeRate,
|
||||||
result::{NumOpError, NumOpResult, MathOp},
|
result::{NumOpError, NumOpResult, MathOp},
|
||||||
time::BlockTime,
|
time::BlockTime,
|
||||||
|
|
|
@ -14,7 +14,7 @@ use arbitrary::{Arbitrary, Unstructured};
|
||||||
// These imports test "typical" usage by user code.
|
// These imports test "typical" usage by user code.
|
||||||
use bitcoin_units::locktime::{absolute, relative}; // Typical usage is `absolute::Height`.
|
use bitcoin_units::locktime::{absolute, relative}; // Typical usage is `absolute::Height`.
|
||||||
use bitcoin_units::{
|
use bitcoin_units::{
|
||||||
amount, block, fee_rate, locktime, parse, weight, Amount, BlockHeight, BlockInterval,
|
amount, block, fee_rate, locktime, parse, weight, Amount, BlockHeight, BlockInterval, BlockMtp,
|
||||||
BlockTime, FeeRate, SignedAmount, Weight,
|
BlockTime, FeeRate, SignedAmount, Weight,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ struct Structs {
|
||||||
j: relative::Time,
|
j: relative::Time,
|
||||||
k: Weight,
|
k: Weight,
|
||||||
l: BlockTime,
|
l: BlockTime,
|
||||||
|
m: BlockMtp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Structs {
|
impl Structs {
|
||||||
|
@ -60,6 +61,7 @@ impl Structs {
|
||||||
j: relative::Time::MAX,
|
j: relative::Time::MAX,
|
||||||
k: Weight::MAX,
|
k: Weight::MAX,
|
||||||
l: BlockTime::from_u32(u32::MAX),
|
l: BlockTime::from_u32(u32::MAX),
|
||||||
|
m: BlockMtp::MAX,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,6 +92,7 @@ struct CommonTraits {
|
||||||
j: relative::Time,
|
j: relative::Time,
|
||||||
k: Weight,
|
k: Weight,
|
||||||
l: BlockTime,
|
l: BlockTime,
|
||||||
|
m: BlockMtp,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A struct that includes all types that implement `Default`.
|
/// A struct that includes all types that implement `Default`.
|
||||||
|
@ -147,7 +150,7 @@ fn api_can_use_modules_from_crate_root() {
|
||||||
#[test]
|
#[test]
|
||||||
fn api_can_use_types_from_crate_root() {
|
fn api_can_use_types_from_crate_root() {
|
||||||
use bitcoin_units::{
|
use bitcoin_units::{
|
||||||
Amount, BlockHeight, BlockInterval, BlockTime, FeeRate, SignedAmount, Weight,
|
Amount, BlockHeight, BlockInterval, BlockMtp, BlockTime, FeeRate, SignedAmount, Weight,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +301,7 @@ impl<'a> Arbitrary<'a> for Structs {
|
||||||
j: relative::Time::arbitrary(u)?,
|
j: relative::Time::arbitrary(u)?,
|
||||||
k: Weight::arbitrary(u)?,
|
k: Weight::arbitrary(u)?,
|
||||||
l: BlockTime::arbitrary(u)?,
|
l: BlockTime::arbitrary(u)?,
|
||||||
|
m: BlockMtp::arbitrary(u)?,
|
||||||
};
|
};
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue