reth_ethereum_forks/hardforks/
mod.rs1mod dev;
2pub use dev::DEV_HARDFORKS;
3
4use crate::{ForkCondition, ForkFilter, ForkId, Hardfork, Head};
5#[cfg(feature = "std")]
6use rustc_hash::FxHashMap;
7
8use alloc::{boxed::Box, vec::Vec};
9
10#[auto_impl::auto_impl(&, Arc)]
12pub trait Hardforks: Clone {
13 fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition;
16
17 fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)>;
19
20 fn is_fork_active_at_timestamp<H: Hardfork>(&self, fork: H, timestamp: u64) -> bool {
22 self.fork(fork).active_at_timestamp(timestamp)
23 }
24
25 fn is_fork_active_at_block<H: Hardfork>(&self, fork: H, block_number: u64) -> bool {
27 self.fork(fork).active_at_block(block_number)
28 }
29
30 fn fork_id(&self, head: &Head) -> ForkId;
32
33 fn latest_fork_id(&self) -> ForkId;
38
39 fn fork_filter(&self, head: Head) -> ForkFilter;
41}
42
43#[derive(Default, Clone, PartialEq, Eq)]
45pub struct ChainHardforks {
46 forks: Vec<(Box<dyn Hardfork>, ForkCondition)>,
47 #[cfg(feature = "std")]
48 map: FxHashMap<&'static str, ForkCondition>,
49 #[cfg(not(feature = "std"))]
50 map: alloc::collections::BTreeMap<&'static str, ForkCondition>,
51}
52
53impl ChainHardforks {
54 pub fn new(forks: Vec<(Box<dyn Hardfork>, ForkCondition)>) -> Self {
58 let map = forks.iter().map(|(fork, condition)| (fork.name(), *condition)).collect();
59
60 Self { forks, map }
61 }
62
63 pub fn len(&self) -> usize {
65 self.forks.len()
66 }
67
68 pub fn is_empty(&self) -> bool {
70 self.forks.is_empty()
71 }
72
73 pub fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition {
76 self.get(fork).unwrap_or_default()
77 }
78
79 pub fn get<H: Hardfork>(&self, fork: H) -> Option<ForkCondition> {
81 self.map.get(fork.name()).copied()
82 }
83
84 pub fn fork_block<H: Hardfork>(&self, fork: H) -> Option<u64> {
86 match self.fork(fork) {
87 ForkCondition::Block(block) => Some(block),
88 ForkCondition::TTD { fork_block, .. } => fork_block,
89 ForkCondition::Timestamp(ts) => Some(ts),
90 ForkCondition::Never => None,
91 }
92 }
93
94 pub fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
96 self.forks.iter().map(|(f, b)| (&**f, *b))
97 }
98
99 pub fn last(&self) -> Option<(Box<dyn Hardfork>, ForkCondition)> {
101 self.forks.last().map(|(f, b)| (f.clone(), *b))
102 }
103
104 pub fn is_fork_active_at_timestamp<H: Hardfork>(&self, fork: H, timestamp: u64) -> bool {
106 self.fork(fork).active_at_timestamp(timestamp)
107 }
108
109 pub fn is_fork_active_at_block<H: Hardfork>(&self, fork: H, block_number: u64) -> bool {
111 self.fork(fork).active_at_block(block_number)
112 }
113
114 pub fn insert<H: Hardfork>(&mut self, fork: H, condition: ForkCondition) {
139 self.remove(&fork);
141
142 let pos = self
144 .forks
145 .iter()
146 .position(|(_, existing_condition)| *existing_condition > condition)
147 .unwrap_or(self.forks.len());
148
149 self.map.insert(fork.name(), condition);
150 self.forks.insert(pos, (Box::new(fork), condition));
151 }
152
153 pub fn extend<H: Hardfork + Clone>(
172 &mut self,
173 forks: impl IntoIterator<Item = (H, ForkCondition)>,
174 ) {
175 for (fork, condition) in forks {
176 self.insert(fork, condition);
177 }
178 }
179
180 pub fn remove<H: Hardfork>(&mut self, fork: &H) {
182 self.forks.retain(|(inner_fork, _)| inner_fork.name() != fork.name());
183 self.map.remove(fork.name());
184 }
185}
186
187impl core::fmt::Debug for ChainHardforks {
188 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
189 f.debug_struct("ChainHardforks")
190 .field("0", &self.forks_iter().map(|(hf, cond)| (hf.name(), cond)).collect::<Vec<_>>())
191 .finish()
192 }
193}
194
195impl<T: Hardfork, const N: usize> From<[(T, ForkCondition); N]> for ChainHardforks {
196 fn from(list: [(T, ForkCondition); N]) -> Self {
197 Self::new(
198 list.into_iter()
199 .map(|(fork, cond)| (Box::new(fork) as Box<dyn Hardfork>, cond))
200 .collect(),
201 )
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use alloy_hardforks::hardfork;
209
210 hardfork!(AHardfork { A1, A2, A3 });
211 hardfork!(BHardfork { B1, B2 });
212
213 #[test]
214 fn add_hardforks() {
215 let mut forks = ChainHardforks::default();
216 forks.insert(AHardfork::A1, ForkCondition::Block(1));
217 forks.insert(BHardfork::B1, ForkCondition::Block(1));
218 assert_eq!(forks.len(), 2);
219 forks.is_fork_active_at_block(AHardfork::A1, 1);
220 forks.is_fork_active_at_block(BHardfork::B1, 1);
221 }
222
223 #[test]
224 fn insert_maintains_fork_order() {
225 let mut forks = ChainHardforks::default();
226
227 forks.insert(BHardfork::B1, ForkCondition::Timestamp(2000));
229 forks.insert(AHardfork::A1, ForkCondition::Block(100));
230 forks.insert(AHardfork::A2, ForkCondition::Block(50));
231 forks.insert(BHardfork::B2, ForkCondition::Timestamp(1000));
232
233 assert_eq!(forks.len(), 4);
234
235 let fork_list: Vec<_> = forks.forks_iter().collect();
236
237 assert_eq!(fork_list[0].0.name(), "A2");
240 assert_eq!(fork_list[0].1, ForkCondition::Block(50));
241 assert_eq!(fork_list[1].0.name(), "A1");
242 assert_eq!(fork_list[1].1, ForkCondition::Block(100));
243 assert_eq!(fork_list[2].0.name(), "B2");
244 assert_eq!(fork_list[2].1, ForkCondition::Timestamp(1000));
245 assert_eq!(fork_list[3].0.name(), "B1");
246 assert_eq!(fork_list[3].1, ForkCondition::Timestamp(2000));
247 }
248
249 #[test]
250 fn insert_replaces_and_reorders_existing_fork() {
251 let mut forks = ChainHardforks::default();
252
253 forks.insert(AHardfork::A1, ForkCondition::Block(100));
255 forks.insert(BHardfork::B1, ForkCondition::Block(200));
256 forks.insert(AHardfork::A2, ForkCondition::Timestamp(1000));
257
258 assert_eq!(forks.len(), 3);
259
260 forks.insert(AHardfork::A1, ForkCondition::Timestamp(500));
262 assert_eq!(forks.len(), 3);
263
264 let fork_list: Vec<_> = forks.forks_iter().collect();
265
266 assert_eq!(fork_list[0].0.name(), "B1");
268 assert_eq!(fork_list[0].1, ForkCondition::Block(200));
269 assert_eq!(fork_list[1].0.name(), "A1");
270 assert_eq!(fork_list[1].1, ForkCondition::Timestamp(500));
271 assert_eq!(fork_list[2].0.name(), "A2");
272 assert_eq!(fork_list[2].1, ForkCondition::Timestamp(1000));
273
274 forks.insert(AHardfork::A1, ForkCondition::Timestamp(2000));
276 assert_eq!(forks.len(), 3);
277
278 let fork_list: Vec<_> = forks.forks_iter().collect();
279
280 assert_eq!(fork_list[0].0.name(), "B1");
281 assert_eq!(fork_list[0].1, ForkCondition::Block(200));
282 assert_eq!(fork_list[1].0.name(), "A2");
283 assert_eq!(fork_list[1].1, ForkCondition::Timestamp(1000));
284 assert_eq!(fork_list[2].0.name(), "A1");
285 assert_eq!(fork_list[2].1, ForkCondition::Timestamp(2000));
286 }
287
288 #[test]
289 fn extend_maintains_order() {
290 let mut forks = ChainHardforks::default();
291
292 forks.extend([
294 (AHardfork::A1, ForkCondition::Block(100)),
295 (AHardfork::A2, ForkCondition::Timestamp(1000)),
296 ]);
297 forks.extend([(BHardfork::B1, ForkCondition::Timestamp(2000))]);
298
299 assert_eq!(forks.len(), 3);
300
301 let fork_list: Vec<_> = forks.forks_iter().collect();
302
303 assert_eq!(fork_list[0].0.name(), "A1");
305 assert_eq!(fork_list[0].1, ForkCondition::Block(100));
306 assert_eq!(fork_list[1].0.name(), "A2");
307 assert_eq!(fork_list[1].1, ForkCondition::Timestamp(1000));
308 assert_eq!(fork_list[2].0.name(), "B1");
309 assert_eq!(fork_list[2].1, ForkCondition::Timestamp(2000));
310
311 forks.extend([(AHardfork::A2, ForkCondition::Timestamp(3000))]);
313 assert_eq!(forks.len(), 3);
314
315 let fork_list: Vec<_> = forks.forks_iter().collect();
316
317 assert_eq!(fork_list[0].0.name(), "A1");
318 assert_eq!(fork_list[1].0.name(), "B1");
319 assert_eq!(fork_list[2].0.name(), "A2");
320 assert_eq!(fork_list[2].1, ForkCondition::Timestamp(3000));
321 }
322}