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