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#[cfg(feature = "std")]
8use std::collections::hash_map::Entry;
9
10#[cfg(not(feature = "std"))]
11use alloc::collections::btree_map::Entry;
12use alloc::{boxed::Box, vec::Vec};
13
14#[auto_impl::auto_impl(&, Arc)]
16pub trait Hardforks: Clone {
17 fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition;
20
21 fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)>;
23
24 fn is_fork_active_at_timestamp<H: Hardfork>(&self, fork: H, timestamp: u64) -> bool {
26 self.fork(fork).active_at_timestamp(timestamp)
27 }
28
29 fn is_fork_active_at_block<H: Hardfork>(&self, fork: H, block_number: u64) -> bool {
31 self.fork(fork).active_at_block(block_number)
32 }
33
34 fn fork_id(&self, head: &Head) -> ForkId;
36
37 fn latest_fork_id(&self) -> ForkId;
42
43 fn fork_filter(&self, head: Head) -> ForkFilter;
45}
46
47#[derive(Default, Clone, PartialEq, Eq)]
49pub struct ChainHardforks {
50 forks: Vec<(Box<dyn Hardfork>, ForkCondition)>,
51 #[cfg(feature = "std")]
52 map: FxHashMap<&'static str, ForkCondition>,
53 #[cfg(not(feature = "std"))]
54 map: alloc::collections::BTreeMap<&'static str, ForkCondition>,
55}
56
57impl ChainHardforks {
58 pub fn new(forks: Vec<(Box<dyn Hardfork>, ForkCondition)>) -> Self {
62 let map = forks.iter().map(|(fork, condition)| (fork.name(), *condition)).collect();
63
64 Self { forks, map }
65 }
66
67 pub fn len(&self) -> usize {
69 self.forks.len()
70 }
71
72 pub fn is_empty(&self) -> bool {
74 self.forks.is_empty()
75 }
76
77 pub fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition {
80 self.get(fork).unwrap_or_default()
81 }
82
83 pub fn get<H: Hardfork>(&self, fork: H) -> Option<ForkCondition> {
85 self.map.get(fork.name()).copied()
86 }
87
88 pub fn fork_block<H: Hardfork>(&self, fork: H) -> Option<u64> {
90 match self.fork(fork) {
91 ForkCondition::Block(block) => Some(block),
92 ForkCondition::TTD { fork_block, .. } => fork_block,
93 ForkCondition::Timestamp(ts) => Some(ts),
94 ForkCondition::Never => None,
95 }
96 }
97
98 pub fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
100 self.forks.iter().map(|(f, b)| (&**f, *b))
101 }
102
103 pub fn last(&self) -> Option<(Box<dyn Hardfork>, ForkCondition)> {
105 self.forks.last().map(|(f, b)| (f.clone(), *b))
106 }
107
108 pub fn is_fork_active_at_timestamp<H: Hardfork>(&self, fork: H, timestamp: u64) -> bool {
110 self.fork(fork).active_at_timestamp(timestamp)
111 }
112
113 pub fn is_fork_active_at_block<H: Hardfork>(&self, fork: H, block_number: u64) -> bool {
115 self.fork(fork).active_at_block(block_number)
116 }
117
118 pub fn insert<H: Hardfork>(&mut self, fork: H, condition: ForkCondition) {
120 match self.map.entry(fork.name()) {
121 Entry::Occupied(mut entry) => {
122 *entry.get_mut() = condition;
123 if let Some((_, inner)) =
124 self.forks.iter_mut().find(|(inner, _)| inner.name() == fork.name())
125 {
126 *inner = condition;
127 }
128 }
129 Entry::Vacant(entry) => {
130 entry.insert(condition);
131 self.forks.push((Box::new(fork), condition));
132 }
133 }
134 }
135
136 pub fn remove<H: Hardfork>(&mut self, fork: H) {
138 self.forks.retain(|(inner_fork, _)| inner_fork.name() != fork.name());
139 self.map.remove(fork.name());
140 }
141}
142
143impl core::fmt::Debug for ChainHardforks {
144 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
145 f.debug_struct("ChainHardforks")
146 .field("0", &self.forks_iter().map(|(hf, cond)| (hf.name(), cond)).collect::<Vec<_>>())
147 .finish()
148 }
149}
150
151impl<T: Hardfork, const N: usize> From<[(T, ForkCondition); N]> for ChainHardforks {
152 fn from(list: [(T, ForkCondition); N]) -> Self {
153 Self::new(
154 list.into_iter()
155 .map(|(fork, cond)| (Box::new(fork) as Box<dyn Hardfork>, cond))
156 .collect(),
157 )
158 }
159}