1use core::str::FromStr;
3use revm::primitives::hardfork::{name as eth_name, SpecId, UnknownHardfork};
4
5#[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 = 100,
13 REGOLITH,
15 CANYON,
17 ECOTONE,
19 FJORD,
21 GRANITE,
23 HOLOCENE,
25 #[default]
27 ISTHMUS,
28 JOVIAN,
30 INTEROP,
32 OSAKA,
34}
35
36impl OpSpecId {
37 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 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
99pub mod name {
101 pub const BEDROCK: &str = "Bedrock";
103 pub const REGOLITH: &str = "Regolith";
105 pub const CANYON: &str = "Canyon";
107 pub const ECOTONE: &str = "Ecotone";
109 pub const FJORD: &str = "Fjord";
111 pub const GRANITE: &str = "Granite";
113 pub const HOLOCENE: &str = "Holocene";
115 pub const ISTHMUS: &str = "Isthmus";
117 pub const JOVIAN: &str = "Jovian";
119 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 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 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 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}