units: pull u32 conversions for BlockHeight/BlockInterval into macro

There is a lot of duplicated code between BlockHeight and BlockInterval.
It obfuscates the differences between them: which timelock types they
can be converted to/from and what their arithmetic properties are.
This commit is contained in:
Andrew Poelstra 2025-05-05 21:46:32 +00:00
parent 7ca45e861b
commit a3228d4636
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 69 additions and 98 deletions

View File

@ -22,26 +22,23 @@ use serde::{Deserialize, Serialize};
use crate::locktime; use crate::locktime;
use crate::locktime::{absolute, relative}; use crate::locktime::{absolute, relative};
/// The block height, zero denotes the genesis block. macro_rules! impl_u32_wrapper {
/// {
/// This type is not meant for constructing height based timelocks, this is a general purpose block $(#[$($type_attrs:tt)*])*
/// height abstraction. For locktimes please see [`locktime::absolute::Height`]. $type_vis:vis struct $newtype:ident($inner_vis:vis u32);
/// } => {
/// This is a thin wrapper around a `u32` that may take on all values of a `u32`. $(#[$($type_attrs)*])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] $type_vis struct $newtype($inner_vis u32);
#[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 { impl $newtype {
/// Block height 0, the genesis block. /// 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. /// The minimum block height (0), the genesis block.
pub const MIN: Self = Self::ZERO; pub const MIN: Self = Self::ZERO;
/// The maximum block height. /// 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`. /// Constructs a new block height from a `u32`.
pub const fn from_u32(inner: u32) -> Self { Self(inner) } pub const fn from_u32(inner: u32) -> Self { Self(inner) }
@ -50,18 +47,46 @@ impl BlockHeight {
pub const fn to_u32(self) -> u32 { self.0 } 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) } 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) } fn from(inner: u32) -> Self { Self::from_u32(inner) }
} }
impl From<BlockHeight> for u32 { impl From<$newtype> for u32 {
fn from(height: BlockHeight) -> Self { height.to_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_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 From<absolute::Height> for BlockHeight { impl From<absolute::Height> for BlockHeight {
@ -86,47 +111,17 @@ impl TryFrom<BlockHeight> for absolute::Height {
} }
} }
/// The block interval. impl_u32_wrapper! {
/// An unsigned block interval.
/// ///
/// Block interval is an integer type denoting the number of blocks that has passed since some point /// Block interval is an integer type representing a difference between the heights of two blocks.
/// 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 /// 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`]. /// purpose block interval abstraction. For locktimes please see [`locktime::relative::Height`].
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
// Public to try and make it really clear that there are no invariants. // Public to try and make it really clear that there are no invariants.
pub struct BlockInterval(pub u32); 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 fmt::Display for BlockInterval {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
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 { impl From<relative::HeightInterval> for BlockInterval {
@ -244,30 +239,6 @@ impl<'a> core::iter::Sum<&'a BlockInterval> for BlockInterval {
} }
} }
#[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)?)),
}
}
}
#[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)?)),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;