reth_ethereum_forks/hardforks/
mod.rs

1mod 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/// Generic trait over a set of ordered hardforks
15#[auto_impl::auto_impl(&, Arc)]
16pub trait Hardforks: Clone {
17    /// Retrieves [`ForkCondition`] from `fork`. If `fork` is not present, returns
18    /// [`ForkCondition::Never`].
19    fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition;
20
21    /// Get an iterator of all hardforks with their respective activation conditions.
22    fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)>;
23
24    /// Convenience method to check if a fork is active at a given timestamp.
25    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    /// Convenience method to check if a fork is active at a given block number.
30    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    /// Compute the [`ForkId`] for the given [`Head`] following eip-6122 spec
35    fn fork_id(&self, head: &Head) -> ForkId;
36
37    /// Returns the [`ForkId`] for the last fork.
38    fn latest_fork_id(&self) -> ForkId;
39
40    /// Creates a [`ForkFilter`] for the block described by [Head].
41    fn fork_filter(&self, head: Head) -> ForkFilter;
42}
43
44/// Ordered list of a chain hardforks that implement [`Hardfork`].
45#[derive(Default, Clone, PartialEq, Eq)]
46pub struct ChainHardforks {
47    forks: Vec<(Box<dyn Hardfork>, ForkCondition)>,
48    #[cfg(feature = "std")]
49    map: FxHashMap<&'static str, ForkCondition>,
50    #[cfg(not(feature = "std"))]
51    map: alloc::collections::BTreeMap<&'static str, ForkCondition>,
52}
53
54impl ChainHardforks {
55    /// Creates a new [`ChainHardforks`] from a list which **must be ordered** by activation.
56    ///
57    /// Equivalent Ethereum hardforks **must be included** as well.
58    pub fn new(forks: Vec<(Box<dyn Hardfork>, ForkCondition)>) -> Self {
59        let map = forks.iter().map(|(fork, condition)| (fork.name(), *condition)).collect();
60
61        Self { forks, map }
62    }
63
64    /// Total number of hardforks.
65    pub fn len(&self) -> usize {
66        self.forks.len()
67    }
68
69    /// Checks if the fork list is empty.
70    pub fn is_empty(&self) -> bool {
71        self.forks.is_empty()
72    }
73
74    /// Retrieves [`ForkCondition`] from `fork`. If `fork` is not present, returns
75    /// [`ForkCondition::Never`].
76    pub fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition {
77        self.get(fork).unwrap_or_default()
78    }
79
80    /// Retrieves [`ForkCondition`] from `fork` if it exists, otherwise `None`.
81    pub fn get<H: Hardfork>(&self, fork: H) -> Option<ForkCondition> {
82        self.map.get(fork.name()).copied()
83    }
84
85    /// Retrieves the fork block number or timestamp from `fork` if it exists, otherwise `None`.
86    pub fn fork_block<H: Hardfork>(&self, fork: H) -> Option<u64> {
87        match self.fork(fork) {
88            ForkCondition::Block(block) => Some(block),
89            ForkCondition::TTD { fork_block, .. } => fork_block,
90            ForkCondition::Timestamp(ts) => Some(ts),
91            ForkCondition::Never => None,
92        }
93    }
94
95    /// Get an iterator of all hardforks with their respective activation conditions.
96    pub fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
97        self.forks.iter().map(|(f, b)| (&**f, *b))
98    }
99
100    /// Get last hardfork from the list.
101    pub fn last(&self) -> Option<(Box<dyn Hardfork>, ForkCondition)> {
102        self.forks.last().map(|(f, b)| (f.clone(), *b))
103    }
104
105    /// Convenience method to check if a fork is active at a given timestamp.
106    pub fn is_fork_active_at_timestamp<H: Hardfork>(&self, fork: H, timestamp: u64) -> bool {
107        self.fork(fork).active_at_timestamp(timestamp)
108    }
109
110    /// Convenience method to check if a fork is active at a given block number.
111    pub fn is_fork_active_at_block<H: Hardfork>(&self, fork: H, block_number: u64) -> bool {
112        self.fork(fork).active_at_block(block_number)
113    }
114
115    /// Inserts `fork` into list, updating with a new [`ForkCondition`] if it already exists.
116    pub fn insert<H: Hardfork>(&mut self, fork: H, condition: ForkCondition) {
117        match self.map.entry(fork.name()) {
118            Entry::Occupied(mut entry) => {
119                *entry.get_mut() = condition;
120                if let Some((_, inner)) =
121                    self.forks.iter_mut().find(|(inner, _)| inner.name() == fork.name())
122                {
123                    *inner = condition;
124                }
125            }
126            Entry::Vacant(entry) => {
127                entry.insert(condition);
128                self.forks.push((Box::new(fork), condition));
129            }
130        }
131    }
132
133    /// Removes `fork` from list.
134    pub fn remove<H: Hardfork>(&mut self, fork: H) {
135        self.forks.retain(|(inner_fork, _)| inner_fork.name() != fork.name());
136        self.map.remove(fork.name());
137    }
138}
139
140impl core::fmt::Debug for ChainHardforks {
141    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142        f.debug_struct("ChainHardforks")
143            .field("0", &self.forks_iter().map(|(hf, cond)| (hf.name(), cond)).collect::<Vec<_>>())
144            .finish()
145    }
146}
147
148impl<T: Hardfork, const N: usize> From<[(T, ForkCondition); N]> for ChainHardforks {
149    fn from(list: [(T, ForkCondition); N]) -> Self {
150        Self::new(
151            list.into_iter()
152                .map(|(fork, cond)| (Box::new(fork) as Box<dyn Hardfork>, cond))
153                .collect(),
154        )
155    }
156}