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    /// Interop spec id.
29    INTEROP,
30    /// Osaka spec id.
31    OSAKA,
32}
33
34impl OpSpecId {
35    /// Converts the [`OpSpecId`] into a [`SpecId`].
36    pub const fn into_eth_spec(self) -> SpecId {
37        match self {
38            Self::BEDROCK | Self::REGOLITH => SpecId::MERGE,
39            Self::CANYON => SpecId::SHANGHAI,
40            Self::ECOTONE | Self::FJORD | Self::GRANITE | Self::HOLOCENE => SpecId::CANCUN,
41            Self::ISTHMUS | Self::INTEROP => SpecId::PRAGUE,
42            Self::OSAKA => SpecId::OSAKA,
43        }
44    }
45
46    /// Checks if the [`OpSpecId`] is enabled in the other [`OpSpecId`].
47    pub const fn is_enabled_in(self, other: OpSpecId) -> bool {
48        other as u8 <= self as u8
49    }
50}
51
52impl From<OpSpecId> for SpecId {
53    fn from(spec: OpSpecId) -> Self {
54        spec.into_eth_spec()
55    }
56}
57
58impl FromStr for OpSpecId {
59    type Err = UnknownHardfork;
60
61    fn from_str(s: &str) -> Result<Self, Self::Err> {
62        match s {
63            name::BEDROCK => Ok(OpSpecId::BEDROCK),
64            name::REGOLITH => Ok(OpSpecId::REGOLITH),
65            name::CANYON => Ok(OpSpecId::CANYON),
66            name::ECOTONE => Ok(OpSpecId::ECOTONE),
67            name::FJORD => Ok(OpSpecId::FJORD),
68            name::GRANITE => Ok(OpSpecId::GRANITE),
69            name::HOLOCENE => Ok(OpSpecId::HOLOCENE),
70            name::ISTHMUS => Ok(OpSpecId::ISTHMUS),
71            name::INTEROP => Ok(OpSpecId::INTEROP),
72            eth_name::OSAKA => Ok(OpSpecId::OSAKA),
73            _ => Err(UnknownHardfork),
74        }
75    }
76}
77
78impl From<OpSpecId> for &'static str {
79    fn from(spec_id: OpSpecId) -> Self {
80        match spec_id {
81            OpSpecId::BEDROCK => name::BEDROCK,
82            OpSpecId::REGOLITH => name::REGOLITH,
83            OpSpecId::CANYON => name::CANYON,
84            OpSpecId::ECOTONE => name::ECOTONE,
85            OpSpecId::FJORD => name::FJORD,
86            OpSpecId::GRANITE => name::GRANITE,
87            OpSpecId::HOLOCENE => name::HOLOCENE,
88            OpSpecId::ISTHMUS => name::ISTHMUS,
89            OpSpecId::INTEROP => name::INTEROP,
90            OpSpecId::OSAKA => eth_name::OSAKA,
91        }
92    }
93}
94
95/// String identifiers for Optimism hardforks
96pub mod name {
97    /// Bedrock spec name.
98    pub const BEDROCK: &str = "Bedrock";
99    /// Regolith spec name.
100    pub const REGOLITH: &str = "Regolith";
101    /// Canyon spec name.
102    pub const CANYON: &str = "Canyon";
103    /// Ecotone spec name.
104    pub const ECOTONE: &str = "Ecotone";
105    /// Fjord spec name.
106    pub const FJORD: &str = "Fjord";
107    /// Granite spec name.
108    pub const GRANITE: &str = "Granite";
109    /// Holocene spec name.
110    pub const HOLOCENE: &str = "Holocene";
111    /// Isthmus spec name.
112    pub const ISTHMUS: &str = "Isthmus";
113    /// Interop spec name.
114    pub const INTEROP: &str = "Interop";
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120    use std::vec;
121
122    #[test]
123    fn test_op_spec_id_eth_spec_compatibility() {
124        // Define test cases: (OpSpecId, enabled in ETH specs, enabled in OP specs)
125        let test_cases = [
126            (
127                OpSpecId::BEDROCK,
128                vec![
129                    (SpecId::MERGE, true),
130                    (SpecId::SHANGHAI, false),
131                    (SpecId::CANCUN, false),
132                    (SpecId::default(), false),
133                ],
134                vec![(OpSpecId::BEDROCK, true), (OpSpecId::REGOLITH, false)],
135            ),
136            (
137                OpSpecId::REGOLITH,
138                vec![
139                    (SpecId::MERGE, true),
140                    (SpecId::SHANGHAI, false),
141                    (SpecId::CANCUN, false),
142                    (SpecId::default(), false),
143                ],
144                vec![(OpSpecId::BEDROCK, true), (OpSpecId::REGOLITH, true)],
145            ),
146            (
147                OpSpecId::CANYON,
148                vec![
149                    (SpecId::MERGE, true),
150                    (SpecId::SHANGHAI, true),
151                    (SpecId::CANCUN, false),
152                    (SpecId::default(), false),
153                ],
154                vec![
155                    (OpSpecId::BEDROCK, true),
156                    (OpSpecId::REGOLITH, true),
157                    (OpSpecId::CANYON, true),
158                ],
159            ),
160            (
161                OpSpecId::ECOTONE,
162                vec![
163                    (SpecId::MERGE, true),
164                    (SpecId::SHANGHAI, true),
165                    (SpecId::CANCUN, true),
166                    (SpecId::default(), false),
167                ],
168                vec![
169                    (OpSpecId::BEDROCK, true),
170                    (OpSpecId::REGOLITH, true),
171                    (OpSpecId::CANYON, true),
172                    (OpSpecId::ECOTONE, true),
173                ],
174            ),
175            (
176                OpSpecId::FJORD,
177                vec![
178                    (SpecId::MERGE, true),
179                    (SpecId::SHANGHAI, true),
180                    (SpecId::CANCUN, true),
181                    (SpecId::default(), false),
182                ],
183                vec![
184                    (OpSpecId::BEDROCK, true),
185                    (OpSpecId::REGOLITH, true),
186                    (OpSpecId::CANYON, true),
187                    (OpSpecId::ECOTONE, true),
188                    (OpSpecId::FJORD, true),
189                ],
190            ),
191        ];
192
193        for (op_spec, eth_tests, op_tests) in test_cases {
194            // Test ETH spec compatibility
195            for (eth_spec, expected) in eth_tests {
196                assert_eq!(
197                    op_spec.into_eth_spec().is_enabled_in(eth_spec),
198                    expected,
199                    "{:?} should {} be enabled in ETH {:?}",
200                    op_spec,
201                    if expected { "" } else { "not " },
202                    eth_spec
203                );
204            }
205
206            // Test OP spec compatibility
207            for (other_op_spec, expected) in op_tests {
208                assert_eq!(
209                    op_spec.is_enabled_in(other_op_spec),
210                    expected,
211                    "{:?} should {} be enabled in OP {:?}",
212                    op_spec,
213                    if expected { "" } else { "not " },
214                    other_op_spec
215                );
216            }
217        }
218    }
219
220    #[test]
221    fn default_op_spec_id() {
222        assert_eq!(OpSpecId::default(), OpSpecId::ISTHMUS);
223    }
224}