1pub use alloy_eips::eip1559::BaseFeeParams;
2use alloy_evm::eth::spec::EthExecutorSpec;
3
4use crate::{
5 constants::{MAINNET_DEPOSIT_CONTRACT, MAINNET_PRUNE_DELETE_LIMIT},
6 ethereum::SEPOLIA_PARIS_TTD,
7 holesky, hoodi, mainnet,
8 mainnet::{MAINNET_PARIS_BLOCK, MAINNET_PARIS_TTD},
9 sepolia,
10 sepolia::SEPOLIA_PARIS_BLOCK,
11 EthChainSpec,
12};
13use alloc::{
14 boxed::Box,
15 collections::BTreeMap,
16 format,
17 string::{String, ToString},
18 sync::Arc,
19 vec::Vec,
20};
21use alloy_chains::{Chain, NamedChain};
22use alloy_consensus::{
23 constants::{
24 EMPTY_WITHDRAWALS, HOLESKY_GENESIS_HASH, HOODI_GENESIS_HASH, MAINNET_GENESIS_HASH,
25 SEPOLIA_GENESIS_HASH,
26 },
27 Header,
28};
29use alloy_eips::{
30 eip1559::INITIAL_BASE_FEE, eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams,
31 eip7892::BlobScheduleBlobParams, eip7928::EMPTY_BLOCK_ACCESS_LIST_HASH,
32};
33use alloy_genesis::{ChainConfig, Genesis};
34use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256};
35use alloy_trie::root::state_root_ref_unhashed;
36use core::fmt::Debug;
37use derive_more::From;
38use reth_ethereum_forks::{
39 ChainHardforks, DisplayHardforks, EthereumHardfork, EthereumHardforks, ForkCondition,
40 ForkFilter, ForkFilterKey, ForkHash, ForkId, Hardfork, Hardforks, Head, DEV_HARDFORKS,
41};
42use reth_network_peers::{holesky_nodes, hoodi_nodes, mainnet_nodes, sepolia_nodes, NodeRecord};
43use reth_primitives_traits::{sync::LazyLock, BlockHeader, SealedHeader};
44
45pub fn make_genesis_header(genesis: &Genesis, hardforks: &ChainHardforks) -> Header {
47 let base_fee_per_gas = hardforks
49 .fork(EthereumHardfork::London)
50 .active_at_block(0)
51 .then(|| genesis.base_fee_per_gas.map(|fee| fee as u64).unwrap_or(INITIAL_BASE_FEE));
52
53 let withdrawals_root = hardforks
56 .fork(EthereumHardfork::Shanghai)
57 .active_at_timestamp(genesis.timestamp)
58 .then_some(EMPTY_WITHDRAWALS);
59
60 let (parent_beacon_block_root, blob_gas_used, excess_blob_gas) =
65 if hardforks.fork(EthereumHardfork::Cancun).active_at_timestamp(genesis.timestamp) {
66 let blob_gas_used = genesis.blob_gas_used.unwrap_or(0);
67 let excess_blob_gas = genesis.excess_blob_gas.unwrap_or(0);
68 (Some(B256::ZERO), Some(blob_gas_used), Some(excess_blob_gas))
69 } else {
70 (None, None, None)
71 };
72
73 let requests_hash = hardforks
75 .fork(EthereumHardfork::Prague)
76 .active_at_timestamp(genesis.timestamp)
77 .then_some(EMPTY_REQUESTS_HASH);
78
79 let block_access_list_hash = hardforks
81 .fork(EthereumHardfork::Amsterdam)
82 .active_at_timestamp(genesis.timestamp)
83 .then_some(EMPTY_BLOCK_ACCESS_LIST_HASH);
84
85 let slot_number = hardforks
87 .fork(EthereumHardfork::Amsterdam)
88 .active_at_timestamp(genesis.timestamp)
89 .then_some(genesis.slot_number.unwrap_or(0));
90
91 Header {
92 number: genesis.number.unwrap_or_default(),
93 parent_hash: genesis.parent_hash.unwrap_or_default(),
94 gas_limit: genesis.gas_limit,
95 difficulty: genesis.difficulty,
96 nonce: genesis.nonce.into(),
97 extra_data: genesis.extra_data.clone(),
98 state_root: state_root_ref_unhashed(&genesis.alloc),
99 timestamp: genesis.timestamp,
100 mix_hash: genesis.mix_hash,
101 beneficiary: genesis.coinbase,
102 base_fee_per_gas,
103 withdrawals_root,
104 parent_beacon_block_root,
105 blob_gas_used,
106 excess_blob_gas,
107 requests_hash,
108 block_access_list_hash,
109 slot_number,
110 ..Default::default()
111 }
112}
113
114pub static MAINNET: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
116 let genesis = serde_json::from_str(include_str!("../res/genesis/mainnet.json"))
117 .expect("Can't deserialize Mainnet genesis json");
118 let hardforks = EthereumHardfork::mainnet().into();
119 let mut spec = ChainSpec {
120 chain: Chain::mainnet(),
121 genesis_header: SealedHeader::new(
122 make_genesis_header(&genesis, &hardforks),
123 MAINNET_GENESIS_HASH,
124 ),
125 genesis,
126 paris_block_and_final_difficulty: Some((
128 MAINNET_PARIS_BLOCK,
129 U256::from(58_750_003_716_598_352_816_469u128),
130 )),
131 hardforks,
132 deposit_contract: Some(MAINNET_DEPOSIT_CONTRACT),
134 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
135 prune_delete_limit: MAINNET_PRUNE_DELETE_LIMIT,
136 blob_params: BlobScheduleBlobParams::default().with_scheduled([
137 (mainnet::MAINNET_BPO1_TIMESTAMP, BlobParams::bpo1()),
138 (mainnet::MAINNET_BPO2_TIMESTAMP, BlobParams::bpo2()),
139 ]),
140 };
141 spec.genesis.config.dao_fork_support = true;
142 spec.into()
143});
144
145pub static SEPOLIA: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
147 let genesis = serde_json::from_str(include_str!("../res/genesis/sepolia.json"))
148 .expect("Can't deserialize Sepolia genesis json");
149 let hardforks = EthereumHardfork::sepolia().into();
150 let mut spec = ChainSpec {
151 chain: Chain::sepolia(),
152 genesis_header: SealedHeader::new(
153 make_genesis_header(&genesis, &hardforks),
154 SEPOLIA_GENESIS_HASH,
155 ),
156 genesis,
157 paris_block_and_final_difficulty: Some((
159 SEPOLIA_PARIS_BLOCK,
160 U256::from(17_000_018_015_853_232u128),
161 )),
162 hardforks,
163 deposit_contract: Some(DepositContract::new(
165 address!("0x7f02c3e3c98b133055b8b348b2ac625669ed295d"),
166 1273020,
167 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
168 )),
169 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
170 prune_delete_limit: 10000,
171 blob_params: BlobScheduleBlobParams::default().with_scheduled([
172 (sepolia::SEPOLIA_BPO1_TIMESTAMP, BlobParams::bpo1()),
173 (sepolia::SEPOLIA_BPO2_TIMESTAMP, BlobParams::bpo2()),
174 ]),
175 };
176 spec.genesis.config.dao_fork_support = true;
177 spec.into()
178});
179
180pub static HOLESKY: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
182 let genesis = serde_json::from_str(include_str!("../res/genesis/holesky.json"))
183 .expect("Can't deserialize Holesky genesis json");
184 let hardforks = EthereumHardfork::holesky().into();
185 let mut spec = ChainSpec {
186 chain: Chain::holesky(),
187 genesis_header: SealedHeader::new(
188 make_genesis_header(&genesis, &hardforks),
189 HOLESKY_GENESIS_HASH,
190 ),
191 genesis,
192 paris_block_and_final_difficulty: Some((0, U256::ONE)),
193 hardforks,
194 deposit_contract: Some(DepositContract::new(
195 Address::repeat_byte(0x42),
196 0,
197 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
198 )),
199 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
200 prune_delete_limit: 10000,
201 blob_params: BlobScheduleBlobParams::default().with_scheduled([
202 (holesky::HOLESKY_BPO1_TIMESTAMP, BlobParams::bpo1()),
203 (holesky::HOLESKY_BPO2_TIMESTAMP, BlobParams::bpo2()),
204 ]),
205 };
206 spec.genesis.config.dao_fork_support = true;
207 spec.into()
208});
209
210pub static HOODI: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
214 let genesis = serde_json::from_str(include_str!("../res/genesis/hoodi.json"))
215 .expect("Can't deserialize Hoodi genesis json");
216 let hardforks = EthereumHardfork::hoodi().into();
217 let mut spec = ChainSpec {
218 chain: Chain::hoodi(),
219 genesis_header: SealedHeader::new(
220 make_genesis_header(&genesis, &hardforks),
221 HOODI_GENESIS_HASH,
222 ),
223 genesis,
224 paris_block_and_final_difficulty: Some((0, U256::ZERO)),
225 hardforks,
226 deposit_contract: Some(DepositContract::new(
227 address!("0x00000000219ab540356cBB839Cbe05303d7705Fa"),
228 0,
229 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
230 )),
231 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
232 prune_delete_limit: 10000,
233 blob_params: BlobScheduleBlobParams::default().with_scheduled([
234 (hoodi::HOODI_BPO1_TIMESTAMP, BlobParams::bpo1()),
235 (hoodi::HOODI_BPO2_TIMESTAMP, BlobParams::bpo2()),
236 ]),
237 };
238 spec.genesis.config.dao_fork_support = true;
239 spec.into()
240});
241
242pub static DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
247 let genesis = serde_json::from_str(include_str!("../res/genesis/dev.json"))
248 .expect("Can't deserialize Dev testnet genesis json");
249 let hardforks = DEV_HARDFORKS.clone();
250 ChainSpec {
251 chain: Chain::dev(),
252 genesis_header: SealedHeader::seal_slow(make_genesis_header(&genesis, &hardforks)),
253 genesis,
254 paris_block_and_final_difficulty: Some((0, U256::ZERO)),
255 hardforks,
256 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
257 deposit_contract: None, ..Default::default()
259 }
260 .into()
261});
262
263pub fn create_chain_config(
266 chain: Option<Chain>,
267 hardforks: &ChainHardforks,
268 deposit_contract_address: Option<Address>,
269 blob_schedule: BTreeMap<String, BlobParams>,
270) -> ChainConfig {
271 let block_num = |fork: EthereumHardfork| hardforks.fork(fork).block_number();
273
274 let timestamp = |fork: EthereumHardfork| hardforks.fork(fork).as_timestamp();
276
277 let (terminal_total_difficulty, terminal_total_difficulty_passed) =
279 match hardforks.fork(EthereumHardfork::Paris) {
280 ForkCondition::TTD { total_difficulty, .. } => (Some(total_difficulty), true),
281 _ => (None, false),
282 };
283
284 let dao_fork_support = hardforks.fork(EthereumHardfork::Dao) != ForkCondition::Never;
286
287 ChainConfig {
288 chain_id: chain.map(|c| c.id()).unwrap_or(0),
289 homestead_block: block_num(EthereumHardfork::Homestead),
290 dao_fork_block: block_num(EthereumHardfork::Dao),
291 dao_fork_support,
292 eip150_block: block_num(EthereumHardfork::Tangerine),
293 eip155_block: block_num(EthereumHardfork::SpuriousDragon),
294 eip158_block: block_num(EthereumHardfork::SpuriousDragon),
295 byzantium_block: block_num(EthereumHardfork::Byzantium),
296 constantinople_block: block_num(EthereumHardfork::Constantinople),
297 petersburg_block: block_num(EthereumHardfork::Petersburg),
298 istanbul_block: block_num(EthereumHardfork::Istanbul),
299 muir_glacier_block: block_num(EthereumHardfork::MuirGlacier),
300 berlin_block: block_num(EthereumHardfork::Berlin),
301 london_block: block_num(EthereumHardfork::London),
302 arrow_glacier_block: block_num(EthereumHardfork::ArrowGlacier),
303 gray_glacier_block: block_num(EthereumHardfork::GrayGlacier),
304 merge_netsplit_block: None,
305 shanghai_time: timestamp(EthereumHardfork::Shanghai),
306 cancun_time: timestamp(EthereumHardfork::Cancun),
307 prague_time: timestamp(EthereumHardfork::Prague),
308 osaka_time: timestamp(EthereumHardfork::Osaka),
309 amsterdam_time: timestamp(EthereumHardfork::Amsterdam),
310 bogota_time: timestamp(EthereumHardfork::Bogota),
311 bpo1_time: timestamp(EthereumHardfork::Bpo1),
312 bpo2_time: timestamp(EthereumHardfork::Bpo2),
313 bpo3_time: timestamp(EthereumHardfork::Bpo3),
314 bpo4_time: timestamp(EthereumHardfork::Bpo4),
315 bpo5_time: timestamp(EthereumHardfork::Bpo5),
316 terminal_total_difficulty,
317 terminal_total_difficulty_passed,
318 deposit_contract_address,
319 blob_schedule,
320 ..Default::default()
321 }
322}
323
324pub fn mainnet_chain_config() -> ChainConfig {
326 let hardforks: ChainHardforks = EthereumHardfork::mainnet().into();
327 let blob_schedule = blob_params_to_schedule(&MAINNET.blob_params, &hardforks);
328 create_chain_config(
329 Some(Chain::mainnet()),
330 &hardforks,
331 Some(MAINNET_DEPOSIT_CONTRACT.address),
332 blob_schedule,
333 )
334}
335
336pub fn blob_params_to_schedule(
338 params: &BlobScheduleBlobParams,
339 hardforks: &ChainHardforks,
340) -> BTreeMap<String, BlobParams> {
341 let mut schedule = BTreeMap::new();
342 schedule.insert("cancun".to_string(), params.cancun);
343 schedule.insert("prague".to_string(), params.prague);
344 schedule.insert("osaka".to_string(), params.osaka);
345
346 let bpo_forks = EthereumHardfork::bpo_variants();
348 for (timestamp, blob_params) in ¶ms.scheduled {
349 for bpo_fork in bpo_forks {
350 if hardforks.fork(bpo_fork).as_timestamp() == Some(*timestamp) {
351 schedule.insert(bpo_fork.name().to_lowercase(), *blob_params);
352 break;
353 }
354 }
355 }
356
357 schedule
358}
359
360#[derive(Clone, Debug, PartialEq, Eq)]
363pub enum BaseFeeParamsKind {
364 Constant(BaseFeeParams),
366 Variable(ForkBaseFeeParams),
369}
370
371impl Default for BaseFeeParamsKind {
372 fn default() -> Self {
373 BaseFeeParams::ethereum().into()
374 }
375}
376
377impl From<BaseFeeParams> for BaseFeeParamsKind {
378 fn from(params: BaseFeeParams) -> Self {
379 Self::Constant(params)
380 }
381}
382
383impl From<ForkBaseFeeParams> for BaseFeeParamsKind {
384 fn from(params: ForkBaseFeeParams) -> Self {
385 Self::Variable(params)
386 }
387}
388
389#[derive(Clone, Debug, PartialEq, Eq, From)]
392pub struct ForkBaseFeeParams(Vec<(Box<dyn Hardfork>, BaseFeeParams)>);
393
394impl<H: BlockHeader> core::ops::Deref for ChainSpec<H> {
395 type Target = ChainHardforks;
396
397 fn deref(&self) -> &Self::Target {
398 &self.hardforks
399 }
400}
401
402#[derive(Debug, Clone, PartialEq, Eq)]
410pub struct ChainSpec<H: BlockHeader = Header> {
411 pub chain: Chain,
413
414 pub genesis: Genesis,
416
417 pub genesis_header: SealedHeader<H>,
419
420 pub paris_block_and_final_difficulty: Option<(u64, U256)>,
423
424 pub hardforks: ChainHardforks,
426
427 pub deposit_contract: Option<DepositContract>,
429
430 pub base_fee_params: BaseFeeParamsKind,
432
433 pub prune_delete_limit: usize,
435
436 pub blob_params: BlobScheduleBlobParams,
438}
439
440impl<H: BlockHeader> Default for ChainSpec<H> {
441 fn default() -> Self {
442 Self {
443 chain: Default::default(),
444 genesis: Default::default(),
445 genesis_header: Default::default(),
446 paris_block_and_final_difficulty: Default::default(),
447 hardforks: Default::default(),
448 deposit_contract: Default::default(),
449 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
450 prune_delete_limit: MAINNET_PRUNE_DELETE_LIMIT,
451 blob_params: Default::default(),
452 }
453 }
454}
455
456impl ChainSpec {
457 pub fn from_genesis(genesis: Genesis) -> Self {
459 genesis.into()
460 }
461
462 pub fn builder() -> ChainSpecBuilder {
464 ChainSpecBuilder::default()
465 }
466
467 pub fn from_chain_id(chain_id: u64) -> Option<Arc<Self>> {
469 match NamedChain::try_from(chain_id).ok()? {
470 NamedChain::Mainnet => Some(MAINNET.clone()),
471 NamedChain::Sepolia => Some(SEPOLIA.clone()),
472 NamedChain::Holesky => Some(HOLESKY.clone()),
473 NamedChain::Hoodi => Some(HOODI.clone()),
474 NamedChain::Dev => Some(DEV.clone()),
475 _ => None,
476 }
477 }
478}
479
480impl<H: BlockHeader> ChainSpec<H> {
481 pub const fn chain(&self) -> Chain {
483 self.chain
484 }
485
486 #[inline]
488 pub const fn is_ethereum(&self) -> bool {
489 self.chain.is_ethereum()
490 }
491
492 #[inline]
494 pub fn is_optimism_mainnet(&self) -> bool {
495 self.chain == Chain::optimism_mainnet()
496 }
497
498 #[inline]
500 pub fn paris_block(&self) -> Option<u64> {
501 self.paris_block_and_final_difficulty.map(|(block, _)| block)
502 }
503
504 pub const fn genesis(&self) -> &Genesis {
508 &self.genesis
509 }
510
511 pub fn genesis_header(&self) -> &H {
513 &self.genesis_header
514 }
515
516 pub fn sealed_genesis_header(&self) -> SealedHeader<H> {
518 SealedHeader::new(self.genesis_header().clone(), self.genesis_hash())
519 }
520
521 pub fn initial_base_fee(&self) -> Option<u64> {
523 let genesis_base_fee =
525 self.genesis.base_fee_per_gas.map(|fee| fee as u64).unwrap_or(INITIAL_BASE_FEE);
526
527 self.is_london_active_at_block(0).then_some(genesis_base_fee)
529 }
530
531 pub fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams {
533 match self.base_fee_params {
534 BaseFeeParamsKind::Constant(bf_params) => bf_params,
535 BaseFeeParamsKind::Variable(ForkBaseFeeParams(ref bf_params)) => {
536 for (fork, params) in bf_params.iter().rev() {
540 if self.hardforks.is_fork_active_at_timestamp(fork.clone(), timestamp) {
541 return *params
542 }
543 }
544
545 bf_params.first().map(|(_, params)| *params).unwrap_or_else(BaseFeeParams::ethereum)
546 }
547 }
548 }
549
550 pub fn genesis_hash(&self) -> B256 {
552 self.genesis_header.hash()
553 }
554
555 pub const fn genesis_timestamp(&self) -> u64 {
557 self.genesis.timestamp
558 }
559
560 pub fn get_final_paris_total_difficulty(&self) -> Option<U256> {
562 self.paris_block_and_final_difficulty.map(|(_, final_difficulty)| final_difficulty)
563 }
564
565 pub fn hardfork_fork_filter<HF: Hardfork + Clone>(&self, fork: HF) -> Option<ForkFilter> {
567 match self.hardforks.fork(fork.clone()) {
568 ForkCondition::Never => None,
569 _ => Some(self.fork_filter(self.satisfy(self.hardforks.fork(fork)))),
570 }
571 }
572
573 pub fn display_hardforks(&self) -> DisplayHardforks {
575 let hardforks_with_meta = self.hardforks.forks_iter().map(|(fork, condition)| {
577 let metadata = condition.as_timestamp().and_then(|timestamp| {
579 EthChainSpec::blob_params_at_timestamp(self, timestamp).map(|params| {
582 format!(
583 "blob: (target: {}, max: {}, fraction: {})",
584 params.target_blob_count, params.max_blob_count, params.update_fraction
585 )
586 })
587 });
588 (fork, condition, metadata)
589 });
590
591 DisplayHardforks::with_meta(hardforks_with_meta)
592 }
593
594 #[inline]
596 pub fn hardfork_fork_id<HF: Hardfork + Clone>(&self, fork: HF) -> Option<ForkId> {
597 let condition = self.hardforks.fork(fork);
598 match condition {
599 ForkCondition::Never => None,
600 _ => Some(self.fork_id(&self.satisfy(condition))),
601 }
602 }
603
604 #[inline]
607 pub fn shanghai_fork_id(&self) -> Option<ForkId> {
608 self.hardfork_fork_id(EthereumHardfork::Shanghai)
609 }
610
611 #[inline]
614 pub fn cancun_fork_id(&self) -> Option<ForkId> {
615 self.hardfork_fork_id(EthereumHardfork::Cancun)
616 }
617
618 #[inline]
621 pub fn latest_fork_id(&self) -> ForkId {
622 self.hardfork_fork_id(self.hardforks.last().unwrap().0).unwrap()
623 }
624
625 pub fn fork_filter(&self, head: Head) -> ForkFilter {
627 let forks = self.hardforks.forks_iter().filter_map(|(_, condition)| {
628 Some(match condition {
631 ForkCondition::Block(block) |
632 ForkCondition::TTD { fork_block: Some(block), .. } => ForkFilterKey::Block(block),
633 ForkCondition::Timestamp(time) => ForkFilterKey::Time(time),
634 _ => return None,
635 })
636 });
637
638 ForkFilter::new(head, self.genesis_hash(), self.genesis_timestamp(), forks)
639 }
640
641 pub fn fork_id(&self, head: &Head) -> ForkId {
653 let mut forkhash = ForkHash::from(self.genesis_hash());
654
655 let mut current_applied = 0;
661
662 for (_, cond) in self.hardforks.forks_iter() {
664 if let ForkCondition::Block(block) |
667 ForkCondition::TTD { fork_block: Some(block), .. } = cond
668 {
669 if head.number >= block {
670 if block != current_applied {
672 forkhash += block;
673 current_applied = block;
674 }
675 } else {
676 return ForkId { hash: forkhash, next: block }
679 }
680 }
681 }
682
683 for timestamp in self.hardforks.forks_iter().filter_map(|(_, cond)| {
687 cond.as_timestamp().filter(|time| time > &self.genesis.timestamp)
689 }) {
690 if head.timestamp >= timestamp {
691 if timestamp != current_applied {
693 forkhash += timestamp;
694 current_applied = timestamp;
695 }
696 } else {
697 return ForkId { hash: forkhash, next: timestamp }
701 }
702 }
703
704 ForkId { hash: forkhash, next: 0 }
705 }
706
707 pub(crate) fn satisfy(&self, cond: ForkCondition) -> Head {
713 match cond {
714 ForkCondition::Block(number) => Head { number, ..Default::default() },
715 ForkCondition::Timestamp(timestamp) => {
716 Head {
719 timestamp,
720 number: self.last_block_fork_before_merge_or_timestamp().unwrap_or_default(),
721 ..Default::default()
722 }
723 }
724 ForkCondition::TTD { total_difficulty, fork_block, .. } => Head {
725 total_difficulty,
726 number: fork_block.unwrap_or_default(),
727 ..Default::default()
728 },
729 ForkCondition::Never => unreachable!(),
730 }
731 }
732
733 pub(crate) fn last_block_fork_before_merge_or_timestamp(&self) -> Option<u64> {
745 let mut hardforks_iter = self.hardforks.forks_iter().peekable();
746 while let Some((_, curr_cond)) = hardforks_iter.next() {
747 if let Some((_, next_cond)) = hardforks_iter.peek() {
748 match next_cond {
752 ForkCondition::TTD { fork_block: Some(block), .. } => return Some(*block),
755
756 ForkCondition::TTD { .. } | ForkCondition::Timestamp(_) => {
759 if let ForkCondition::Block(block_num) = curr_cond {
762 return Some(block_num);
763 }
764 }
765 ForkCondition::Block(_) | ForkCondition::Never => {}
766 }
767 }
768 }
769 None
770 }
771
772 pub fn bootnodes(&self) -> Option<Vec<NodeRecord>> {
774 use NamedChain as C;
775
776 match self.chain.try_into().ok()? {
777 C::Mainnet => Some(mainnet_nodes()),
778 C::Sepolia => Some(sepolia_nodes()),
779 C::Holesky => Some(holesky_nodes()),
780 C::Hoodi => Some(hoodi_nodes()),
781 _ => None,
782 }
783 }
784
785 pub fn map_header<NewH: BlockHeader>(self, f: impl FnOnce(H) -> NewH) -> ChainSpec<NewH> {
787 let Self {
788 chain,
789 genesis,
790 genesis_header,
791 paris_block_and_final_difficulty,
792 hardforks,
793 deposit_contract,
794 base_fee_params,
795 prune_delete_limit,
796 blob_params,
797 } = self;
798 ChainSpec {
799 chain,
800 genesis,
801 genesis_header: SealedHeader::new_unhashed(f(genesis_header.into_header())),
802 paris_block_and_final_difficulty,
803 hardforks,
804 deposit_contract,
805 base_fee_params,
806 prune_delete_limit,
807 blob_params,
808 }
809 }
810}
811
812impl From<Genesis> for ChainSpec {
813 fn from(genesis: Genesis) -> Self {
814 let hardfork_opts = [
816 (EthereumHardfork::Frontier.boxed(), Some(0)),
817 (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block),
818 (EthereumHardfork::Dao.boxed(), genesis.config.dao_fork_block),
819 (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block),
820 (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block),
821 (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block),
822 (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block),
823 (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block),
824 (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block),
825 (EthereumHardfork::MuirGlacier.boxed(), genesis.config.muir_glacier_block),
826 (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block),
827 (EthereumHardfork::London.boxed(), genesis.config.london_block),
828 (EthereumHardfork::ArrowGlacier.boxed(), genesis.config.arrow_glacier_block),
829 (EthereumHardfork::GrayGlacier.boxed(), genesis.config.gray_glacier_block),
830 ];
831 let mut hardforks = hardfork_opts
832 .into_iter()
833 .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block))))
834 .collect::<Vec<_>>();
835
836 let paris_block_and_final_difficulty = if let Some(ttd) =
840 genesis.config.terminal_total_difficulty
841 {
842 hardforks.push((
843 EthereumHardfork::Paris.boxed(),
844 ForkCondition::TTD {
845 activation_block_number: genesis
848 .config
849 .merge_netsplit_block
850 .or_else(|| {
851 match genesis.config.chain_id {
859 1 if ttd == MAINNET_PARIS_TTD => return Some(MAINNET_PARIS_BLOCK),
860 11155111 if ttd == SEPOLIA_PARIS_TTD => {
861 return Some(SEPOLIA_PARIS_BLOCK)
862 }
863 _ => {}
864 };
865 None
866 })
867 .unwrap_or_default(),
868 total_difficulty: ttd,
869 fork_block: genesis.config.merge_netsplit_block,
870 },
871 ));
872
873 genesis.config.merge_netsplit_block.map(|block| (block, ttd))
874 } else {
875 None
876 };
877
878 let time_hardfork_opts = [
880 (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time),
881 (EthereumHardfork::Cancun.boxed(), genesis.config.cancun_time),
882 (EthereumHardfork::Prague.boxed(), genesis.config.prague_time),
883 (EthereumHardfork::Osaka.boxed(), genesis.config.osaka_time),
884 (EthereumHardfork::Bpo1.boxed(), genesis.config.bpo1_time),
885 (EthereumHardfork::Bpo2.boxed(), genesis.config.bpo2_time),
886 (EthereumHardfork::Bpo3.boxed(), genesis.config.bpo3_time),
887 (EthereumHardfork::Bpo4.boxed(), genesis.config.bpo4_time),
888 (EthereumHardfork::Bpo5.boxed(), genesis.config.bpo5_time),
889 (EthereumHardfork::Amsterdam.boxed(), genesis.config.amsterdam_time),
890 (EthereumHardfork::Bogota.boxed(), genesis.config.bogota_time),
891 ];
892
893 let mut time_hardforks = time_hardfork_opts
894 .into_iter()
895 .filter_map(|(hardfork, opt)| {
896 opt.map(|time| (hardfork, ForkCondition::Timestamp(time)))
897 })
898 .collect::<Vec<_>>();
899
900 hardforks.append(&mut time_hardforks);
901
902 let mainnet_hardforks: ChainHardforks = EthereumHardfork::mainnet().into();
904 let mainnet_order = mainnet_hardforks.forks_iter();
905
906 let mut ordered_hardforks = Vec::with_capacity(hardforks.len());
907 for (hardfork, _) in mainnet_order {
908 if let Some(pos) = hardforks.iter().position(|(e, _)| **e == *hardfork) {
909 ordered_hardforks.push(hardforks.remove(pos));
910 }
911 }
912
913 ordered_hardforks.append(&mut hardforks);
915
916 let blob_params = genesis.config.blob_schedule_blob_params();
918
919 let deposit_contract = genesis.config.deposit_contract_address.map(|address| {
924 DepositContract { address, block: 0, topic: MAINNET_DEPOSIT_CONTRACT.topic }
925 });
926
927 let hardforks = ChainHardforks::new(ordered_hardforks);
928
929 Self {
930 chain: genesis.config.chain_id.into(),
931 genesis_header: SealedHeader::new_unhashed(make_genesis_header(&genesis, &hardforks)),
932 genesis,
933 hardforks,
934 paris_block_and_final_difficulty,
935 deposit_contract,
936 blob_params,
937 ..Default::default()
938 }
939 }
940}
941
942impl<H: BlockHeader> Hardforks for ChainSpec<H> {
943 fn fork<HF: Hardfork>(&self, fork: HF) -> ForkCondition {
944 self.hardforks.fork(fork)
945 }
946
947 fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
948 self.hardforks.forks_iter()
949 }
950
951 fn fork_id(&self, head: &Head) -> ForkId {
952 self.fork_id(head)
953 }
954
955 fn latest_fork_id(&self) -> ForkId {
956 self.latest_fork_id()
957 }
958
959 fn fork_filter(&self, head: Head) -> ForkFilter {
960 self.fork_filter(head)
961 }
962}
963
964impl<H: BlockHeader> EthereumHardforks for ChainSpec<H> {
965 fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition {
966 self.fork(fork)
967 }
968}
969
970#[auto_impl::auto_impl(&, Arc)]
972pub trait ChainSpecProvider: Debug + Send {
973 type ChainSpec: EthChainSpec + 'static;
975
976 fn chain_spec(&self) -> Arc<Self::ChainSpec>;
978}
979
980#[derive(Debug, Default, Clone)]
982pub struct ChainSpecBuilder {
983 chain: Option<Chain>,
984 genesis: Option<Genesis>,
985 hardforks: ChainHardforks,
986}
987
988impl ChainSpecBuilder {
989 pub fn mainnet() -> Self {
991 Self {
992 chain: Some(MAINNET.chain),
993 genesis: Some(MAINNET.genesis.clone()),
994 hardforks: MAINNET.hardforks.clone(),
995 }
996 }
997}
998
999impl ChainSpecBuilder {
1000 pub const fn chain(mut self, chain: Chain) -> Self {
1002 self.chain = Some(chain);
1003 self
1004 }
1005
1006 pub fn reset(mut self) -> Self {
1008 self.hardforks = ChainHardforks::default();
1009 self
1010 }
1011
1012 pub fn genesis(mut self, genesis: Genesis) -> Self {
1014 self.genesis = Some(genesis);
1015 self
1016 }
1017
1018 pub fn with_fork<H: Hardfork>(mut self, fork: H, condition: ForkCondition) -> Self {
1020 self.hardforks.insert(fork, condition);
1021 self
1022 }
1023
1024 pub fn with_forks(mut self, forks: ChainHardforks) -> Self {
1026 self.hardforks = forks;
1027 self
1028 }
1029
1030 pub fn without_fork<H: Hardfork>(mut self, fork: H) -> Self {
1032 self.hardforks.remove(&fork);
1033 self
1034 }
1035
1036 pub fn paris_at_ttd(self, ttd: U256, activation_block_number: BlockNumber) -> Self {
1040 self.with_fork(
1041 EthereumHardfork::Paris,
1042 ForkCondition::TTD { activation_block_number, total_difficulty: ttd, fork_block: None },
1043 )
1044 }
1045
1046 pub fn frontier_activated(mut self) -> Self {
1048 self.hardforks.insert(EthereumHardfork::Frontier, ForkCondition::Block(0));
1049 self
1050 }
1051
1052 pub fn dao_activated(mut self) -> Self {
1054 self = self.frontier_activated();
1055 self.hardforks.insert(EthereumHardfork::Dao, ForkCondition::Block(0));
1056 self
1057 }
1058
1059 pub fn homestead_activated(mut self) -> Self {
1061 self = self.dao_activated();
1062 self.hardforks.insert(EthereumHardfork::Homestead, ForkCondition::Block(0));
1063 self
1064 }
1065
1066 pub fn tangerine_whistle_activated(mut self) -> Self {
1068 self = self.homestead_activated();
1069 self.hardforks.insert(EthereumHardfork::Tangerine, ForkCondition::Block(0));
1070 self
1071 }
1072
1073 pub fn spurious_dragon_activated(mut self) -> Self {
1075 self = self.tangerine_whistle_activated();
1076 self.hardforks.insert(EthereumHardfork::SpuriousDragon, ForkCondition::Block(0));
1077 self
1078 }
1079
1080 pub fn byzantium_activated(mut self) -> Self {
1082 self = self.spurious_dragon_activated();
1083 self.hardforks.insert(EthereumHardfork::Byzantium, ForkCondition::Block(0));
1084 self
1085 }
1086
1087 pub fn constantinople_activated(mut self) -> Self {
1089 self = self.byzantium_activated();
1090 self.hardforks.insert(EthereumHardfork::Constantinople, ForkCondition::Block(0));
1091 self
1092 }
1093
1094 pub fn petersburg_activated(mut self) -> Self {
1096 self = self.constantinople_activated();
1097 self.hardforks.insert(EthereumHardfork::Petersburg, ForkCondition::Block(0));
1098 self
1099 }
1100
1101 pub fn istanbul_activated(mut self) -> Self {
1103 self = self.petersburg_activated();
1104 self.hardforks.insert(EthereumHardfork::Istanbul, ForkCondition::Block(0));
1105 self
1106 }
1107
1108 pub fn muirglacier_activated(mut self) -> Self {
1110 self = self.istanbul_activated();
1111 self.hardforks.insert(EthereumHardfork::MuirGlacier, ForkCondition::Block(0));
1112 self
1113 }
1114
1115 pub fn berlin_activated(mut self) -> Self {
1117 self = self.muirglacier_activated();
1118 self.hardforks.insert(EthereumHardfork::Berlin, ForkCondition::Block(0));
1119 self
1120 }
1121
1122 pub fn london_activated(mut self) -> Self {
1124 self = self.berlin_activated();
1125 self.hardforks.insert(EthereumHardfork::London, ForkCondition::Block(0));
1126 self
1127 }
1128
1129 pub fn arrowglacier_activated(mut self) -> Self {
1131 self = self.london_activated();
1132 self.hardforks.insert(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0));
1133 self
1134 }
1135
1136 pub fn grayglacier_activated(mut self) -> Self {
1138 self = self.arrowglacier_activated();
1139 self.hardforks.insert(EthereumHardfork::GrayGlacier, ForkCondition::Block(0));
1140 self
1141 }
1142
1143 pub fn paris_activated(mut self) -> Self {
1145 self = self.grayglacier_activated();
1146 self.hardforks.insert(
1147 EthereumHardfork::Paris,
1148 ForkCondition::TTD {
1149 activation_block_number: 0,
1150 total_difficulty: U256::ZERO,
1151 fork_block: None,
1152 },
1153 );
1154 self
1155 }
1156
1157 pub fn shanghai_activated(mut self) -> Self {
1159 self = self.paris_activated();
1160 self.hardforks.insert(EthereumHardfork::Shanghai, ForkCondition::Timestamp(0));
1161 self
1162 }
1163
1164 pub fn cancun_activated(mut self) -> Self {
1166 self = self.shanghai_activated();
1167 self.hardforks.insert(EthereumHardfork::Cancun, ForkCondition::Timestamp(0));
1168 self
1169 }
1170
1171 pub fn prague_activated(mut self) -> Self {
1173 self = self.cancun_activated();
1174 self.hardforks.insert(EthereumHardfork::Prague, ForkCondition::Timestamp(0));
1175 self
1176 }
1177
1178 pub fn with_prague_at(mut self, timestamp: u64) -> Self {
1180 self.hardforks.insert(EthereumHardfork::Prague, ForkCondition::Timestamp(timestamp));
1181 self
1182 }
1183
1184 pub fn osaka_activated(mut self) -> Self {
1186 self = self.prague_activated();
1187 self.hardforks.insert(EthereumHardfork::Osaka, ForkCondition::Timestamp(0));
1188 self
1189 }
1190
1191 pub fn with_osaka_at(mut self, timestamp: u64) -> Self {
1193 self.hardforks.insert(EthereumHardfork::Osaka, ForkCondition::Timestamp(timestamp));
1194 self
1195 }
1196
1197 pub fn amsterdam_activated(mut self) -> Self {
1199 self = self.osaka_activated();
1200 self.hardforks.insert(EthereumHardfork::Amsterdam, ForkCondition::Timestamp(0));
1201 self
1202 }
1203
1204 pub fn with_amsterdam_at(mut self, timestamp: u64) -> Self {
1206 self.hardforks.insert(EthereumHardfork::Amsterdam, ForkCondition::Timestamp(timestamp));
1207 self
1208 }
1209
1210 pub fn bogota_activated(mut self) -> Self {
1212 self = self.amsterdam_activated();
1213 self.hardforks.insert(EthereumHardfork::Bogota, ForkCondition::Timestamp(0));
1214 self
1215 }
1216
1217 pub fn with_bogota_at(mut self, timestamp: u64) -> Self {
1219 self.hardforks.insert(EthereumHardfork::Bogota, ForkCondition::Timestamp(timestamp));
1220 self
1221 }
1222
1223 pub fn build(self) -> ChainSpec {
1230 let paris_block_and_final_difficulty = {
1231 self.hardforks.get(EthereumHardfork::Paris).and_then(|cond| {
1232 if let ForkCondition::TTD { total_difficulty, activation_block_number, .. } = cond {
1233 Some((activation_block_number, total_difficulty))
1234 } else {
1235 None
1236 }
1237 })
1238 };
1239 let genesis = self.genesis.expect("The genesis is required");
1240 ChainSpec {
1241 chain: self.chain.expect("The chain is required"),
1242 genesis_header: SealedHeader::new_unhashed(make_genesis_header(
1243 &genesis,
1244 &self.hardforks,
1245 )),
1246 genesis,
1247 hardforks: self.hardforks,
1248 paris_block_and_final_difficulty,
1249 deposit_contract: None,
1250 ..Default::default()
1251 }
1252 }
1253}
1254
1255impl From<&Arc<ChainSpec>> for ChainSpecBuilder {
1256 fn from(value: &Arc<ChainSpec>) -> Self {
1257 Self {
1258 chain: Some(value.chain),
1259 genesis: Some(value.genesis.clone()),
1260 hardforks: value.hardforks.clone(),
1261 }
1262 }
1263}
1264
1265impl<H: BlockHeader> EthExecutorSpec for ChainSpec<H> {
1266 fn deposit_contract_address(&self) -> Option<Address> {
1267 self.deposit_contract.map(|deposit_contract| deposit_contract.address)
1268 }
1269}
1270
1271#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1273pub struct DepositContract {
1274 pub address: Address,
1276 pub block: BlockNumber,
1278 pub topic: B256,
1280}
1281
1282impl DepositContract {
1283 pub const fn new(address: Address, block: BlockNumber, topic: B256) -> Self {
1285 Self { address, block, topic }
1286 }
1287}
1288
1289#[cfg(any(test, feature = "test-utils"))]
1291pub fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) {
1292 for (block, expected_id) in cases {
1293 let computed_id = spec.fork_id(block);
1294 assert_eq!(
1295 expected_id, &computed_id,
1296 "Expected fork ID {:?}, computed fork ID {:?} at block {}",
1297 expected_id, computed_id, block.number
1298 );
1299 }
1300}
1301
1302#[cfg(test)]
1303mod tests {
1304
1305 use super::*;
1306 use alloy_chains::Chain;
1307 use alloy_consensus::constants::ETH_TO_WEI;
1308 use alloy_eips::{eip4844::BLOB_TX_MIN_BLOB_GASPRICE, eip7840::BlobParams};
1309 use alloy_evm::block::calc::{base_block_reward, block_reward};
1310 use alloy_genesis::{ChainConfig, GenesisAccount};
1311 use alloy_primitives::{b256, hex};
1312 use alloy_trie::{TrieAccount, EMPTY_ROOT_HASH};
1313 use core::ops::Deref;
1314 use reth_ethereum_forks::{ForkCondition, ForkHash, ForkId, Head};
1315 use std::collections::HashMap;
1316
1317 fn test_hardfork_fork_ids(spec: &ChainSpec, cases: &[(EthereumHardfork, ForkId)]) {
1318 for (hardfork, expected_id) in cases {
1319 if let Some(computed_id) = spec.hardfork_fork_id(*hardfork) {
1320 assert_eq!(
1321 expected_id, &computed_id,
1322 "Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for hardfork {hardfork}"
1323 );
1324 if matches!(hardfork, EthereumHardfork::Shanghai) {
1325 if let Some(shanghai_id) = spec.shanghai_fork_id() {
1326 assert_eq!(
1327 expected_id, &shanghai_id,
1328 "Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for Shanghai hardfork"
1329 );
1330 } else {
1331 panic!("Expected ForkCondition to return Some for Hardfork::Shanghai");
1332 }
1333 }
1334 }
1335 }
1336 }
1337
1338 #[test]
1339 fn test_hardfork_list_display_mainnet() {
1340 assert_eq!(
1341 MAINNET.display_hardforks().to_string(),
1342 "Pre-merge hard forks (block based):
1343- Frontier @0
1344- Homestead @1150000
1345- Dao @1920000
1346- Tangerine @2463000
1347- SpuriousDragon @2675000
1348- Byzantium @4370000
1349- Constantinople @7280000
1350- Petersburg @7280000
1351- Istanbul @9069000
1352- MuirGlacier @9200000
1353- Berlin @12244000
1354- London @12965000
1355- ArrowGlacier @13773000
1356- GrayGlacier @15050000
1357Merge hard forks:
1358- Paris @58750000000000000000000 (network is known to be merged)
1359Post-merge hard forks (timestamp based):
1360- Shanghai @1681338455
1361- Cancun @1710338135 blob: (target: 3, max: 6, fraction: 3338477)
1362- Prague @1746612311 blob: (target: 6, max: 9, fraction: 5007716)
1363- Osaka @1764798551 blob: (target: 6, max: 9, fraction: 5007716)
1364- Bpo1 @1765290071 blob: (target: 10, max: 15, fraction: 8346193)
1365- Bpo2 @1767747671 blob: (target: 14, max: 21, fraction: 11684671)"
1366 );
1367 }
1368
1369 #[test]
1370 fn test_hardfork_list_ignores_disabled_forks() {
1371 let spec = ChainSpec::builder()
1372 .chain(Chain::mainnet())
1373 .genesis(Genesis::default())
1374 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1375 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Never)
1376 .build();
1377 assert_eq!(
1378 spec.display_hardforks().to_string(),
1379 "Pre-merge hard forks (block based):
1380- Frontier @0"
1381 );
1382 }
1383
1384 #[test]
1386 fn ignores_genesis_fork_blocks() {
1387 let spec = ChainSpec::builder()
1388 .chain(Chain::mainnet())
1389 .genesis(Genesis::default())
1390 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1391 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(0))
1392 .with_fork(EthereumHardfork::Tangerine, ForkCondition::Block(0))
1393 .with_fork(EthereumHardfork::SpuriousDragon, ForkCondition::Block(0))
1394 .with_fork(EthereumHardfork::Byzantium, ForkCondition::Block(0))
1395 .with_fork(EthereumHardfork::Constantinople, ForkCondition::Block(0))
1396 .with_fork(EthereumHardfork::Istanbul, ForkCondition::Block(0))
1397 .with_fork(EthereumHardfork::MuirGlacier, ForkCondition::Block(0))
1398 .with_fork(EthereumHardfork::Berlin, ForkCondition::Block(0))
1399 .with_fork(EthereumHardfork::London, ForkCondition::Block(0))
1400 .with_fork(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0))
1401 .with_fork(EthereumHardfork::GrayGlacier, ForkCondition::Block(0))
1402 .build();
1403
1404 assert_eq!(spec.deref().len(), 12, "12 forks should be active.");
1405 assert_eq!(
1406 spec.fork_id(&Head { number: 1, ..Default::default() }),
1407 ForkId { hash: ForkHash::from(spec.genesis_hash()), next: 0 },
1408 "the fork ID should be the genesis hash; forks at genesis are ignored for fork filters"
1409 );
1410 }
1411
1412 #[test]
1413 fn ignores_duplicate_fork_blocks() {
1414 let empty_genesis = Genesis::default();
1415 let unique_spec = ChainSpec::builder()
1416 .chain(Chain::mainnet())
1417 .genesis(empty_genesis.clone())
1418 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1419 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(1))
1420 .build();
1421
1422 let duplicate_spec = ChainSpec::builder()
1423 .chain(Chain::mainnet())
1424 .genesis(empty_genesis)
1425 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1426 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(1))
1427 .with_fork(EthereumHardfork::Tangerine, ForkCondition::Block(1))
1428 .build();
1429
1430 assert_eq!(
1431 unique_spec.fork_id(&Head { number: 2, ..Default::default() }),
1432 duplicate_spec.fork_id(&Head { number: 2, ..Default::default() }),
1433 "duplicate fork blocks should be deduplicated for fork filters"
1434 );
1435 }
1436
1437 #[test]
1438 fn test_chainspec_satisfy() {
1439 let empty_genesis = Genesis::default();
1440 let happy_path_case = ChainSpec::builder()
1442 .chain(Chain::mainnet())
1443 .genesis(empty_genesis.clone())
1444 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1445 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1446 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1447 .build();
1448 let happy_path_head = happy_path_case.satisfy(ForkCondition::Timestamp(11313123));
1449 let happy_path_expected = Head { number: 73, timestamp: 11313123, ..Default::default() };
1450 assert_eq!(
1451 happy_path_head, happy_path_expected,
1452 "expected satisfy() to return {happy_path_expected:#?}, but got {happy_path_head:#?} "
1453 );
1454 let multiple_timestamp_fork_case = ChainSpec::builder()
1456 .chain(Chain::mainnet())
1457 .genesis(empty_genesis.clone())
1458 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1459 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1460 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1461 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(11313398))
1462 .build();
1463 let multi_timestamp_head =
1464 multiple_timestamp_fork_case.satisfy(ForkCondition::Timestamp(11313398));
1465 let mult_timestamp_expected =
1466 Head { number: 73, timestamp: 11313398, ..Default::default() };
1467 assert_eq!(
1468 multi_timestamp_head, mult_timestamp_expected,
1469 "expected satisfy() to return {mult_timestamp_expected:#?}, but got {multi_timestamp_head:#?} "
1470 );
1471 let no_block_fork_case = ChainSpec::builder()
1473 .chain(Chain::mainnet())
1474 .genesis(empty_genesis.clone())
1475 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1476 .build();
1477 let no_block_fork_head = no_block_fork_case.satisfy(ForkCondition::Timestamp(11313123));
1478 let no_block_fork_expected = Head { number: 0, timestamp: 11313123, ..Default::default() };
1479 assert_eq!(
1480 no_block_fork_head, no_block_fork_expected,
1481 "expected satisfy() to return {no_block_fork_expected:#?}, but got {no_block_fork_head:#?} ",
1482 );
1483 let fork_cond_ttd_blocknum_case = ChainSpec::builder()
1485 .chain(Chain::mainnet())
1486 .genesis(empty_genesis.clone())
1487 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1488 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1489 .with_fork(
1490 EthereumHardfork::Paris,
1491 ForkCondition::TTD {
1492 activation_block_number: 101,
1493 fork_block: Some(101),
1494 total_difficulty: U256::from(10_790_000),
1495 },
1496 )
1497 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1498 .build();
1499 let fork_cond_ttd_blocknum_head =
1500 fork_cond_ttd_blocknum_case.satisfy(ForkCondition::Timestamp(11313123));
1501 let fork_cond_ttd_blocknum_expected =
1502 Head { number: 101, timestamp: 11313123, ..Default::default() };
1503 assert_eq!(
1504 fork_cond_ttd_blocknum_head, fork_cond_ttd_blocknum_expected,
1505 "expected satisfy() to return {fork_cond_ttd_blocknum_expected:#?}, but got {fork_cond_ttd_blocknum_head:#?} ",
1506 );
1507
1508 let fork_cond_block_only_case = ChainSpec::builder()
1512 .chain(Chain::mainnet())
1513 .genesis(empty_genesis)
1514 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1515 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1516 .build();
1517 let fork_cond_block_only_head = fork_cond_block_only_case.satisfy(ForkCondition::Block(73));
1518 let fork_cond_block_only_expected = Head { number: 73, ..Default::default() };
1519 assert_eq!(
1520 fork_cond_block_only_head, fork_cond_block_only_expected,
1521 "expected satisfy() to return {fork_cond_block_only_expected:#?}, but got {fork_cond_block_only_head:#?} ",
1522 );
1523 let fork_cond_ttd_no_new_spec = fork_cond_block_only_case.satisfy(ForkCondition::TTD {
1526 activation_block_number: 101,
1527 fork_block: None,
1528 total_difficulty: U256::from(10_790_000),
1529 });
1530 let fork_cond_ttd_no_new_spec_expected =
1531 Head { total_difficulty: U256::from(10_790_000), ..Default::default() };
1532 assert_eq!(
1533 fork_cond_ttd_no_new_spec, fork_cond_ttd_no_new_spec_expected,
1534 "expected satisfy() to return {fork_cond_ttd_no_new_spec_expected:#?}, but got {fork_cond_ttd_no_new_spec:#?} ",
1535 );
1536 }
1537
1538 #[test]
1539 fn mainnet_hardfork_fork_ids() {
1540 test_hardfork_fork_ids(
1541 &MAINNET,
1542 &[
1543 (
1544 EthereumHardfork::Frontier,
1545 ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
1546 ),
1547 (
1548 EthereumHardfork::Homestead,
1549 ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
1550 ),
1551 (
1552 EthereumHardfork::Dao,
1553 ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
1554 ),
1555 (
1556 EthereumHardfork::Tangerine,
1557 ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
1558 ),
1559 (
1560 EthereumHardfork::SpuriousDragon,
1561 ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
1562 ),
1563 (
1564 EthereumHardfork::Byzantium,
1565 ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
1566 ),
1567 (
1568 EthereumHardfork::Constantinople,
1569 ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
1570 ),
1571 (
1572 EthereumHardfork::Petersburg,
1573 ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
1574 ),
1575 (
1576 EthereumHardfork::Istanbul,
1577 ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
1578 ),
1579 (
1580 EthereumHardfork::MuirGlacier,
1581 ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
1582 ),
1583 (
1584 EthereumHardfork::Berlin,
1585 ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
1586 ),
1587 (
1588 EthereumHardfork::London,
1589 ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
1590 ),
1591 (
1592 EthereumHardfork::ArrowGlacier,
1593 ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
1594 ),
1595 (
1596 EthereumHardfork::GrayGlacier,
1597 ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
1598 ),
1599 (
1600 EthereumHardfork::Shanghai,
1601 ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
1602 ),
1603 (
1604 EthereumHardfork::Cancun,
1605 ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
1606 ),
1607 (
1608 EthereumHardfork::Prague,
1609 ForkId {
1610 hash: ForkHash(hex!("0xc376cf8b")),
1611 next: mainnet::MAINNET_OSAKA_TIMESTAMP,
1612 },
1613 ),
1614 ],
1615 );
1616 }
1617
1618 #[test]
1619 fn sepolia_hardfork_fork_ids() {
1620 test_hardfork_fork_ids(
1621 &SEPOLIA,
1622 &[
1623 (
1624 EthereumHardfork::Frontier,
1625 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1626 ),
1627 (
1628 EthereumHardfork::Homestead,
1629 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1630 ),
1631 (
1632 EthereumHardfork::Tangerine,
1633 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1634 ),
1635 (
1636 EthereumHardfork::SpuriousDragon,
1637 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1638 ),
1639 (
1640 EthereumHardfork::Byzantium,
1641 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1642 ),
1643 (
1644 EthereumHardfork::Constantinople,
1645 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1646 ),
1647 (
1648 EthereumHardfork::Petersburg,
1649 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1650 ),
1651 (
1652 EthereumHardfork::Istanbul,
1653 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1654 ),
1655 (
1656 EthereumHardfork::Berlin,
1657 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1658 ),
1659 (
1660 EthereumHardfork::London,
1661 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1662 ),
1663 (
1664 EthereumHardfork::Paris,
1665 ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
1666 ),
1667 (
1668 EthereumHardfork::Shanghai,
1669 ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
1670 ),
1671 (
1672 EthereumHardfork::Cancun,
1673 ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
1674 ),
1675 (
1676 EthereumHardfork::Prague,
1677 ForkId {
1678 hash: ForkHash(hex!("0xed88b5fd")),
1679 next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
1680 },
1681 ),
1682 (
1683 EthereumHardfork::Osaka,
1684 ForkId {
1685 hash: ForkHash(hex!("0xe2ae4999")),
1686 next: sepolia::SEPOLIA_BPO1_TIMESTAMP,
1687 },
1688 ),
1689 (
1690 EthereumHardfork::Bpo1,
1691 ForkId {
1692 hash: ForkHash(hex!("0x56078a1e")),
1693 next: sepolia::SEPOLIA_BPO2_TIMESTAMP,
1694 },
1695 ),
1696 (
1697 EthereumHardfork::Bpo2,
1698 ForkId {
1699 hash: ForkHash(hex!("0x268956b6")),
1700 next: sepolia::SEPOLIA_AMSTERDAM_TIMESTAMP,
1701 },
1702 ),
1703 (
1704 EthereumHardfork::Amsterdam,
1705 ForkId { hash: ForkHash(hex!("0x6c1d9423")), next: 0 },
1706 ),
1707 ],
1708 );
1709 }
1710
1711 #[test]
1712 fn mainnet_fork_ids() {
1713 test_fork_ids(
1714 &MAINNET,
1715 &[
1716 (
1717 Head { number: 0, ..Default::default() },
1718 ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
1719 ),
1720 (
1721 Head { number: 1150000, ..Default::default() },
1722 ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
1723 ),
1724 (
1725 Head { number: 1920000, ..Default::default() },
1726 ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
1727 ),
1728 (
1729 Head { number: 2463000, ..Default::default() },
1730 ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
1731 ),
1732 (
1733 Head { number: 2675000, ..Default::default() },
1734 ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
1735 ),
1736 (
1737 Head { number: 4370000, ..Default::default() },
1738 ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
1739 ),
1740 (
1741 Head { number: 7280000, ..Default::default() },
1742 ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
1743 ),
1744 (
1745 Head { number: 9069000, ..Default::default() },
1746 ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
1747 ),
1748 (
1749 Head { number: 9200000, ..Default::default() },
1750 ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
1751 ),
1752 (
1753 Head { number: 12244000, ..Default::default() },
1754 ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
1755 ),
1756 (
1757 Head { number: 12965000, ..Default::default() },
1758 ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
1759 ),
1760 (
1761 Head { number: 13773000, ..Default::default() },
1762 ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
1763 ),
1764 (
1765 Head { number: 15050000, ..Default::default() },
1766 ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
1767 ),
1768 (
1770 Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
1771 ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
1772 ),
1773 (
1775 Head { number: 20000001, timestamp: 1710338135, ..Default::default() },
1776 ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
1777 ),
1778 (
1780 Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
1781 ForkId {
1782 hash: ForkHash(hex!("0xc376cf8b")),
1783 next: mainnet::MAINNET_OSAKA_TIMESTAMP,
1784 },
1785 ),
1786 (
1788 Head {
1789 number: 20000004,
1790 timestamp: mainnet::MAINNET_OSAKA_TIMESTAMP,
1791 ..Default::default()
1792 },
1793 ForkId {
1794 hash: ForkHash(hex!("0x5167e2a6")),
1795 next: mainnet::MAINNET_BPO1_TIMESTAMP,
1796 },
1797 ),
1798 ],
1799 );
1800 }
1801
1802 #[test]
1803 fn hoodi_fork_ids() {
1804 test_fork_ids(
1805 &HOODI,
1806 &[
1807 (
1808 Head { number: 0, ..Default::default() },
1809 ForkId { hash: ForkHash(hex!("0xbef71d30")), next: 1742999832 },
1810 ),
1811 (
1813 Head { number: 0, timestamp: 1742999833, ..Default::default() },
1814 ForkId {
1815 hash: ForkHash(hex!("0x0929e24e")),
1816 next: hoodi::HOODI_OSAKA_TIMESTAMP,
1817 },
1818 ),
1819 (
1821 Head {
1822 number: 0,
1823 timestamp: hoodi::HOODI_OSAKA_TIMESTAMP,
1824 ..Default::default()
1825 },
1826 ForkId {
1827 hash: ForkHash(hex!("0xe7e0e7ff")),
1828 next: hoodi::HOODI_BPO1_TIMESTAMP,
1829 },
1830 ),
1831 (
1833 Head {
1834 number: 0,
1835 timestamp: hoodi::HOODI_BPO1_TIMESTAMP,
1836 ..Default::default()
1837 },
1838 ForkId {
1839 hash: ForkHash(hex!("0x3893353e")),
1840 next: hoodi::HOODI_BPO2_TIMESTAMP,
1841 },
1842 ),
1843 (
1845 Head {
1846 number: 0,
1847 timestamp: hoodi::HOODI_BPO2_TIMESTAMP,
1848 ..Default::default()
1849 },
1850 ForkId {
1851 hash: ForkHash(hex!("0x23aa1351")),
1852 next: hoodi::HOODI_AMSTERDAM_TIMESTAMP,
1853 },
1854 ),
1855 (
1857 Head {
1858 number: 0,
1859 timestamp: hoodi::HOODI_AMSTERDAM_TIMESTAMP - 1,
1860 ..Default::default()
1861 },
1862 ForkId {
1863 hash: ForkHash(hex!("0x23aa1351")),
1864 next: hoodi::HOODI_AMSTERDAM_TIMESTAMP,
1865 },
1866 ),
1867 (
1869 Head {
1870 number: 0,
1871 timestamp: hoodi::HOODI_AMSTERDAM_TIMESTAMP,
1872 ..Default::default()
1873 },
1874 ForkId { hash: ForkHash(hex!("0x3d068b59")), next: 0 },
1875 ),
1876 (
1878 Head {
1879 number: 0,
1880 timestamp: hoodi::HOODI_AMSTERDAM_TIMESTAMP + 1,
1881 ..Default::default()
1882 },
1883 ForkId { hash: ForkHash(hex!("0x3d068b59")), next: 0 },
1884 ),
1885 ],
1886 )
1887 }
1888
1889 #[test]
1890 fn holesky_fork_ids() {
1891 test_fork_ids(
1892 &HOLESKY,
1893 &[
1894 (
1895 Head { number: 0, ..Default::default() },
1896 ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
1897 ),
1898 (
1900 Head { number: 123, ..Default::default() },
1901 ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
1902 ),
1903 (
1905 Head { number: 123, timestamp: 1696000703, ..Default::default() },
1906 ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
1907 ),
1908 (
1910 Head { number: 123, timestamp: 1696000704, ..Default::default() },
1911 ForkId { hash: ForkHash(hex!("0xfd4f016b")), next: 1707305664 },
1912 ),
1913 (
1915 Head { number: 123, timestamp: 1707305663, ..Default::default() },
1916 ForkId { hash: ForkHash(hex!("0xfd4f016b")), next: 1707305664 },
1917 ),
1918 (
1920 Head { number: 123, timestamp: 1707305664, ..Default::default() },
1921 ForkId { hash: ForkHash(hex!("0x9b192ad0")), next: 1740434112 },
1922 ),
1923 (
1925 Head { number: 123, timestamp: 1740434111, ..Default::default() },
1926 ForkId { hash: ForkHash(hex!("0x9b192ad0")), next: 1740434112 },
1927 ),
1928 (
1930 Head { number: 123, timestamp: 1740434112, ..Default::default() },
1931 ForkId {
1932 hash: ForkHash(hex!("0xdfbd9bed")),
1933 next: holesky::HOLESKY_OSAKA_TIMESTAMP,
1934 },
1935 ),
1936 (
1938 Head {
1939 number: 123,
1940 timestamp: holesky::HOLESKY_OSAKA_TIMESTAMP,
1941 ..Default::default()
1942 },
1943 ForkId {
1944 hash: ForkHash(hex!("0x783def52")),
1945 next: holesky::HOLESKY_BPO1_TIMESTAMP,
1946 },
1947 ),
1948 ],
1949 )
1950 }
1951
1952 #[test]
1953 fn sepolia_fork_ids() {
1954 test_fork_ids(
1955 &SEPOLIA,
1956 &[
1957 (
1958 Head { number: 0, ..Default::default() },
1959 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1960 ),
1961 (
1962 Head { number: 1735370, ..Default::default() },
1963 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1964 ),
1965 (
1966 Head { number: 1735371, ..Default::default() },
1967 ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
1968 ),
1969 (
1970 Head { number: 1735372, timestamp: 1677557087, ..Default::default() },
1971 ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
1972 ),
1973 (
1975 Head { number: 1735373, timestamp: 1677557088, ..Default::default() },
1976 ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
1977 ),
1978 (
1980 Head { number: 1735374, timestamp: 1706655071, ..Default::default() },
1981 ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
1982 ),
1983 (
1985 Head { number: 1735375, timestamp: 1706655072, ..Default::default() },
1986 ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
1987 ),
1988 (
1990 Head { number: 1735376, timestamp: 1741159775, ..Default::default() },
1991 ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
1992 ),
1993 (
1995 Head { number: 1735377, timestamp: 1741159776, ..Default::default() },
1996 ForkId {
1997 hash: ForkHash(hex!("0xed88b5fd")),
1998 next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
1999 },
2000 ),
2001 (
2003 Head {
2004 number: 1735377,
2005 timestamp: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
2006 ..Default::default()
2007 },
2008 ForkId {
2009 hash: ForkHash(hex!("0xe2ae4999")),
2010 next: sepolia::SEPOLIA_BPO1_TIMESTAMP,
2011 },
2012 ),
2013 (
2015 Head {
2016 number: 1735377,
2017 timestamp: sepolia::SEPOLIA_BPO1_TIMESTAMP,
2018 ..Default::default()
2019 },
2020 ForkId {
2021 hash: ForkHash(hex!("0x56078a1e")),
2022 next: sepolia::SEPOLIA_BPO2_TIMESTAMP,
2023 },
2024 ),
2025 (
2027 Head {
2028 number: 1735377,
2029 timestamp: sepolia::SEPOLIA_BPO2_TIMESTAMP,
2030 ..Default::default()
2031 },
2032 ForkId {
2033 hash: ForkHash(hex!("0x268956b6")),
2034 next: sepolia::SEPOLIA_AMSTERDAM_TIMESTAMP,
2035 },
2036 ),
2037 (
2039 Head {
2040 number: 1735377,
2041 timestamp: sepolia::SEPOLIA_AMSTERDAM_TIMESTAMP - 1,
2042 ..Default::default()
2043 },
2044 ForkId {
2045 hash: ForkHash(hex!("0x268956b6")),
2046 next: sepolia::SEPOLIA_AMSTERDAM_TIMESTAMP,
2047 },
2048 ),
2049 (
2051 Head {
2052 number: 1735377,
2053 timestamp: sepolia::SEPOLIA_AMSTERDAM_TIMESTAMP,
2054 ..Default::default()
2055 },
2056 ForkId { hash: ForkHash(hex!("0x6c1d9423")), next: 0 },
2057 ),
2058 (
2060 Head {
2061 number: 1735377,
2062 timestamp: sepolia::SEPOLIA_AMSTERDAM_TIMESTAMP + 1,
2063 ..Default::default()
2064 },
2065 ForkId { hash: ForkHash(hex!("0x6c1d9423")), next: 0 },
2066 ),
2067 ],
2068 );
2069 }
2070
2071 #[test]
2072 fn dev_fork_ids() {
2073 test_fork_ids(
2074 &DEV,
2075 &[(
2076 Head { number: 0, ..Default::default() },
2077 ForkId { hash: ForkHash(hex!("0x0b1a4ef7")), next: 0 },
2078 )],
2079 )
2080 }
2081
2082 #[test]
2086 fn timestamped_forks() {
2087 let mainnet_with_timestamps = ChainSpecBuilder::mainnet().build();
2088 test_fork_ids(
2089 &mainnet_with_timestamps,
2090 &[
2091 (
2092 Head { number: 0, timestamp: 0, ..Default::default() },
2093 ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
2094 ), (
2096 Head { number: 1149999, timestamp: 0, ..Default::default() },
2097 ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
2098 ), (
2100 Head { number: 1150000, timestamp: 0, ..Default::default() },
2101 ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
2102 ), (
2104 Head { number: 1919999, timestamp: 0, ..Default::default() },
2105 ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
2106 ), (
2108 Head { number: 1920000, timestamp: 0, ..Default::default() },
2109 ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
2110 ), (
2112 Head { number: 2462999, timestamp: 0, ..Default::default() },
2113 ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
2114 ), (
2116 Head { number: 2463000, timestamp: 0, ..Default::default() },
2117 ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
2118 ), (
2120 Head { number: 2674999, timestamp: 0, ..Default::default() },
2121 ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
2122 ), (
2124 Head { number: 2675000, timestamp: 0, ..Default::default() },
2125 ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
2126 ), (
2128 Head { number: 4369999, timestamp: 0, ..Default::default() },
2129 ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
2130 ), (
2132 Head { number: 4370000, timestamp: 0, ..Default::default() },
2133 ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
2134 ), (
2136 Head { number: 7279999, timestamp: 0, ..Default::default() },
2137 ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
2138 ), (
2140 Head { number: 7280000, timestamp: 0, ..Default::default() },
2141 ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
2142 ), (
2144 Head { number: 9068999, timestamp: 0, ..Default::default() },
2145 ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
2146 ), (
2148 Head { number: 9069000, timestamp: 0, ..Default::default() },
2149 ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
2150 ), (
2152 Head { number: 9199999, timestamp: 0, ..Default::default() },
2153 ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
2154 ), (
2156 Head { number: 9200000, timestamp: 0, ..Default::default() },
2157 ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
2158 ), (
2160 Head { number: 12243999, timestamp: 0, ..Default::default() },
2161 ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
2162 ), (
2164 Head { number: 12244000, timestamp: 0, ..Default::default() },
2165 ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
2166 ), (
2168 Head { number: 12964999, timestamp: 0, ..Default::default() },
2169 ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
2170 ), (
2172 Head { number: 12965000, timestamp: 0, ..Default::default() },
2173 ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
2174 ), (
2176 Head { number: 13772999, timestamp: 0, ..Default::default() },
2177 ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
2178 ), (
2180 Head { number: 13773000, timestamp: 0, ..Default::default() },
2181 ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
2182 ), (
2184 Head { number: 15049999, timestamp: 0, ..Default::default() },
2185 ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
2186 ), (
2188 Head { number: 15050000, timestamp: 0, ..Default::default() },
2189 ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
2190 ), (
2192 Head { number: 19999999, timestamp: 1667999999, ..Default::default() },
2193 ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
2194 ), (
2196 Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
2197 ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
2198 ), (
2200 Head { number: 20000001, timestamp: 1710338134, ..Default::default() },
2201 ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
2202 ), (
2204 Head { number: 20000002, timestamp: 1710338135, ..Default::default() },
2205 ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
2206 ), (
2208 Head { number: 20000003, timestamp: 1746612310, ..Default::default() },
2209 ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
2210 ), (
2212 Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
2213 ForkId {
2214 hash: ForkHash(hex!("0xc376cf8b")),
2215 next: mainnet::MAINNET_OSAKA_TIMESTAMP,
2216 },
2217 ),
2218 (
2220 Head {
2221 number: 20000004,
2222 timestamp: mainnet::MAINNET_OSAKA_TIMESTAMP,
2223 ..Default::default()
2224 },
2225 ForkId {
2226 hash: ForkHash(hex!("0x5167e2a6")),
2227 next: mainnet::MAINNET_BPO1_TIMESTAMP,
2228 },
2229 ),
2230 ],
2231 );
2232 }
2233
2234 fn construct_chainspec(
2237 builder: ChainSpecBuilder,
2238 shanghai_time: u64,
2239 cancun_time: u64,
2240 ) -> ChainSpec {
2241 builder
2242 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(shanghai_time))
2243 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(cancun_time))
2244 .build()
2245 }
2246
2247 #[test]
2252 fn test_timestamp_fork_in_genesis() {
2253 let timestamp = 1690475657u64;
2254 let default_spec_builder = ChainSpecBuilder::default()
2255 .chain(Chain::from_id(1337))
2256 .genesis(Genesis::default().with_timestamp(timestamp))
2257 .paris_activated();
2258
2259 let tests = [
2262 (
2263 construct_chainspec(default_spec_builder.clone(), timestamp - 1, timestamp + 1),
2264 timestamp + 1,
2265 ),
2266 (
2267 construct_chainspec(default_spec_builder.clone(), timestamp, timestamp + 1),
2268 timestamp + 1,
2269 ),
2270 (
2271 construct_chainspec(default_spec_builder, timestamp + 1, timestamp + 2),
2272 timestamp + 1,
2273 ),
2274 ];
2275
2276 for (spec, expected_timestamp) in tests {
2277 let got_forkid = spec.fork_id(&Head { number: 0, timestamp: 0, ..Default::default() });
2278 let genesis_hash = spec.genesis_hash();
2283 let expected_forkid =
2284 ForkId { hash: ForkHash::from(genesis_hash), next: expected_timestamp };
2285 assert_eq!(got_forkid, expected_forkid);
2286 }
2287 }
2288
2289 #[test]
2291 fn check_terminal_ttd() {
2292 let chainspec = ChainSpecBuilder::mainnet().build();
2293
2294 let terminal_block_ttd = U256::from(58750003716598352816469_u128);
2296 let terminal_block_difficulty = U256::from(11055787484078698_u128);
2297 assert!(!chainspec
2298 .fork(EthereumHardfork::Paris)
2299 .active_at_ttd(terminal_block_ttd, terminal_block_difficulty));
2300
2301 let first_pos_block_ttd = U256::from(58750003716598352816469_u128);
2303 let first_pos_difficulty = U256::ZERO;
2304 assert!(chainspec
2305 .fork(EthereumHardfork::Paris)
2306 .active_at_ttd(first_pos_block_ttd, first_pos_difficulty));
2307 }
2308
2309 #[test]
2310 fn geth_genesis_with_shanghai() {
2311 let geth_genesis = r#"
2312 {
2313 "config": {
2314 "chainId": 1337,
2315 "homesteadBlock": 0,
2316 "eip150Block": 0,
2317 "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2318 "eip155Block": 0,
2319 "eip158Block": 0,
2320 "byzantiumBlock": 0,
2321 "constantinopleBlock": 0,
2322 "petersburgBlock": 0,
2323 "istanbulBlock": 0,
2324 "muirGlacierBlock": 0,
2325 "berlinBlock": 0,
2326 "londonBlock": 0,
2327 "arrowGlacierBlock": 0,
2328 "grayGlacierBlock": 0,
2329 "shanghaiTime": 0,
2330 "cancunTime": 1,
2331 "terminalTotalDifficulty": 0,
2332 "terminalTotalDifficultyPassed": true,
2333 "ethash": {}
2334 },
2335 "nonce": "0x0",
2336 "timestamp": "0x0",
2337 "extraData": "0x",
2338 "gasLimit": "0x4c4b40",
2339 "difficulty": "0x1",
2340 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2341 "coinbase": "0x0000000000000000000000000000000000000000",
2342 "alloc": {
2343 "658bdf435d810c91414ec09147daa6db62406379": {
2344 "balance": "0x487a9a304539440000"
2345 },
2346 "aa00000000000000000000000000000000000000": {
2347 "code": "0x6042",
2348 "storage": {
2349 "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
2350 "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000",
2351 "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000",
2352 "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303"
2353 },
2354 "balance": "0x1",
2355 "nonce": "0x1"
2356 },
2357 "bb00000000000000000000000000000000000000": {
2358 "code": "0x600154600354",
2359 "storage": {
2360 "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
2361 "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000",
2362 "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000",
2363 "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303"
2364 },
2365 "balance": "0x2",
2366 "nonce": "0x1"
2367 }
2368 },
2369 "number": "0x0",
2370 "gasUsed": "0x0",
2371 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2372 "baseFeePerGas": "0x3b9aca00"
2373 }
2374 "#;
2375
2376 let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
2377 let chainspec = ChainSpec::from(genesis);
2378
2379 assert_eq!(
2381 chainspec.hardforks.get(EthereumHardfork::Homestead).unwrap(),
2382 ForkCondition::Block(0)
2383 );
2384 assert_eq!(
2385 chainspec.hardforks.get(EthereumHardfork::Tangerine).unwrap(),
2386 ForkCondition::Block(0)
2387 );
2388 assert_eq!(
2389 chainspec.hardforks.get(EthereumHardfork::SpuriousDragon).unwrap(),
2390 ForkCondition::Block(0)
2391 );
2392 assert_eq!(
2393 chainspec.hardforks.get(EthereumHardfork::Byzantium).unwrap(),
2394 ForkCondition::Block(0)
2395 );
2396 assert_eq!(
2397 chainspec.hardforks.get(EthereumHardfork::Constantinople).unwrap(),
2398 ForkCondition::Block(0)
2399 );
2400 assert_eq!(
2401 chainspec.hardforks.get(EthereumHardfork::Petersburg).unwrap(),
2402 ForkCondition::Block(0)
2403 );
2404 assert_eq!(
2405 chainspec.hardforks.get(EthereumHardfork::Istanbul).unwrap(),
2406 ForkCondition::Block(0)
2407 );
2408 assert_eq!(
2409 chainspec.hardforks.get(EthereumHardfork::MuirGlacier).unwrap(),
2410 ForkCondition::Block(0)
2411 );
2412 assert_eq!(
2413 chainspec.hardforks.get(EthereumHardfork::Berlin).unwrap(),
2414 ForkCondition::Block(0)
2415 );
2416 assert_eq!(
2417 chainspec.hardforks.get(EthereumHardfork::London).unwrap(),
2418 ForkCondition::Block(0)
2419 );
2420 assert_eq!(
2421 chainspec.hardforks.get(EthereumHardfork::ArrowGlacier).unwrap(),
2422 ForkCondition::Block(0)
2423 );
2424 assert_eq!(
2425 chainspec.hardforks.get(EthereumHardfork::GrayGlacier).unwrap(),
2426 ForkCondition::Block(0)
2427 );
2428
2429 assert_eq!(
2431 chainspec.hardforks.get(EthereumHardfork::Shanghai).unwrap(),
2432 ForkCondition::Timestamp(0)
2433 );
2434
2435 assert_eq!(
2437 chainspec.hardforks.get(EthereumHardfork::Cancun).unwrap(),
2438 ForkCondition::Timestamp(1)
2439 );
2440
2441 let key_rlp = vec![
2443 (
2444 hex!("0x658bdf435d810c91414ec09147daa6db62406379"),
2445 &hex!(
2446 "0xf84d8089487a9a304539440000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
2447 )[..],
2448 ),
2449 (
2450 hex!("0xaa00000000000000000000000000000000000000"),
2451 &hex!(
2452 "0xf8440101a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0ce92c756baff35fa740c3557c1a971fd24d2d35b7c8e067880d50cd86bb0bc99"
2453 )[..],
2454 ),
2455 (
2456 hex!("0xbb00000000000000000000000000000000000000"),
2457 &hex!(
2458 "0xf8440102a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0e25a53cbb501cec2976b393719c63d832423dd70a458731a0b64e4847bbca7d2"
2459 )[..],
2460 ),
2461 ];
2462
2463 for (key, expected_rlp) in key_rlp {
2464 let account = chainspec.genesis.alloc.get(&key).expect("account should exist");
2465 assert_eq!(&alloy_rlp::encode(TrieAccount::from(account.clone())), expected_rlp);
2466 }
2467
2468 let expected_state_root: B256 =
2469 b256!("0x078dc6061b1d8eaa8493384b59c9c65ceb917201221d08b80c4de6770b6ec7e7");
2470 assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
2471
2472 assert_eq!(chainspec.genesis_header().withdrawals_root, Some(EMPTY_ROOT_HASH));
2473
2474 let expected_hash: B256 =
2475 b256!("0x1fc027d65f820d3eef441ebeec139ebe09e471cf98516dce7b5643ccb27f418c");
2476 let hash = chainspec.genesis_hash();
2477 assert_eq!(hash, expected_hash);
2478 }
2479
2480 #[test]
2481 fn hive_geth_json() {
2482 let hive_json = r#"
2483 {
2484 "nonce": "0x0000000000000042",
2485 "difficulty": "0x2123456",
2486 "mixHash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
2487 "coinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
2488 "timestamp": "0x123456",
2489 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2490 "extraData": "0xfafbfcfd",
2491 "gasLimit": "0x2fefd8",
2492 "alloc": {
2493 "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {
2494 "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
2495 },
2496 "e6716f9544a56c530d868e4bfbacb172315bdead": {
2497 "balance": "0x11",
2498 "code": "0x12"
2499 },
2500 "b9c015918bdaba24b4ff057a92a3873d6eb201be": {
2501 "balance": "0x21",
2502 "storage": {
2503 "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22"
2504 }
2505 },
2506 "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {
2507 "balance": "0x31",
2508 "nonce": "0x32"
2509 },
2510 "0000000000000000000000000000000000000001": {
2511 "balance": "0x41"
2512 },
2513 "0000000000000000000000000000000000000002": {
2514 "balance": "0x51"
2515 },
2516 "0000000000000000000000000000000000000003": {
2517 "balance": "0x61"
2518 },
2519 "0000000000000000000000000000000000000004": {
2520 "balance": "0x71"
2521 }
2522 },
2523 "config": {
2524 "ethash": {},
2525 "chainId": 10,
2526 "homesteadBlock": 0,
2527 "eip150Block": 0,
2528 "eip155Block": 0,
2529 "eip158Block": 0,
2530 "byzantiumBlock": 0,
2531 "constantinopleBlock": 0,
2532 "petersburgBlock": 0,
2533 "istanbulBlock": 0
2534 }
2535 }
2536 "#;
2537
2538 let genesis = serde_json::from_str::<Genesis>(hive_json).unwrap();
2539 let chainspec: ChainSpec = genesis.into();
2540 assert_eq!(chainspec.chain, Chain::optimism_mainnet());
2541 let expected_state_root: B256 =
2542 b256!("0x9a6049ac535e3dc7436c189eaa81c73f35abd7f282ab67c32944ff0301d63360");
2543 assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
2544 let hard_forks = vec![
2545 EthereumHardfork::Byzantium,
2546 EthereumHardfork::Homestead,
2547 EthereumHardfork::Istanbul,
2548 EthereumHardfork::Petersburg,
2549 EthereumHardfork::Constantinople,
2550 ];
2551 for fork in hard_forks {
2552 assert_eq!(chainspec.hardforks.get(fork).unwrap(), ForkCondition::Block(0));
2553 }
2554
2555 let expected_hash: B256 =
2556 b256!("0x5ae31c6522bd5856129f66be3d582b842e4e9faaa87f21cce547128339a9db3c");
2557 let hash = chainspec.genesis_header().hash_slow();
2558 assert_eq!(hash, expected_hash);
2559 }
2560
2561 #[test]
2562 fn test_hive_paris_block_genesis_json() {
2563 let hive_paris = r#"
2566 {
2567 "config": {
2568 "ethash": {},
2569 "chainId": 3503995874084926,
2570 "homesteadBlock": 0,
2571 "eip150Block": 6,
2572 "eip155Block": 12,
2573 "eip158Block": 12,
2574 "byzantiumBlock": 18,
2575 "constantinopleBlock": 24,
2576 "petersburgBlock": 30,
2577 "istanbulBlock": 36,
2578 "muirGlacierBlock": 42,
2579 "berlinBlock": 48,
2580 "londonBlock": 54,
2581 "arrowGlacierBlock": 60,
2582 "grayGlacierBlock": 66,
2583 "mergeNetsplitBlock": 72,
2584 "terminalTotalDifficulty": 9454784,
2585 "shanghaiTime": 780,
2586 "cancunTime": 840
2587 },
2588 "nonce": "0x0",
2589 "timestamp": "0x0",
2590 "extraData": "0x68697665636861696e",
2591 "gasLimit": "0x23f3e20",
2592 "difficulty": "0x20000",
2593 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2594 "coinbase": "0x0000000000000000000000000000000000000000",
2595 "alloc": {
2596 "000f3df6d732807ef1319fb7b8bb8522d0beac02": {
2597 "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
2598 "balance": "0x2a"
2599 },
2600 "0c2c51a0990aee1d73c1228de158688341557508": {
2601 "balance": "0xc097ce7bc90715b34b9f1000000000"
2602 },
2603 "14e46043e63d0e3cdcf2530519f4cfaf35058cb2": {
2604 "balance": "0xc097ce7bc90715b34b9f1000000000"
2605 },
2606 "16c57edf7fa9d9525378b0b81bf8a3ced0620c1c": {
2607 "balance": "0xc097ce7bc90715b34b9f1000000000"
2608 },
2609 "1f4924b14f34e24159387c0a4cdbaa32f3ddb0cf": {
2610 "balance": "0xc097ce7bc90715b34b9f1000000000"
2611 },
2612 "1f5bde34b4afc686f136c7a3cb6ec376f7357759": {
2613 "balance": "0xc097ce7bc90715b34b9f1000000000"
2614 },
2615 "2d389075be5be9f2246ad654ce152cf05990b209": {
2616 "balance": "0xc097ce7bc90715b34b9f1000000000"
2617 },
2618 "3ae75c08b4c907eb63a8960c45b86e1e9ab6123c": {
2619 "balance": "0xc097ce7bc90715b34b9f1000000000"
2620 },
2621 "4340ee1b812acb40a1eb561c019c327b243b92df": {
2622 "balance": "0xc097ce7bc90715b34b9f1000000000"
2623 },
2624 "4a0f1452281bcec5bd90c3dce6162a5995bfe9df": {
2625 "balance": "0xc097ce7bc90715b34b9f1000000000"
2626 },
2627 "4dde844b71bcdf95512fb4dc94e84fb67b512ed8": {
2628 "balance": "0xc097ce7bc90715b34b9f1000000000"
2629 },
2630 "5f552da00dfb4d3749d9e62dcee3c918855a86a0": {
2631 "balance": "0xc097ce7bc90715b34b9f1000000000"
2632 },
2633 "654aa64f5fbefb84c270ec74211b81ca8c44a72e": {
2634 "balance": "0xc097ce7bc90715b34b9f1000000000"
2635 },
2636 "717f8aa2b982bee0e29f573d31df288663e1ce16": {
2637 "balance": "0xc097ce7bc90715b34b9f1000000000"
2638 },
2639 "7435ed30a8b4aeb0877cef0c6e8cffe834eb865f": {
2640 "balance": "0xc097ce7bc90715b34b9f1000000000"
2641 },
2642 "83c7e323d189f18725ac510004fdc2941f8c4a78": {
2643 "balance": "0xc097ce7bc90715b34b9f1000000000"
2644 },
2645 "84e75c28348fb86acea1a93a39426d7d60f4cc46": {
2646 "balance": "0xc097ce7bc90715b34b9f1000000000"
2647 },
2648 "8bebc8ba651aee624937e7d897853ac30c95a067": {
2649 "storage": {
2650 "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001",
2651 "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000002",
2652 "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000003"
2653 },
2654 "balance": "0x1",
2655 "nonce": "0x1"
2656 },
2657 "c7b99a164efd027a93f147376cc7da7c67c6bbe0": {
2658 "balance": "0xc097ce7bc90715b34b9f1000000000"
2659 },
2660 "d803681e487e6ac18053afc5a6cd813c86ec3e4d": {
2661 "balance": "0xc097ce7bc90715b34b9f1000000000"
2662 },
2663 "e7d13f7aa2a838d24c59b40186a0aca1e21cffcc": {
2664 "balance": "0xc097ce7bc90715b34b9f1000000000"
2665 },
2666 "eda8645ba6948855e3b3cd596bbb07596d59c603": {
2667 "balance": "0xc097ce7bc90715b34b9f1000000000"
2668 }
2669 },
2670 "number": "0x0",
2671 "gasUsed": "0x0",
2672 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2673 "baseFeePerGas": null,
2674 "excessBlobGas": null,
2675 "blobGasUsed": null
2676 }
2677 "#;
2678
2679 let genesis: Genesis = serde_json::from_str(hive_paris).unwrap();
2681 let chainspec = ChainSpec::from(genesis);
2682
2683 let expected_forkid = ForkId { hash: ForkHash(hex!("0xbc0c2605")), next: 0 };
2685 let got_forkid =
2686 chainspec.fork_id(&Head { number: 73, timestamp: 840, ..Default::default() });
2687
2688 assert_eq!(got_forkid, expected_forkid);
2690 assert_eq!(chainspec.paris_block_and_final_difficulty, Some((72, U256::from(9454784))));
2692 }
2693
2694 #[test]
2695 fn test_parse_genesis_json() {
2696 let s = r#"{"config":{"ethash":{},"chainId":1337,"homesteadBlock":0,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true,"shanghaiTime":0},"nonce":"0x0","timestamp":"0x0","extraData":"0x","gasLimit":"0x4c4b40","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"658bdf435d810c91414ec09147daa6db62406379":{"balance":"0x487a9a304539440000"},"aa00000000000000000000000000000000000000":{"code":"0x6042","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x1","nonce":"0x1"},"bb00000000000000000000000000000000000000":{"code":"0x600154600354","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x2","nonce":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":"0x1337"}"#;
2697 let genesis: Genesis = serde_json::from_str(s).unwrap();
2698 let acc =
2699 genesis.alloc.get(&address!("0xaa00000000000000000000000000000000000000")).unwrap();
2700 assert_eq!(acc.balance, U256::ONE);
2701 assert_eq!(genesis.base_fee_per_gas, Some(0x1337));
2702 }
2703
2704 #[test]
2705 fn test_parse_cancun_genesis_json() {
2706 let s = r#"{"config":{"ethash":{},"chainId":1337,"homesteadBlock":0,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true,"shanghaiTime":0,"cancunTime":4661},"nonce":"0x0","timestamp":"0x0","extraData":"0x","gasLimit":"0x4c4b40","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"658bdf435d810c91414ec09147daa6db62406379":{"balance":"0x487a9a304539440000"},"aa00000000000000000000000000000000000000":{"code":"0x6042","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x1","nonce":"0x1"},"bb00000000000000000000000000000000000000":{"code":"0x600154600354","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x2","nonce":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":"0x3b9aca00"}"#;
2707 let genesis: Genesis = serde_json::from_str(s).unwrap();
2708 let acc =
2709 genesis.alloc.get(&address!("0xaa00000000000000000000000000000000000000")).unwrap();
2710 assert_eq!(acc.balance, U256::ONE);
2711 assert_eq!(genesis.config.cancun_time, Some(4661));
2713 }
2714
2715 #[test]
2716 fn test_parse_prague_genesis_all_formats() {
2717 let s = r#"{"config":{"ethash":{},"chainId":1337,"homesteadBlock":0,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true,"shanghaiTime":0,"cancunTime":4661, "pragueTime": 4662},"nonce":"0x0","timestamp":"0x0","extraData":"0x","gasLimit":"0x4c4b40","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"658bdf435d810c91414ec09147daa6db62406379":{"balance":"0x487a9a304539440000"},"aa00000000000000000000000000000000000000":{"code":"0x6042","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x1","nonce":"0x1"},"bb00000000000000000000000000000000000000":{"code":"0x600154600354","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x2","nonce":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":"0x3b9aca00"}"#;
2718 let genesis: Genesis = serde_json::from_str(s).unwrap();
2719
2720 let acc =
2722 genesis.alloc.get(&address!("0xaa00000000000000000000000000000000000000")).unwrap();
2723 assert_eq!(acc.balance, U256::ONE);
2724 assert_eq!(genesis.config.cancun_time, Some(4661));
2726 assert_eq!(genesis.config.prague_time, Some(4662));
2728 }
2729
2730 #[test]
2731 fn test_parse_cancun_genesis_all_formats() {
2732 let s = r#"{"config":{"ethash":{},"chainId":1337,"homesteadBlock":0,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true,"shanghaiTime":0,"cancunTime":4661},"nonce":"0x0","timestamp":"0x0","extraData":"0x","gasLimit":"0x4c4b40","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"658bdf435d810c91414ec09147daa6db62406379":{"balance":"0x487a9a304539440000"},"aa00000000000000000000000000000000000000":{"code":"0x6042","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x1","nonce":"0x1"},"bb00000000000000000000000000000000000000":{"code":"0x600154600354","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x2","nonce":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":"0x3b9aca00"}"#;
2733 let genesis: Genesis = serde_json::from_str(s).unwrap();
2734
2735 let acc =
2737 genesis.alloc.get(&address!("0xaa00000000000000000000000000000000000000")).unwrap();
2738 assert_eq!(acc.balance, U256::ONE);
2739 assert_eq!(genesis.config.cancun_time, Some(4661));
2741 }
2742
2743 #[test]
2744 fn test_paris_block_and_total_difficulty() {
2745 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2746 let paris_chainspec = ChainSpecBuilder::default()
2747 .chain(Chain::from_id(1337))
2748 .genesis(genesis)
2749 .paris_activated()
2750 .build();
2751 assert_eq!(paris_chainspec.paris_block_and_final_difficulty, Some((0, U256::ZERO)));
2752 }
2753
2754 #[test]
2755 fn test_default_cancun_header_forkhash() {
2756 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2758 let default_chainspec = ChainSpecBuilder::default()
2759 .chain(Chain::from_id(1337))
2760 .genesis(genesis)
2761 .cancun_activated()
2762 .build();
2763 let mut header = default_chainspec.genesis_header().clone();
2764
2765 header.state_root =
2767 b256!("0x62e2595e017f0ca23e08d17221010721a71c3ae932f4ea3cb12117786bb392d4");
2768
2769 assert_eq!(header.withdrawals_root, Some(EMPTY_WITHDRAWALS));
2771
2772 assert_eq!(header.parent_beacon_block_root, Some(B256::ZERO));
2775 assert_eq!(header.blob_gas_used, Some(0));
2776 assert_eq!(header.excess_blob_gas, Some(0));
2777
2778 let genesis_hash = header.hash_slow();
2780 let expected_hash =
2781 b256!("0x16bb7c59613a5bad3f7c04a852fd056545ade2483968d9a25a1abb05af0c4d37");
2782 assert_eq!(genesis_hash, expected_hash);
2783
2784 let expected_forkhash = ForkHash(hex!("0x8062457a"));
2786 assert_eq!(ForkHash::from(genesis_hash), expected_forkhash);
2787 }
2788
2789 #[test]
2790 fn test_amsterdam_genesis_slot_number() {
2791 let genesis =
2793 Genesis { gas_limit: 0x2fefd8u64, ..Default::default() }.with_slot_number(Some(999));
2794 let chainspec = ChainSpecBuilder::default()
2795 .chain(Chain::from_id(1337))
2796 .genesis(genesis)
2797 .amsterdam_activated()
2798 .build();
2799 assert_eq!(chainspec.genesis_header().slot_number, Some(999));
2800
2801 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2803 let chainspec = ChainSpecBuilder::default()
2804 .chain(Chain::from_id(1337))
2805 .genesis(genesis)
2806 .amsterdam_activated()
2807 .build();
2808 assert_eq!(chainspec.genesis_header().slot_number, Some(0));
2809 }
2810
2811 #[test]
2812 fn holesky_paris_activated_at_genesis() {
2813 assert!(HOLESKY
2814 .fork(EthereumHardfork::Paris)
2815 .active_at_ttd(HOLESKY.genesis.difficulty, HOLESKY.genesis.difficulty));
2816 }
2817
2818 #[test]
2819 fn test_genesis_format_deserialization() {
2820 let config = ChainConfig {
2822 chain_id: 2600,
2823 homestead_block: Some(0),
2824 eip150_block: Some(0),
2825 eip155_block: Some(0),
2826 eip158_block: Some(0),
2827 byzantium_block: Some(0),
2828 constantinople_block: Some(0),
2829 petersburg_block: Some(0),
2830 istanbul_block: Some(0),
2831 berlin_block: Some(0),
2832 london_block: Some(0),
2833 shanghai_time: Some(0),
2834 terminal_total_difficulty: Some(U256::ZERO),
2835 terminal_total_difficulty_passed: true,
2836 ..Default::default()
2837 };
2838 let genesis = Genesis {
2840 config,
2841 nonce: 0,
2842 timestamp: 1698688670,
2843 gas_limit: 5000,
2844 difficulty: U256::ZERO,
2845 mix_hash: B256::ZERO,
2846 coinbase: Address::ZERO,
2847 ..Default::default()
2848 };
2849
2850 let address = address!("0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b");
2852 let account = GenesisAccount::default().with_balance(U256::from(33));
2853 let genesis = genesis.extend_accounts(HashMap::from([(address, account)]));
2854
2855 let serialized_genesis = serde_json::to_string(&genesis).unwrap();
2857 let deserialized_genesis: Genesis = serde_json::from_str(&serialized_genesis).unwrap();
2858
2859 assert_eq!(genesis, deserialized_genesis);
2860 }
2861
2862 #[test]
2863 fn check_fork_id_chainspec_with_fork_condition_never() {
2864 let spec: ChainSpec = ChainSpec {
2865 chain: Chain::mainnet(),
2866 genesis: Genesis::default(),
2867 hardforks: ChainHardforks::new(vec![(
2868 EthereumHardfork::Frontier.boxed(),
2869 ForkCondition::Never,
2870 )]),
2871 paris_block_and_final_difficulty: None,
2872 deposit_contract: None,
2873 ..Default::default()
2874 };
2875
2876 assert_eq!(spec.hardfork_fork_id(EthereumHardfork::Frontier), None);
2877 }
2878
2879 #[test]
2880 fn check_fork_filter_chainspec_with_fork_condition_never() {
2881 let spec: ChainSpec = ChainSpec {
2882 chain: Chain::mainnet(),
2883 genesis: Genesis::default(),
2884 hardforks: ChainHardforks::new(vec![(
2885 EthereumHardfork::Shanghai.boxed(),
2886 ForkCondition::Never,
2887 )]),
2888 paris_block_and_final_difficulty: None,
2889 deposit_contract: None,
2890 ..Default::default()
2891 };
2892
2893 assert_eq!(spec.hardfork_fork_filter(EthereumHardfork::Shanghai), None);
2894 }
2895
2896 #[test]
2897 fn latest_eth_mainnet_fork_id() {
2898 assert_eq!(ForkId { hash: ForkHash(hex!("0x07c9462e")), next: 0 }, MAINNET.latest_fork_id())
2900 }
2901
2902 #[test]
2903 fn latest_hoodi_mainnet_fork_id() {
2904 assert_eq!(ForkId { hash: ForkHash(hex!("0x3d068b59")), next: 0 }, HOODI.latest_fork_id())
2906 }
2907
2908 #[test]
2909 fn latest_holesky_mainnet_fork_id() {
2910 assert_eq!(ForkId { hash: ForkHash(hex!("0x9bc6cb31")), next: 0 }, HOLESKY.latest_fork_id())
2912 }
2913
2914 #[test]
2915 fn latest_sepolia_mainnet_fork_id() {
2916 assert_eq!(ForkId { hash: ForkHash(hex!("0x6c1d9423")), next: 0 }, SEPOLIA.latest_fork_id())
2918 }
2919
2920 #[test]
2921 fn test_fork_order_ethereum_mainnet() {
2922 let genesis = Genesis {
2923 config: ChainConfig {
2924 chain_id: 0,
2925 homestead_block: Some(0),
2926 dao_fork_block: Some(0),
2927 dao_fork_support: false,
2928 eip150_block: Some(0),
2929 eip155_block: Some(0),
2930 eip158_block: Some(0),
2931 byzantium_block: Some(0),
2932 constantinople_block: Some(0),
2933 petersburg_block: Some(0),
2934 istanbul_block: Some(0),
2935 muir_glacier_block: Some(0),
2936 berlin_block: Some(0),
2937 london_block: Some(0),
2938 arrow_glacier_block: Some(0),
2939 gray_glacier_block: Some(0),
2940 merge_netsplit_block: Some(0),
2941 shanghai_time: Some(0),
2942 cancun_time: Some(0),
2943 terminal_total_difficulty: Some(U256::ZERO),
2944 ..Default::default()
2945 },
2946 ..Default::default()
2947 };
2948
2949 let chain_spec: ChainSpec = genesis.into();
2950
2951 let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect();
2952 let expected_hardforks = vec![
2953 EthereumHardfork::Frontier.boxed(),
2954 EthereumHardfork::Homestead.boxed(),
2955 EthereumHardfork::Dao.boxed(),
2956 EthereumHardfork::Tangerine.boxed(),
2957 EthereumHardfork::SpuriousDragon.boxed(),
2958 EthereumHardfork::Byzantium.boxed(),
2959 EthereumHardfork::Constantinople.boxed(),
2960 EthereumHardfork::Petersburg.boxed(),
2961 EthereumHardfork::Istanbul.boxed(),
2962 EthereumHardfork::MuirGlacier.boxed(),
2963 EthereumHardfork::Berlin.boxed(),
2964 EthereumHardfork::London.boxed(),
2965 EthereumHardfork::ArrowGlacier.boxed(),
2966 EthereumHardfork::GrayGlacier.boxed(),
2967 EthereumHardfork::Paris.boxed(),
2968 EthereumHardfork::Shanghai.boxed(),
2969 EthereumHardfork::Cancun.boxed(),
2970 ];
2971
2972 assert!(expected_hardforks
2973 .iter()
2974 .zip(hardforks.iter())
2975 .all(|(expected, actual)| &**expected == *actual));
2976 assert_eq!(expected_hardforks.len(), hardforks.len());
2977 }
2978
2979 #[test]
2980 fn test_calc_base_block_reward() {
2981 let cases = [
2983 ((0, U256::ZERO), Some(ETH_TO_WEI * 5)),
2985 ((4370000, U256::ZERO), Some(ETH_TO_WEI * 3)),
2987 ((7280000, U256::ZERO), Some(ETH_TO_WEI * 2)),
2989 ((15537394, U256::from(58_750_000_000_000_000_000_000_u128)), None),
2991 ];
2992
2993 for ((block_number, _td), expected_reward) in cases {
2994 assert_eq!(base_block_reward(&*MAINNET, block_number), expected_reward);
2995 }
2996 }
2997
2998 #[test]
2999 fn test_calc_full_block_reward() {
3000 let base_reward = ETH_TO_WEI;
3001 let one_thirty_twoth_reward = base_reward >> 5;
3002
3003 let cases = [
3005 (0, base_reward),
3006 (1, base_reward + one_thirty_twoth_reward),
3007 (2, base_reward + one_thirty_twoth_reward * 2),
3008 ];
3009
3010 for (num_ommers, expected_reward) in cases {
3011 assert_eq!(block_reward(base_reward, num_ommers), expected_reward);
3012 }
3013 }
3014
3015 #[test]
3016 fn blob_params_from_genesis() {
3017 let s = r#"{
3018 "blobSchedule": {
3019 "cancun":{
3020 "baseFeeUpdateFraction":3338477,
3021 "max":6,
3022 "target":3
3023 },
3024 "prague":{
3025 "baseFeeUpdateFraction":3338477,
3026 "max":6,
3027 "target":3
3028 }
3029 }
3030 }"#;
3031 let config: ChainConfig = serde_json::from_str(s).unwrap();
3032 let hardfork_params = config.blob_schedule_blob_params();
3033 let expected = BlobScheduleBlobParams {
3034 cancun: BlobParams {
3035 target_blob_count: 3,
3036 max_blob_count: 6,
3037 update_fraction: 3338477,
3038 min_blob_fee: BLOB_TX_MIN_BLOB_GASPRICE,
3039 max_blobs_per_tx: 6,
3040 blob_base_cost: 0,
3041 },
3042 prague: BlobParams {
3043 target_blob_count: 3,
3044 max_blob_count: 6,
3045 update_fraction: 3338477,
3046 min_blob_fee: BLOB_TX_MIN_BLOB_GASPRICE,
3047 max_blobs_per_tx: 6,
3048 blob_base_cost: 0,
3049 },
3050 ..Default::default()
3051 };
3052 assert_eq!(hardfork_params, expected);
3053 }
3054
3055 #[test]
3056 fn parse_perf_net_genesis() {
3057 let s = r#"{
3058 "config": {
3059 "chainId": 1,
3060 "homesteadBlock": 1150000,
3061 "daoForkBlock": 1920000,
3062 "daoForkSupport": true,
3063 "eip150Block": 2463000,
3064 "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
3065 "eip155Block": 2675000,
3066 "eip158Block": 2675000,
3067 "byzantiumBlock": 4370000,
3068 "constantinopleBlock": 7280000,
3069 "petersburgBlock": 7280000,
3070 "istanbulBlock": 9069000,
3071 "muirGlacierBlock": 9200000,
3072 "berlinBlock": 12244000,
3073 "londonBlock": 12965000,
3074 "arrowGlacierBlock": 13773000,
3075 "grayGlacierBlock": 15050000,
3076 "terminalTotalDifficulty": 58750000000000000000000,
3077 "terminalTotalDifficultyPassed": true,
3078 "shanghaiTime": 1681338455,
3079 "cancunTime": 1710338135,
3080 "pragueTime": 1746612311,
3081 "ethash": {},
3082 "depositContractAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa",
3083 "blobSchedule": {
3084 "cancun": {
3085 "target": 3,
3086 "max": 6,
3087 "baseFeeUpdateFraction": 3338477
3088 },
3089 "prague": {
3090 "target": 6,
3091 "max": 9,
3092 "baseFeeUpdateFraction": 5007716
3093 }
3094 }
3095 },
3096 "nonce": "0x42",
3097 "timestamp": "0x0",
3098 "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
3099 "gasLimit": "0x1388",
3100 "difficulty": "0x400000000",
3101 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
3102 "coinbase": "0x0000000000000000000000000000000000000000",
3103 "number": "0x0",
3104 "gasUsed": "0x0",
3105 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
3106 "baseFeePerGas": null
3107}"#;
3108
3109 let genesis = serde_json::from_str::<Genesis>(s).unwrap();
3110 let chainspec = ChainSpec::from_genesis(genesis);
3111 let activation = chainspec.hardforks.fork(EthereumHardfork::Paris);
3112 assert_eq!(
3113 activation,
3114 ForkCondition::TTD {
3115 activation_block_number: MAINNET_PARIS_BLOCK,
3116 total_difficulty: MAINNET_PARIS_TTD,
3117 fork_block: None,
3118 }
3119 )
3120 }
3121}