stratus/eth/primitives/
block_number.rs

1use std::ops::Add;
2use std::ops::AddAssign;
3use std::str::FromStr;
4
5use alloy_primitives::U64;
6use alloy_primitives::U256;
7use alloy_primitives::keccak256;
8use anyhow::anyhow;
9use display_json::DebugAsJson;
10#[cfg(test)]
11use fake::Dummy;
12#[cfg(test)]
13use fake::Faker;
14
15use crate::eth::primitives::Hash;
16
17#[derive(DebugAsJson, derive_more::Display, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
18#[serde(transparent)]
19pub struct BlockNumber(pub U64);
20
21impl BlockNumber {
22    pub const ZERO: BlockNumber = BlockNumber(U64::ZERO);
23    pub const ONE: BlockNumber = BlockNumber(U64::ONE);
24    pub const MAX: BlockNumber = BlockNumber(U64::from_limbs([i64::MAX as u64]));
25
26    /// Calculates the keccak256 hash of the block number.
27    pub fn hash(&self) -> Hash {
28        Hash::new(*keccak256(<[u8; 8]>::from(*self)))
29    }
30
31    /// Returns the previous block number.
32    pub fn prev(&self) -> Option<Self> {
33        if self.is_zero() { None } else { Some(Self(self.0 - U64::ONE)) }
34    }
35
36    /// Returns the next block number.
37    pub fn next_block_number(&self) -> Self {
38        Self(self.0 + U64::ONE)
39    }
40
41    /// Checks if it is the zero block number.
42    pub fn is_zero(&self) -> bool {
43        self.0.is_zero()
44    }
45
46    /// Count how many blocks there is between itself and the othe block.
47    ///
48    /// Assumes that self is the lower-end of the range.
49    pub fn count_to(self, higher_end: impl Into<u64>) -> u64 {
50        higher_end.into().saturating_sub(self.as_u64()) + 1
51    }
52
53    pub fn as_i64(&self) -> i64 {
54        self.0.try_into().unwrap()
55    }
56
57    pub fn as_u64(&self) -> u64 {
58        self.0.try_into().unwrap()
59    }
60
61    pub fn as_u32(&self) -> u32 {
62        self.0.try_into().unwrap()
63    }
64}
65
66#[cfg(test)]
67impl Dummy<Faker> for BlockNumber {
68    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &Faker, rng: &mut R) -> Self {
69        rng.next_u64().into()
70    }
71}
72
73// -----------------------------------------------------------------------------
74// Math
75// -----------------------------------------------------------------------------
76
77impl Add<usize> for BlockNumber {
78    type Output = BlockNumber;
79
80    fn add(self, rhs: usize) -> Self::Output {
81        Self(self.0 + U64::from(rhs))
82    }
83}
84
85impl AddAssign<usize> for BlockNumber {
86    fn add_assign(&mut self, rhs: usize) {
87        self.0 = self.0 + U64::from(rhs);
88    }
89}
90
91// -----------------------------------------------------------------------------
92// Conversions: Other -> Self
93// -----------------------------------------------------------------------------
94
95impl From<u8> for BlockNumber {
96    fn from(value: u8) -> Self {
97        Self(U64::from(value))
98    }
99}
100
101impl From<u16> for BlockNumber {
102    fn from(value: u16) -> Self {
103        Self(U64::from(value))
104    }
105}
106
107impl From<u32> for BlockNumber {
108    fn from(value: u32) -> Self {
109        Self(U64::from(value))
110    }
111}
112
113impl From<u64> for BlockNumber {
114    fn from(value: u64) -> Self {
115        Self(U64::from(value))
116    }
117}
118
119impl From<U64> for BlockNumber {
120    fn from(value: U64) -> Self {
121        Self(value)
122    }
123}
124
125impl From<usize> for BlockNumber {
126    fn from(value: usize) -> Self {
127        Self(U64::from(value))
128    }
129}
130
131impl From<i32> for BlockNumber {
132    fn from(value: i32) -> Self {
133        Self(U64::from(value as u32))
134    }
135}
136
137impl From<i64> for BlockNumber {
138    fn from(value: i64) -> Self {
139        Self(U64::from(value as u64))
140    }
141}
142
143impl FromStr for BlockNumber {
144    type Err = anyhow::Error;
145
146    fn from_str(s: &str) -> anyhow::Result<Self> {
147        // This parses a hexadecimal string
148        match U64::from_str(s) {
149            Ok(parsed) => Ok(Self(parsed)),
150            Err(e) => {
151                tracing::warn!(reason = ?e, value = %s, "failed to parse block number");
152                Err(anyhow!("Failed to parse field '{}' with value '{}'", "blockNumber", s))
153            }
154        }
155    }
156}
157
158// -----------------------------------------------------------------------------
159// Conversions: Self -> Other
160// -----------------------------------------------------------------------------
161impl From<BlockNumber> for u64 {
162    fn from(block_number: BlockNumber) -> Self {
163        block_number.as_u64()
164    }
165}
166
167impl From<BlockNumber> for U64 {
168    fn from(block_number: BlockNumber) -> Self {
169        block_number.0
170    }
171}
172
173impl From<BlockNumber> for U256 {
174    fn from(block_number: BlockNumber) -> Self {
175        Self::from(block_number.as_u64())
176    }
177}
178
179impl From<BlockNumber> for [u8; 8] {
180    fn from(block_number: BlockNumber) -> Self {
181        block_number.as_u64().to_be_bytes()
182    }
183}