1use reth_chainspec::{EthChainSpec, EthereumHardforks};
2use reth_ethereum_forks::{EthereumHardfork, Hardforks};
3use reth_primitives_traits::BlockHeader;
4use revm::primitives::hardfork::SpecId;
5
6pub fn revm_spec<C, H>(chain_spec: &C, header: &H) -> SpecId
8where
9 C: EthereumHardforks + EthChainSpec + Hardforks,
10 H: BlockHeader,
11{
12 revm_spec_by_timestamp_and_block_number(chain_spec, header.timestamp(), header.number())
13}
14
15pub fn revm_spec_by_timestamp_and_block_number<C>(
17 chain_spec: &C,
18 timestamp: u64,
19 block_number: u64,
20) -> SpecId
21where
22 C: EthereumHardforks + EthChainSpec + Hardforks,
23{
24 if chain_spec
25 .fork(EthereumHardfork::Osaka)
26 .active_at_timestamp_or_number(timestamp, block_number)
27 {
28 SpecId::OSAKA
29 } else if chain_spec
30 .fork(EthereumHardfork::Prague)
31 .active_at_timestamp_or_number(timestamp, block_number)
32 {
33 SpecId::PRAGUE
34 } else if chain_spec
35 .fork(EthereumHardfork::Cancun)
36 .active_at_timestamp_or_number(timestamp, block_number)
37 {
38 SpecId::CANCUN
39 } else if chain_spec
40 .fork(EthereumHardfork::Shanghai)
41 .active_at_timestamp_or_number(timestamp, block_number)
42 {
43 SpecId::SHANGHAI
44 } else if chain_spec.is_paris_active_at_block(block_number) {
45 SpecId::MERGE
46 } else if chain_spec
47 .fork(EthereumHardfork::London)
48 .active_at_timestamp_or_number(timestamp, block_number)
49 {
50 SpecId::LONDON
51 } else if chain_spec
52 .fork(EthereumHardfork::Berlin)
53 .active_at_timestamp_or_number(timestamp, block_number)
54 {
55 SpecId::BERLIN
56 } else if chain_spec
57 .fork(EthereumHardfork::Istanbul)
58 .active_at_timestamp_or_number(timestamp, block_number)
59 {
60 SpecId::ISTANBUL
61 } else if chain_spec
62 .fork(EthereumHardfork::Petersburg)
63 .active_at_timestamp_or_number(timestamp, block_number)
64 {
65 SpecId::PETERSBURG
66 } else if chain_spec
67 .fork(EthereumHardfork::Byzantium)
68 .active_at_timestamp_or_number(timestamp, block_number)
69 {
70 SpecId::BYZANTIUM
71 } else if chain_spec
72 .fork(EthereumHardfork::SpuriousDragon)
73 .active_at_timestamp_or_number(timestamp, block_number)
74 {
75 SpecId::SPURIOUS_DRAGON
76 } else if chain_spec
77 .fork(EthereumHardfork::Tangerine)
78 .active_at_timestamp_or_number(timestamp, block_number)
79 {
80 SpecId::TANGERINE
81 } else if chain_spec
82 .fork(EthereumHardfork::Homestead)
83 .active_at_timestamp_or_number(timestamp, block_number)
84 {
85 SpecId::HOMESTEAD
86 } else if chain_spec
87 .fork(EthereumHardfork::Frontier)
88 .active_at_timestamp_or_number(timestamp, block_number)
89 {
90 SpecId::FRONTIER
91 } else {
92 panic!(
93 "invalid hardfork chainspec: expected at least one hardfork, got {}",
94 chain_spec.display_hardforks()
95 )
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use crate::U256;
103 use alloy_consensus::Header;
104 use reth_chainspec::{ChainSpecBuilder, MAINNET};
105
106 #[test]
107 fn test_revm_spec_by_timestamp() {
108 assert_eq!(
109 revm_spec_by_timestamp_and_block_number(
110 &ChainSpecBuilder::mainnet().cancun_activated().build(),
111 0,
112 0
113 ),
114 SpecId::CANCUN
115 );
116 assert_eq!(
117 revm_spec_by_timestamp_and_block_number(
118 &ChainSpecBuilder::mainnet().shanghai_activated().build(),
119 0,
120 0
121 ),
122 SpecId::SHANGHAI
123 );
124 let mainnet = ChainSpecBuilder::mainnet().build();
125 assert_eq!(
126 revm_spec_by_timestamp_and_block_number(&mainnet, 0, mainnet.paris_block().unwrap()),
127 SpecId::MERGE
128 );
129 }
130
131 #[test]
132 fn test_to_revm_spec() {
133 assert_eq!(
134 revm_spec(&ChainSpecBuilder::mainnet().cancun_activated().build(), &Header::default()),
135 SpecId::CANCUN
136 );
137 assert_eq!(
138 revm_spec(
139 &ChainSpecBuilder::mainnet().shanghai_activated().build(),
140 &Header::default()
141 ),
142 SpecId::SHANGHAI
143 );
144 assert_eq!(
145 revm_spec(&ChainSpecBuilder::mainnet().paris_activated().build(), &Header::default()),
146 SpecId::MERGE
147 );
148 assert_eq!(
149 revm_spec(&ChainSpecBuilder::mainnet().london_activated().build(), &Header::default()),
150 SpecId::LONDON
151 );
152 assert_eq!(
153 revm_spec(&ChainSpecBuilder::mainnet().berlin_activated().build(), &Header::default()),
154 SpecId::BERLIN
155 );
156 assert_eq!(
157 revm_spec(
158 &ChainSpecBuilder::mainnet().istanbul_activated().build(),
159 &Header::default()
160 ),
161 SpecId::ISTANBUL
162 );
163 assert_eq!(
164 revm_spec(
165 &ChainSpecBuilder::mainnet().petersburg_activated().build(),
166 &Header::default()
167 ),
168 SpecId::PETERSBURG
169 );
170 assert_eq!(
171 revm_spec(
172 &ChainSpecBuilder::mainnet().byzantium_activated().build(),
173 &Header::default()
174 ),
175 SpecId::BYZANTIUM
176 );
177 assert_eq!(
178 revm_spec(
179 &ChainSpecBuilder::mainnet().spurious_dragon_activated().build(),
180 &Header::default()
181 ),
182 SpecId::SPURIOUS_DRAGON
183 );
184 assert_eq!(
185 revm_spec(
186 &ChainSpecBuilder::mainnet().tangerine_whistle_activated().build(),
187 &Header::default()
188 ),
189 SpecId::TANGERINE
190 );
191 assert_eq!(
192 revm_spec(
193 &ChainSpecBuilder::mainnet().homestead_activated().build(),
194 &Header::default()
195 ),
196 SpecId::HOMESTEAD
197 );
198 assert_eq!(
199 revm_spec(
200 &ChainSpecBuilder::mainnet().frontier_activated().build(),
201 &Header::default()
202 ),
203 SpecId::FRONTIER
204 );
205 }
206
207 #[test]
208 fn test_eth_spec() {
209 assert_eq!(
210 revm_spec(&*MAINNET, &Header { timestamp: 1710338135, ..Default::default() }),
211 SpecId::CANCUN
212 );
213 assert_eq!(
214 revm_spec(&*MAINNET, &Header { timestamp: 1681338455, ..Default::default() }),
215 SpecId::SHANGHAI
216 );
217
218 assert_eq!(
219 revm_spec(
220 &*MAINNET,
221 &Header { difficulty: U256::from(10_u128), number: 15537394, ..Default::default() }
222 ),
223 SpecId::MERGE
224 );
225 assert_eq!(
226 revm_spec(&*MAINNET, &Header { number: 15537394 - 10, ..Default::default() }),
227 SpecId::LONDON
228 );
229 assert_eq!(
230 revm_spec(&*MAINNET, &Header { number: 12244000 + 10, ..Default::default() }),
231 SpecId::BERLIN
232 );
233 assert_eq!(
234 revm_spec(&*MAINNET, &Header { number: 12244000 - 10, ..Default::default() }),
235 SpecId::ISTANBUL
236 );
237 assert_eq!(
238 revm_spec(&*MAINNET, &Header { number: 7280000 + 10, ..Default::default() }),
239 SpecId::PETERSBURG
240 );
241 assert_eq!(
242 revm_spec(&*MAINNET, &Header { number: 7280000 - 10, ..Default::default() }),
243 SpecId::BYZANTIUM
244 );
245 assert_eq!(
246 revm_spec(&*MAINNET, &Header { number: 2675000 + 10, ..Default::default() }),
247 SpecId::SPURIOUS_DRAGON
248 );
249 assert_eq!(
250 revm_spec(&*MAINNET, &Header { number: 2675000 - 10, ..Default::default() }),
251 SpecId::TANGERINE
252 );
253 assert_eq!(
254 revm_spec(&*MAINNET, &Header { number: 1150000 + 10, ..Default::default() }),
255 SpecId::HOMESTEAD
256 );
257 assert_eq!(
258 revm_spec(&*MAINNET, &Header { number: 1150000 - 10, ..Default::default() }),
259 SpecId::FRONTIER
260 );
261 }
262}