reth_ethereum_forks/hardforks/
mod.rs

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