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    ///
39    /// NOTE: This returns the latest implemented [`ForkId`]. In many cases this will be the future
40    /// [`ForkId`] on given network.
41    fn latest_fork_id(&self) -> ForkId;
42
43    /// Creates a [`ForkFilter`] for the block described by [Head].
44    fn fork_filter(&self, head: Head) -> ForkFilter;
45}
46
47/// Ordered list of a chain hardforks that implement [`Hardfork`].
48#[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    /// Creates a new [`ChainHardforks`] from a list which **must be ordered** by activation.
59    ///
60    /// Equivalent Ethereum hardforks **must be included** as well.
61    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    /// Total number of hardforks.
68    pub fn len(&self) -> usize {
69        self.forks.len()
70    }
71
72    /// Checks if the fork list is empty.
73    pub fn is_empty(&self) -> bool {
74        self.forks.is_empty()
75    }
76
77    /// Retrieves [`ForkCondition`] from `fork`. If `fork` is not present, returns
78    /// [`ForkCondition::Never`].
79    pub fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition {
80        self.get(fork).unwrap_or_default()
81    }
82
83    /// Retrieves [`ForkCondition`] from `fork` if it exists, otherwise `None`.
84    pub fn get<H: Hardfork>(&self, fork: H) -> Option<ForkCondition> {
85        self.map.get(fork.name()).copied()
86    }
87
88    /// Retrieves the fork block number or timestamp from `fork` if it exists, otherwise `None`.
89    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    /// Get an iterator of all hardforks with their respective activation conditions.
99    pub fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
100        self.forks.iter().map(|(f, b)| (&**f, *b))
101    }
102
103    /// Get last hardfork from the list.
104    pub fn last(&self) -> Option<(Box<dyn Hardfork>, ForkCondition)> {
105        self.forks.last().map(|(f, b)| (f.clone(), *b))
106    }
107
108    /// Convenience method to check if a fork is active at a given timestamp.
109    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    /// Convenience method to check if a fork is active at a given block number.
114    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    /// Inserts `fork` into list, updating with a new [`ForkCondition`] if it already exists.
119    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    /// Removes `fork` from list.
137    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}