#[cfg(feature = "encoding")]
use core::convert::Infallible;
use core::{fmt, ops};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
#[cfg(feature = "encoding")]
use internals::write_err;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[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 fmt::Display for $newtype {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
crate::parse_int::impl_parse_str_from_int_infallible!($newtype, u32, from);
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 = "serde")]
impl Serialize for $newtype {
#[inline]
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
u32::serialize(&self.to_u32(), s)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for $newtype {
#[inline]
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::from_u32(u32::deserialize(d)?))
}
}
#[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! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlockHeight(u32);
}
impl BlockHeight {
pub const ZERO: Self = Self(0);
pub const MIN: Self = Self::ZERO;
pub const MAX: Self = Self(u32::MAX);
pub const fn from_u32(inner: u32) -> Self { Self(inner) }
pub const fn to_u32(self) -> u32 { self.0 }
#[must_use]
pub fn checked_sub(self, other: Self) -> Option<BlockHeightInterval> {
self.to_u32().checked_sub(other.to_u32()).map(BlockHeightInterval)
}
#[must_use]
pub fn checked_add(self, other: BlockHeightInterval) -> Option<Self> {
self.to_u32().checked_add(other.to_u32()).map(Self)
}
#[inline]
#[must_use]
pub const fn saturating_add(self, rhs: BlockHeightInterval) -> Self {
Self::from_u32(self.to_u32().saturating_add(rhs.to_u32()))
}
#[inline]
#[must_use]
pub const fn saturating_sub(self, rhs: BlockHeightInterval) -> Self {
Self::from_u32(self.to_u32().saturating_sub(rhs.to_u32()))
}
}
crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(BlockHeight);
impl From<absolute::Height> for BlockHeight {
fn from(h: absolute::Height) -> Self { Self::from_u32(h.to_u32()) }
}
impl TryFrom<BlockHeight> for absolute::Height {
type Error = absolute::ConversionError;
fn try_from(h: BlockHeight) -> Result<Self, Self::Error> { Self::from_u32(h.to_u32()) }
}
#[cfg(feature = "encoding")]
encoding::encoder_newtype_exact! {
pub struct BlockHeightEncoder<'e>(encoding::ArrayEncoder<4>);
}
#[cfg(feature = "encoding")]
impl encoding::Encodable for BlockHeight {
type Encoder<'e> = BlockHeightEncoder<'e>;
fn encoder(&self) -> Self::Encoder<'_> {
BlockHeightEncoder::new(encoding::ArrayEncoder::without_length_prefix(
self.to_u32().to_le_bytes(),
))
}
}
#[cfg(feature = "encoding")]
pub struct BlockHeightDecoder(encoding::ArrayDecoder<4>);
#[cfg(feature = "encoding")]
impl Default for BlockHeightDecoder {
fn default() -> Self { Self::new() }
}
#[cfg(feature = "encoding")]
impl BlockHeightDecoder {
pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
}
#[cfg(feature = "encoding")]
impl encoding::Decoder for BlockHeightDecoder {
type Output = BlockHeight;
type Error = BlockHeightDecoderError;
#[inline]
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
self.0.push_bytes(bytes).map_err(BlockHeightDecoderError)
}
#[inline]
fn end(self) -> Result<Self::Output, Self::Error> {
let n = u32::from_le_bytes(self.0.end().map_err(BlockHeightDecoderError)?);
Ok(BlockHeight::from_u32(n))
}
#[inline]
fn read_limit(&self) -> usize { self.0.read_limit() }
}
#[cfg(feature = "encoding")]
impl encoding::Decodable for BlockHeight {
type Decoder = BlockHeightDecoder;
fn decoder() -> Self::Decoder { BlockHeightDecoder(encoding::ArrayDecoder::<4>::new()) }
}
#[cfg(feature = "encoding")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockHeightDecoderError(encoding::UnexpectedEofError);
#[cfg(feature = "encoding")]
impl From<Infallible> for BlockHeightDecoderError {
fn from(never: Infallible) -> Self { match never {} }
}
#[cfg(feature = "encoding")]
impl fmt::Display for BlockHeightDecoderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write_err!(f, "block height decoder error"; self.0)
}
}
#[cfg(all(feature = "std", feature = "encoding"))]
impl std::error::Error for BlockHeightDecoderError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
}
impl_u32_wrapper! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlockHeightInterval(u32);
}
impl BlockHeightInterval {
pub const ZERO: Self = Self(0);
pub const MIN: Self = Self::ZERO;
pub const MAX: Self = Self(u32::MAX);
pub const fn from_u32(inner: u32) -> Self { Self(inner) }
pub const fn to_u32(self) -> u32 { self.0 }
#[must_use]
pub fn checked_sub(self, other: Self) -> Option<Self> {
self.to_u32().checked_sub(other.to_u32()).map(Self)
}
#[must_use]
pub fn checked_add(self, other: Self) -> Option<Self> {
self.to_u32().checked_add(other.to_u32()).map(Self)
}
}
crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(BlockHeightInterval);
impl From<relative::NumberOfBlocks> for BlockHeightInterval {
fn from(h: relative::NumberOfBlocks) -> Self { Self::from_u32(h.to_height().into()) }
}
impl TryFrom<BlockHeightInterval> for relative::NumberOfBlocks {
type Error = TooBigForRelativeHeightError;
fn try_from(h: BlockHeightInterval) -> Result<Self, Self::Error> {
u16::try_from(h.to_u32())
.map(Self::from)
.map_err(|_| TooBigForRelativeHeightError(h.into()))
}
}
impl_u32_wrapper! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlockMtp(u32);
}
impl BlockMtp {
pub const ZERO: Self = Self(0);
pub const MIN: Self = Self::ZERO;
pub const MAX: Self = Self(u32::MAX);
pub const fn from_u32(inner: u32) -> Self { Self(inner) }
pub const fn to_u32(self) -> u32 { self.0 }
pub fn new(mut timestamps: [crate::BlockTime; 11]) -> Self {
timestamps.sort_unstable();
Self::from_u32(u32::from(timestamps[5]))
}
#[must_use]
pub fn checked_sub(self, other: Self) -> Option<BlockMtpInterval> {
self.to_u32().checked_sub(other.to_u32()).map(BlockMtpInterval)
}
#[must_use]
pub fn checked_add(self, other: BlockMtpInterval) -> Option<Self> {
self.to_u32().checked_add(other.to_u32()).map(Self)
}
}
crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(BlockMtp);
impl From<absolute::MedianTimePast> for BlockMtp {
fn from(h: absolute::MedianTimePast) -> Self { Self::from_u32(h.to_u32()) }
}
impl TryFrom<BlockMtp> for absolute::MedianTimePast {
type Error = absolute::ConversionError;
fn try_from(h: BlockMtp) -> Result<Self, Self::Error> { Self::from_u32(h.to_u32()) }
}
impl_u32_wrapper! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlockMtpInterval(u32);
}
impl BlockMtpInterval {
pub const ZERO: Self = Self(0);
pub const MIN: Self = Self::ZERO;
pub const MAX: Self = Self(u32::MAX);
pub const fn from_u32(inner: u32) -> Self { Self(inner) }
pub const fn to_u32(self) -> u32 { self.0 }
#[inline]
pub const fn to_relative_mtp_interval_floor(
self,
) -> Result<relative::NumberOf512Seconds, relative::TimeOverflowError> {
relative::NumberOf512Seconds::from_seconds_floor(self.to_u32())
}
#[inline]
pub const fn to_relative_mtp_interval_ceil(
self,
) -> Result<relative::NumberOf512Seconds, relative::TimeOverflowError> {
relative::NumberOf512Seconds::from_seconds_ceil(self.to_u32())
}
#[must_use]
pub fn checked_sub(self, other: Self) -> Option<Self> {
self.to_u32().checked_sub(other.to_u32()).map(Self)
}
#[must_use]
pub fn checked_add(self, other: Self) -> Option<Self> {
self.to_u32().checked_add(other.to_u32()).map(Self)
}
}
crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(BlockMtpInterval);
impl From<relative::NumberOf512Seconds> for BlockMtpInterval {
fn from(h: relative::NumberOf512Seconds) -> Self { Self::from_u32(h.to_seconds()) }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TooBigForRelativeHeightError(u32);
impl fmt::Display for TooBigForRelativeHeightError {
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::NumberOfBlocks::MAX
)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TooBigForRelativeHeightError {}
crate::internal_macros::impl_op_for_references! {
impl ops::Sub<BlockHeight> for BlockHeight {
type Output = BlockHeightInterval;
fn sub(self, rhs: BlockHeight) -> Self::Output {
let interval = self.to_u32() - rhs.to_u32();
BlockHeightInterval::from_u32(interval)
}
}
impl ops::Add<BlockHeightInterval> for BlockHeight {
type Output = BlockHeight;
fn add(self, rhs: BlockHeightInterval) -> Self::Output {
let height = self.to_u32() + rhs.to_u32();
BlockHeight::from_u32(height)
}
}
impl ops::Sub<BlockHeightInterval> for BlockHeight {
type Output = BlockHeight;
fn sub(self, rhs: BlockHeightInterval) -> Self::Output {
let height = self.to_u32() - rhs.to_u32();
BlockHeight::from_u32(height)
}
}
impl ops::Add<BlockHeightInterval> for BlockHeightInterval {
type Output = BlockHeightInterval;
fn add(self, rhs: BlockHeightInterval) -> Self::Output {
let height = self.to_u32() + rhs.to_u32();
BlockHeightInterval::from_u32(height)
}
}
impl ops::Sub<BlockHeightInterval> for BlockHeightInterval {
type Output = BlockHeightInterval;
fn sub(self, rhs: BlockHeightInterval) -> Self::Output {
let height = self.to_u32() - rhs.to_u32();
BlockHeightInterval::from_u32(height)
}
}
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)
}
}
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)
}
}
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)
}
}
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)
}
}
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!(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<I: Iterator<Item = Self>>(iter: I) -> Self {
let sum = iter.map(Self::to_u32).sum();
Self::from_u32(sum)
}
}
impl<'a> core::iter::Sum<&'a Self> for BlockHeightInterval {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Self>,
{
let sum = iter.map(|interval| interval.to_u32()).sum();
Self::from_u32(sum)
}
}
impl core::iter::Sum for BlockMtpInterval {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let sum = iter.map(Self::to_u32).sum();
Self::from_u32(sum)
}
}
impl<'a> core::iter::Sum<&'a Self> for BlockMtpInterval {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Self>,
{
let sum = iter.map(|interval| interval.to_u32()).sum();
Self::from_u32(sum)
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "encoding")]
use encoding::{Decoder as _, UnexpectedEofError};
use super::*;
use crate::relative::{NumberOf512Seconds, TimeOverflowError};
#[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::NumberOfBlocks::from(10u16).into();
assert_eq!(interval_from_height.to_u32(), 10u32);
let invalid_height_greater =
relative::NumberOfBlocks::try_from(BlockHeightInterval(u32::from(u16::MAX) + 1));
assert!(invalid_height_greater.is_err());
let valid_height =
relative::NumberOfBlocks::try_from(BlockHeightInterval(u32::from(u16::MAX)));
assert!(valid_height.is_ok());
}
#[test]
fn all_available_ops() {
assert!(BlockHeight(10) - BlockHeight(7) == BlockHeightInterval(3));
assert!(BlockHeight(100) + BlockHeightInterval(1) == BlockHeight(101));
assert!(BlockHeight(100) - BlockHeightInterval(1) == BlockHeight(99));
assert!(BlockHeightInterval(1) + BlockHeightInterval(2) == BlockHeightInterval(3));
assert!(BlockHeightInterval(10) - BlockHeightInterval(7) == BlockHeightInterval(3));
assert!(
[BlockHeightInterval(1), BlockHeightInterval(2), BlockHeightInterval(3)]
.iter()
.sum::<BlockHeightInterval>()
== BlockHeightInterval(6)
);
assert!(
[BlockHeightInterval(4), BlockHeightInterval(5), BlockHeightInterval(6)]
.into_iter()
.sum::<BlockHeightInterval>()
== BlockHeightInterval(15)
);
assert!(
[BlockMtpInterval(1), BlockMtpInterval(2), BlockMtpInterval(3)]
.iter()
.sum::<BlockMtpInterval>()
== BlockMtpInterval(6)
);
assert!(
[BlockMtpInterval(4), BlockMtpInterval(5), BlockMtpInterval(6)]
.into_iter()
.sum::<BlockMtpInterval>()
== BlockMtpInterval(15)
);
let mut int = BlockHeightInterval(1);
int += BlockHeightInterval(2);
assert_eq!(int, BlockHeightInterval(3));
let mut int = BlockHeightInterval(10);
int -= BlockHeightInterval(7);
assert_eq!(int, BlockHeightInterval(3));
}
#[test]
fn block_height_checked() {
let a = BlockHeight(10);
let b = BlockHeight(5);
assert_eq!(a.checked_sub(b), Some(BlockHeightInterval(5)));
assert_eq!(a.checked_add(BlockHeightInterval(5)), Some(BlockHeight(15)));
assert_eq!(a.checked_sub(BlockHeight(11)), None);
assert_eq!(a.checked_add(BlockHeightInterval(u32::MAX - 5)), None);
}
#[test]
fn block_height_interval_checked() {
let a = BlockHeightInterval(10);
let b = BlockHeightInterval(5);
assert_eq!(a.checked_sub(b), Some(BlockHeightInterval(5)));
assert_eq!(a.checked_add(b), Some(BlockHeightInterval(15)));
assert_eq!(a.checked_sub(BlockHeightInterval(11)), None);
assert_eq!(a.checked_add(BlockHeightInterval(u32::MAX - 5)), None);
}
#[test]
fn block_mtp_interval_checked() {
let a = BlockMtpInterval(10);
let b = BlockMtpInterval(5);
assert_eq!(a.checked_sub(b), Some(BlockMtpInterval(5)));
assert_eq!(a.checked_add(b), Some(BlockMtpInterval(15)));
assert_eq!(a.checked_sub(BlockMtpInterval(11)), None);
assert_eq!(a.checked_add(BlockMtpInterval(u32::MAX - 5)), None);
}
#[test]
fn block_mtp_checked() {
let a = BlockMtp(10);
let b = BlockMtp(5);
assert_eq!(a.checked_sub(b), Some(BlockMtpInterval(5)));
assert_eq!(a.checked_add(BlockMtpInterval(5)), Some(BlockMtp(15)));
assert_eq!(a.checked_sub(BlockMtp(11)), None);
assert_eq!(a.checked_add(BlockMtpInterval(u32::MAX - 5)), None);
}
#[test]
fn block_mtp_interval_from_number_of_512seconds() {
let n = NumberOf512Seconds::from_seconds_floor(0).unwrap();
let interval = BlockMtpInterval::from(n);
assert_eq!(interval, BlockMtpInterval(0));
let n = NumberOf512Seconds::from_seconds_floor(1024).unwrap();
let interval = BlockMtpInterval::from(n);
assert_eq!(interval, BlockMtpInterval(1024));
}
#[test]
fn block_mtp_interval_to_relative_mtp_floor() {
let time = NumberOf512Seconds::from_512_second_intervals(0);
let interval = BlockMtpInterval::from_u32(0);
assert_eq!(interval.to_relative_mtp_interval_floor().unwrap(), time);
let time = NumberOf512Seconds::from_512_second_intervals(1);
let interval = BlockMtpInterval::from_u32(1023);
assert_eq!(interval.to_relative_mtp_interval_floor().unwrap(), time); assert_ne!(interval.to_relative_mtp_interval_ceil().unwrap(), time);
let max_time = NumberOf512Seconds::from_512_second_intervals(u16::MAX);
let max_seconds = u32::from(u16::MAX) * 512 + 511;
let interval = BlockMtpInterval::from_u32(max_seconds);
assert_eq!(interval.to_relative_mtp_interval_floor().unwrap(), max_time);
let interval = BlockMtpInterval::from_u32(max_seconds + 1);
assert_eq!(
interval.to_relative_mtp_interval_floor().unwrap_err(),
TimeOverflowError { seconds: max_seconds + 1 }
);
}
#[test]
fn block_mtp_interval_to_relative_mtp_ceil() {
let time = NumberOf512Seconds::from_512_second_intervals(0);
let interval = BlockMtpInterval::from_u32(0);
assert_eq!(interval.to_relative_mtp_interval_ceil().unwrap(), time);
let time = NumberOf512Seconds::from_512_second_intervals(2);
let interval = BlockMtpInterval::from_u32(1023);
assert_eq!(interval.to_relative_mtp_interval_ceil().unwrap(), time); assert_ne!(interval.to_relative_mtp_interval_floor().unwrap(), time);
let max_time = NumberOf512Seconds::from_512_second_intervals(u16::MAX);
let max_seconds = u32::from(u16::MAX) * 512;
let interval = BlockMtpInterval::from_u32(max_seconds);
assert_eq!(interval.to_relative_mtp_interval_ceil().unwrap(), max_time);
let interval = BlockMtpInterval::from_u32(max_seconds + 1);
assert_eq!(
interval.to_relative_mtp_interval_ceil().unwrap_err(),
TimeOverflowError { seconds: max_seconds + 1 }
);
}
#[test]
#[cfg(feature = "encoding")]
fn block_height_decoding_error() {
let bytes = [0xff, 0xff, 0xff];
let mut decoder = BlockHeightDecoder::default();
assert!(decoder.push_bytes(&mut bytes.as_slice()).unwrap());
let error = decoder.end().unwrap_err();
assert!(matches!(error, BlockHeightDecoderError(UnexpectedEofError { .. })));
}
macro_rules! serde_roundtrip_test {
{ $test_name:tt, $typ:ident } => {
#[test]
#[cfg(feature = "serde")]
fn $test_name() {
let t = $typ(1_654_321);
let json = serde_json::to_string(&t).unwrap();
assert_eq!(json, "1654321");
let roundtrip = serde_json::from_str::<$typ>(&json).unwrap();
assert_eq!(t, roundtrip);
}
}
}
serde_roundtrip_test!(block_height_serde_round_trip, BlockHeight);
serde_roundtrip_test!(block_height_interval_serde_round_trip, BlockHeightInterval);
serde_roundtrip_test!(block_mtp_serde_round_trip, BlockMtp);
serde_roundtrip_test!(block_mtp_interval_serde_round_trip, BlockMtpInterval);
#[test]
fn block_height_saturating_add() {
assert_eq!(BlockHeight(100).saturating_add(BlockHeightInterval(50)), BlockHeight(150),);
assert_eq!(BlockHeight::ZERO.saturating_add(BlockHeightInterval(1)), BlockHeight(1),);
assert_eq!(BlockHeight::MAX.saturating_add(BlockHeightInterval(1)), BlockHeight::MAX,);
assert_eq!(BlockHeight::MAX.saturating_add(BlockHeightInterval(100)), BlockHeight::MAX,);
assert_eq!(
BlockHeight(u32::MAX - 10).saturating_add(BlockHeightInterval(20)),
BlockHeight::MAX,
);
assert_eq!(BlockHeight(500).saturating_add(BlockHeightInterval::ZERO), BlockHeight(500),);
}
#[test]
fn block_height_saturating_sub() {
assert_eq!(BlockHeight(100).saturating_sub(BlockHeightInterval(50)), BlockHeight(50),);
assert_eq!(BlockHeight(100).saturating_sub(BlockHeightInterval(100)), BlockHeight(0),);
assert_eq!(BlockHeight::MIN.saturating_sub(BlockHeightInterval(1)), BlockHeight::MIN,);
assert_eq!(BlockHeight::ZERO.saturating_sub(BlockHeightInterval(100)), BlockHeight::ZERO,);
assert_eq!(BlockHeight(10).saturating_sub(BlockHeightInterval(20)), BlockHeight::ZERO,);
assert_eq!(BlockHeight(500).saturating_sub(BlockHeightInterval::ZERO), BlockHeight(500),);
}
}