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 INTEROP,
30 OSAKA,
32}
33
34impl OpSpecId {
35 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 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
95pub mod name {
97 pub const BEDROCK: &str = "Bedrock";
99 pub const REGOLITH: &str = "Regolith";
101 pub const CANYON: &str = "Canyon";
103 pub const ECOTONE: &str = "Ecotone";
105 pub const FJORD: &str = "Fjord";
107 pub const GRANITE: &str = "Granite";
109 pub const HOLOCENE: &str = "Holocene";
111 pub const ISTHMUS: &str = "Isthmus";
113 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 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 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 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}