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 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
82pub 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 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 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 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}