op_revm/
spec.rs

1//! Contains the `[OpSpecId]` type and its implementation.
2use core::str::FromStr;
3use revm::primitives::hardfork::{name as eth_name, SpecId, UnknownHardfork};
4
5/// Optimism spec id.
6#[repr(u8)]
7#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[allow(non_camel_case_types)]
10pub enum OpSpecId {
11    /// Bedrock spec id.
12    BEDROCK = 100,
13    /// Regolith spec id.
14    REGOLITH,
15    /// Canyon spec id.
16    CANYON,
17    /// Ecotone spec id.
18    ECOTONE,
19    /// Fjord spec id.
20    FJORD,
21    /// Granite spec id.
22    GRANITE,
23    /// Holocene spec id.
24    HOLOCENE,
25    /// Isthmus spec id.
26    #[default]
27    ISTHMUS,
28    /// Jovian spec id.
29    JOVIAN,
30    /// Interop spec id.
31    INTEROP,
32    /// Osaka spec id.
33    OSAKA,
34}
35
36impl OpSpecId {
37    /// Converts the [`OpSpecId`] into a [`SpecId`].
38    pub const fn into_eth_spec(self) -> SpecId {
39        match self {
40            Self::BEDROCK | Self::REGOLITH => SpecId::MERGE,
41            Self::CANYON => SpecId::SHANGHAI,
42            Self::ECOTONE | Self::FJORD | Self::GRANITE | Self::HOLOCENE => SpecId::CANCUN,
43            Self::ISTHMUS | Self::INTEROP => SpecId::PRAGUE,
44            Self::JOVIAN | Self::OSAKA => SpecId::OSAKA,
45        }
46    }
47
48    /// Checks if the [`OpSpecId`] is enabled in the other [`OpSpecId`].
49    pub const fn is_enabled_in(self, other: OpSpecId) -> bool {
50        other as u8 <= self as u8
51    }
52}
53
54impl From<OpSpecId> for SpecId {
55    fn from(spec: OpSpecId) -> Self {
56        spec.into_eth_spec()
57    }
58}
59
60impl FromStr for OpSpecId {
61    type Err = UnknownHardfork;
62
63    fn from_str(s: &str) -> Result<Self, Self::Err> {
64        match s {
65            name::BEDROCK => Ok(OpSpecId::BEDROCK),
66            name::REGOLITH => Ok(OpSpecId::REGOLITH),
67            name::CANYON => Ok(OpSpecId::CANYON),
68            name::ECOTONE => Ok(OpSpecId::ECOTONE),
69            name::FJORD => Ok(OpSpecId::FJORD),
70            name::GRANITE => Ok(OpSpecId::GRANITE),
71            name::HOLOCENE => Ok(OpSpecId::HOLOCENE),
72            name::ISTHMUS => Ok(OpSpecId::ISTHMUS),
73            name::JOVIAN => Ok(OpSpecId::JOVIAN),
74            name::INTEROP => Ok(OpSpecId::INTEROP),
75            eth_name::OSAKA => Ok(OpSpecId::OSAKA),
76            _ => Err(UnknownHardfork),
77        }
78    }
79}
80
81impl From<OpSpecId> for &'static str {
82    fn from(spec_id: OpSpecId) -> Self {
83        match spec_id {
84            OpSpecId::BEDROCK => name::BEDROCK,
85            OpSpecId::REGOLITH => name::REGOLITH,
86            OpSpecId::CANYON => name::CANYON,
87            OpSpecId::ECOTONE => name::ECOTONE,
88            OpSpecId::FJORD => name::FJORD,
89            OpSpecId::GRANITE => name::GRANITE,
90            OpSpecId::HOLOCENE => name::HOLOCENE,
91            OpSpecId::ISTHMUS => name::ISTHMUS,
92            OpSpecId::JOVIAN => name::JOVIAN,
93            OpSpecId::INTEROP => name::INTEROP,
94            OpSpecId::OSAKA => eth_name::OSAKA,
95        }
96    }
97}
98
99/// String identifiers for Optimism hardforks
100pub mod name {
101    /// Bedrock spec name.
102    pub const BEDROCK: &str = "Bedrock";
103    /// Regolith spec name.
104    pub const REGOLITH: &str = "Regolith";
105    /// Canyon spec name.
106    pub const CANYON: &str = "Canyon";
107    /// Ecotone spec name.
108    pub const ECOTONE: &str = "Ecotone";
109    /// Fjord spec name.
110    pub const FJORD: &str = "Fjord";
111    /// Granite spec name.
112    pub const GRANITE: &str = "Granite";
113    /// Holocene spec name.
114    pub const HOLOCENE: &str = "Holocene";
115    /// Isthmus spec name.
116    pub const ISTHMUS: &str = "Isthmus";
117    /// Jovian spec name.
118    pub const JOVIAN: &str = "Jovian";
119    /// Interop spec name.
120    pub const INTEROP: &str = "Interop";
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126    use std::vec;
127
128    #[test]
129    fn test_op_spec_id_eth_spec_compatibility() {
130        // Define test cases: (OpSpecId, enabled in ETH specs, enabled in OP specs)
131        let test_cases = [
132            (
133                OpSpecId::BEDROCK,
134                vec![
135                    (SpecId::MERGE, true),
136                    (SpecId::SHANGHAI, false),
137                    (SpecId::CANCUN, false),
138                    (SpecId::default(), false),
139                ],
140                vec![(OpSpecId::BEDROCK, true), (OpSpecId::REGOLITH, false)],
141            ),
142            (
143                OpSpecId::REGOLITH,
144                vec![
145                    (SpecId::MERGE, true),
146                    (SpecId::SHANGHAI, false),
147                    (SpecId::CANCUN, false),
148                    (SpecId::default(), false),
149                ],
150                vec![(OpSpecId::BEDROCK, true), (OpSpecId::REGOLITH, true)],
151            ),
152            (
153                OpSpecId::CANYON,
154                vec![
155                    (SpecId::MERGE, true),
156                    (SpecId::SHANGHAI, true),
157                    (SpecId::CANCUN, false),
158                    (SpecId::default(), false),
159                ],
160                vec![
161                    (OpSpecId::BEDROCK, true),
162                    (OpSpecId::REGOLITH, true),
163                    (OpSpecId::CANYON, true),
164                ],
165            ),
166            (
167                OpSpecId::ECOTONE,
168                vec![
169                    (SpecId::MERGE, true),
170                    (SpecId::SHANGHAI, true),
171                    (SpecId::CANCUN, true),
172                    (SpecId::default(), false),
173                ],
174                vec![
175                    (OpSpecId::BEDROCK, true),
176                    (OpSpecId::REGOLITH, true),
177                    (OpSpecId::CANYON, true),
178                    (OpSpecId::ECOTONE, true),
179                ],
180            ),
181            (
182                OpSpecId::FJORD,
183                vec![
184                    (SpecId::MERGE, true),
185                    (SpecId::SHANGHAI, true),
186                    (SpecId::CANCUN, true),
187                    (SpecId::default(), false),
188                ],
189                vec![
190                    (OpSpecId::BEDROCK, true),
191                    (OpSpecId::REGOLITH, true),
192                    (OpSpecId::CANYON, true),
193                    (OpSpecId::ECOTONE, true),
194                    (OpSpecId::FJORD, true),
195                ],
196            ),
197            (
198                OpSpecId::JOVIAN,
199                vec![
200                    (SpecId::PRAGUE, true),
201                    (SpecId::SHANGHAI, true),
202                    (SpecId::CANCUN, true),
203                    (SpecId::MERGE, true),
204                    (SpecId::OSAKA, true),
205                ],
206                vec![
207                    (OpSpecId::BEDROCK, true),
208                    (OpSpecId::REGOLITH, true),
209                    (OpSpecId::CANYON, true),
210                    (OpSpecId::ECOTONE, true),
211                    (OpSpecId::FJORD, true),
212                    (OpSpecId::HOLOCENE, true),
213                    (OpSpecId::ISTHMUS, true),
214                ],
215            ),
216        ];
217
218        for (op_spec, eth_tests, op_tests) in test_cases {
219            // Test ETH spec compatibility
220            for (eth_spec, expected) in eth_tests {
221                assert_eq!(
222                    op_spec.into_eth_spec().is_enabled_in(eth_spec),
223                    expected,
224                    "{:?} should {} be enabled in ETH {:?}",
225                    op_spec,
226                    if expected { "" } else { "not " },
227                    eth_spec
228                );
229            }
230
231            // Test OP spec compatibility
232            for (other_op_spec, expected) in op_tests {
233                assert_eq!(
234                    op_spec.is_enabled_in(other_op_spec),
235                    expected,
236                    "{:?} should {} be enabled in OP {:?}",
237                    op_spec,
238                    if expected { "" } else { "not " },
239                    other_op_spec
240                );
241            }
242        }
243    }
244
245    #[test]
246    fn default_op_spec_id() {
247        assert_eq!(OpSpecId::default(), OpSpecId::ISTHMUS);
248    }
249}