// SPDX-License-Identifier: CC0-1.0 //! Block height and interval types. //! //! These types are thin wrappers around `u32`, no invariants implemented or implied. //! //! These are general types for abstracting over block heights, they are not designed to use with //! lock times. If you are creating lock times you should be using the //! [`locktime::absolute::Height`] and [`locktime::relative::Height`] types. //! //! The difference between these types and the locktime types is that these types are thin wrappers //! whereas the locktime types contain more complex locktime specific abstractions. use core::{fmt, ops}; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(doc)] use crate::locktime; use crate::locktime::{absolute, relative}; 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 $newtype { /// Block height 0, the genesis block. 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 = 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 $newtype { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } crate::impl_parse_str_from_int_infallible!($newtype, u32, from); impl From 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 { 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 BlockHeight { /// Attempt to subtract two [`BlockHeight`]s, returning `None` in case of overflow. pub fn checked_sub(self, other: Self) -> Option { 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.0.checked_add(other.0).map(Self) } } impl From for BlockHeight { /// Converts a [`locktime::absolute::Height`] to a [`BlockHeight`]. /// /// An absolute locktime block height has a maximum value of [`absolute::LOCK_TIME_THRESHOLD`] /// minus one, while [`BlockHeight`] may take the full range of `u32`. fn from(h: absolute::Height) -> Self { Self::from_u32(h.to_u32()) } } impl TryFrom for absolute::Height { type Error = absolute::ConversionError; /// Converts a [`BlockHeight`] to a [`locktime::absolute::Height`]. /// /// An absolute locktime block height has a maximum value of [`absolute::LOCK_TIME_THRESHOLD`] /// minus one, while [`BlockHeight`] may take the full range of `u32`. fn try_from(h: BlockHeight) -> Result { absolute::Height::from_u32(h.to_u32()) } } 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 BlockHeightInterval { /// Attempt to subtract two [`BlockHeightInterval`]s, returning `None` in case of overflow. pub fn checked_sub(self, other: Self) -> Option { 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.0.checked_add(other.0).map(Self) } } impl From 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 /// [`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 for relative::HeightInterval { type Error = TooBigForRelativeBlockHeightIntervalError; /// Converts a [`BlockHeightInterval`] to a [`locktime::relative::HeightInterval`]. /// /// A relative locktime block height has a maximum value of `u16::MAX` where as a /// [`BlockHeightInterval`] is a thin wrapper around a `u32`, the two types are not interchangeable. fn try_from(h: BlockHeightInterval) -> Result { let h = h.to_u32(); if h > u32::from(u16::MAX) { return Err(TooBigForRelativeBlockHeightIntervalError(h)); } Ok(relative::HeightInterval::from(h as u16)) // Cast ok, value checked above. } } 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 { 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.0.checked_add(other.0).map(Self) } } impl From 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 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 { 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::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::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.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.0.checked_add(other.0).map(Self) } } impl From 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); 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::HeightInterval::MAX ) } } #[cfg(feature = "std")] impl std::error::Error for TooBigForRelativeBlockHeightIntervalError {} crate::internal_macros::impl_op_for_references! { // height - height = interval impl ops::Sub for BlockHeight { type Output = BlockHeightInterval; fn sub(self, rhs: BlockHeight) -> Self::Output { let interval = self.to_u32() - rhs.to_u32(); BlockHeightInterval::from_u32(interval) } } // height + interval = height impl ops::Add for BlockHeight { type Output = BlockHeight; 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 for BlockHeight { type Output = BlockHeight; 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 for BlockHeightInterval { type Output = BlockHeightInterval; fn add(self, rhs: BlockHeightInterval) -> Self::Output { let height = self.to_u32() + rhs.to_u32(); BlockHeightInterval::from_u32(height) } } // interval - interval = interval impl ops::Sub for BlockHeightInterval { type Output = BlockHeightInterval; fn sub(self, rhs: BlockHeightInterval) -> Self::Output { let height = self.to_u32() - rhs.to_u32(); BlockHeightInterval::from_u32(height) } } // height - height = interval impl ops::Sub 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 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 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 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 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!(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 BlockHeightInterval { fn sum>(iter: I) -> Self { let sum = iter.map(|interval| interval.0).sum(); BlockHeightInterval::from_u32(sum) } } impl<'a> core::iter::Sum<&'a BlockHeightInterval> for BlockHeightInterval { fn sum(iter: I) -> Self where I: Iterator, { let sum = iter.map(|interval| interval.0).sum(); BlockHeightInterval::from_u32(sum) } } impl core::iter::Sum for BlockMtpInterval { fn sum>(iter: I) -> Self { let sum = iter.map(|interval| interval.0).sum(); BlockMtpInterval::from_u32(sum) } } impl<'a> core::iter::Sum<&'a BlockMtpInterval> for BlockMtpInterval { fn sum(iter: I) -> Self where I: Iterator, { let sum = iter.map(|interval| interval.0).sum(); BlockMtpInterval::from_u32(sum) } } #[cfg(test)] mod tests { use super::*; #[test] fn sanity_check() { let height: u32 = BlockHeight(100).into(); assert_eq!(height, 100); let interval: u32 = BlockHeightInterval(100).into(); assert_eq!(interval, 100); 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(BlockHeightInterval(u32::from(u16::MAX) + 1)); assert!(invalid_height_greater.is_err()); let valid_height = relative::HeightInterval::try_from(BlockHeightInterval(u32::from(u16::MAX))); assert!(valid_height.is_ok()); } // These tests are supposed to comprise an exhaustive list of available operations. #[test] fn all_available_ops() { // height - height = interval assert!(BlockHeight(10) - BlockHeight(7) == BlockHeightInterval(3)); // height + interval = height assert!(BlockHeight(100) + BlockHeightInterval(1) == BlockHeight(101)); // height - interval == height assert!(BlockHeight(100) - BlockHeightInterval(1) == BlockHeight(99)); // interval + interval = interval assert!(BlockHeightInterval(1) + BlockHeightInterval(2) == BlockHeightInterval(3)); // interval - interval = interval assert!(BlockHeightInterval(10) - BlockHeightInterval(7) == BlockHeightInterval(3)); assert!( [BlockHeightInterval(1), BlockHeightInterval(2), BlockHeightInterval(3)] .iter() .sum::() == BlockHeightInterval(6) ); assert!( [BlockHeightInterval(4), BlockHeightInterval(5), BlockHeightInterval(6)] .into_iter() .sum::() == BlockHeightInterval(15) ); // interval += interval let mut int = BlockHeightInterval(1); int += BlockHeightInterval(2); assert_eq!(int, BlockHeightInterval(3)); // interval -= interval let mut int = BlockHeightInterval(10); int -= BlockHeightInterval(7); assert_eq!(int, BlockHeightInterval(3)); } }