op_revm/
spec.rs

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