1pub use alloy_eips::eip1559::BaseFeeParams;
2
3use crate::{constants::MAINNET_DEPOSIT_CONTRACT, once_cell_set, EthChainSpec, LazyLock, OnceLock};
4use alloc::{boxed::Box, sync::Arc, vec::Vec};
5use alloy_chains::{Chain, NamedChain};
6use alloy_consensus::{
7 constants::{
8 DEV_GENESIS_HASH, EMPTY_WITHDRAWALS, HOLESKY_GENESIS_HASH, MAINNET_GENESIS_HASH,
9 SEPOLIA_GENESIS_HASH,
10 },
11 Header,
12};
13use alloy_eips::{
14 eip1559::INITIAL_BASE_FEE, eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS,
15 eip7685::EMPTY_REQUESTS_HASH,
16};
17use alloy_genesis::Genesis;
18use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256};
19use derive_more::From;
20use reth_ethereum_forks::{
21 ChainHardforks, DisplayHardforks, EthereumHardfork, EthereumHardforks, ForkCondition,
22 ForkFilter, ForkFilterKey, ForkHash, ForkId, Hardfork, Hardforks, Head, DEV_HARDFORKS,
23};
24use reth_network_peers::{
25 base_nodes, base_testnet_nodes, holesky_nodes, mainnet_nodes, op_nodes, op_testnet_nodes,
26 sepolia_nodes, NodeRecord,
27};
28use reth_primitives_traits::SealedHeader;
29use reth_trie_common::root::state_root_ref_unhashed;
30
31pub static MAINNET: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
33 let mut spec = ChainSpec {
34 chain: Chain::mainnet(),
35 genesis: serde_json::from_str(include_str!("../res/genesis/mainnet.json"))
36 .expect("Can't deserialize Mainnet genesis json"),
37 genesis_hash: once_cell_set(MAINNET_GENESIS_HASH),
38 genesis_header: Default::default(),
39 paris_block_and_final_difficulty: Some((
41 15537394,
42 U256::from(58_750_003_716_598_352_816_469u128),
43 )),
44 hardforks: EthereumHardfork::mainnet().into(),
45 deposit_contract: Some(DepositContract::new(
47 MAINNET_DEPOSIT_CONTRACT_ADDRESS,
48 11052984,
49 b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
50 )),
51 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
52 prune_delete_limit: 20000,
53 };
54 spec.genesis.config.dao_fork_support = true;
55 spec.into()
56});
57
58pub static SEPOLIA: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
60 let mut spec = ChainSpec {
61 chain: Chain::sepolia(),
62 genesis: serde_json::from_str(include_str!("../res/genesis/sepolia.json"))
63 .expect("Can't deserialize Sepolia genesis json"),
64 genesis_hash: once_cell_set(SEPOLIA_GENESIS_HASH),
65 genesis_header: Default::default(),
66 paris_block_and_final_difficulty: Some((1450409, U256::from(17_000_018_015_853_232u128))),
68 hardforks: EthereumHardfork::sepolia().into(),
69 deposit_contract: Some(DepositContract::new(
71 address!("7f02c3e3c98b133055b8b348b2ac625669ed295d"),
72 1273020,
73 b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
74 )),
75 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
76 prune_delete_limit: 10000,
77 };
78 spec.genesis.config.dao_fork_support = true;
79 spec.into()
80});
81
82pub static HOLESKY: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
84 let mut spec = ChainSpec {
85 chain: Chain::holesky(),
86 genesis: serde_json::from_str(include_str!("../res/genesis/holesky.json"))
87 .expect("Can't deserialize Holesky genesis json"),
88 genesis_hash: once_cell_set(HOLESKY_GENESIS_HASH),
89 genesis_header: Default::default(),
90 paris_block_and_final_difficulty: Some((0, U256::from(1))),
91 hardforks: EthereumHardfork::holesky().into(),
92 deposit_contract: Some(DepositContract::new(
93 address!("4242424242424242424242424242424242424242"),
94 0,
95 b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
96 )),
97 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
98 prune_delete_limit: 10000,
99 };
100 spec.genesis.config.dao_fork_support = true;
101 spec.into()
102});
103
104pub static DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
109 ChainSpec {
110 chain: Chain::dev(),
111 genesis: serde_json::from_str(include_str!("../res/genesis/dev.json"))
112 .expect("Can't deserialize Dev testnet genesis json"),
113 genesis_hash: once_cell_set(DEV_GENESIS_HASH),
114 paris_block_and_final_difficulty: Some((0, U256::from(0))),
115 hardforks: DEV_HARDFORKS.clone(),
116 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
117 deposit_contract: None, ..Default::default()
119 }
120 .into()
121});
122
123pub static SEISMIC_DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
125 ChainSpec {
126 chain: Chain::from_id(5124),
127 genesis: serde_json::from_str(include_str!("../res/genesis/dev.json"))
128 .expect("Can't deserialize Dev testnet genesis json"),
129 genesis_hash: once_cell_set(DEV_GENESIS_HASH),
130 paris_block_and_final_difficulty: Some((0, U256::from(0))),
131 hardforks: DEV_HARDFORKS.clone(),
132 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
133 deposit_contract: None, ..Default::default()
135 }
136 .into()
137});
138
139pub static SEISMIC_MAINNET: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
141 let mut spec = ChainSpec {
142 chain: Chain::from_id(5123),
143 genesis: serde_json::from_str(include_str!("../res/genesis/mainnet.json"))
144 .expect("Can't deserialize Mainnet genesis json"),
145 genesis_hash: once_cell_set(MAINNET_GENESIS_HASH),
146 genesis_header: Default::default(),
147 paris_block_and_final_difficulty: Some((
149 15537394,
150 U256::from(58_750_003_716_598_352_816_469u128),
151 )),
152 hardforks: EthereumHardfork::mainnet().into(),
153 deposit_contract: Some(DepositContract::new(
155 MAINNET_DEPOSIT_CONTRACT_ADDRESS,
156 11052984,
157 b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
158 )),
159 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
160 prune_delete_limit: 20000,
161 };
162 spec.genesis.config.dao_fork_support = true;
163 spec.into()
164});
165
166#[derive(Clone, Debug, PartialEq, Eq)]
169pub enum BaseFeeParamsKind {
170 Constant(BaseFeeParams),
172 Variable(ForkBaseFeeParams),
175}
176
177impl Default for BaseFeeParamsKind {
178 fn default() -> Self {
179 BaseFeeParams::ethereum().into()
180 }
181}
182
183impl From<BaseFeeParams> for BaseFeeParamsKind {
184 fn from(params: BaseFeeParams) -> Self {
185 Self::Constant(params)
186 }
187}
188
189impl From<ForkBaseFeeParams> for BaseFeeParamsKind {
190 fn from(params: ForkBaseFeeParams) -> Self {
191 Self::Variable(params)
192 }
193}
194
195#[derive(Clone, Debug, PartialEq, Eq, From)]
198pub struct ForkBaseFeeParams(Vec<(Box<dyn Hardfork>, BaseFeeParams)>);
199
200impl core::ops::Deref for ChainSpec {
201 type Target = ChainHardforks;
202
203 fn deref(&self) -> &Self::Target {
204 &self.hardforks
205 }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq)]
216pub struct ChainSpec {
217 pub chain: Chain,
219
220 pub genesis: Genesis,
222
223 pub genesis_hash: OnceLock<B256>,
228
229 pub genesis_header: OnceLock<Header>,
234
235 pub paris_block_and_final_difficulty: Option<(u64, U256)>,
238
239 pub hardforks: ChainHardforks,
241
242 pub deposit_contract: Option<DepositContract>,
244
245 pub base_fee_params: BaseFeeParamsKind,
247
248 pub prune_delete_limit: usize,
250}
251
252impl Default for ChainSpec {
253 fn default() -> Self {
254 Self {
255 chain: Default::default(),
256 genesis_hash: Default::default(),
257 genesis: Default::default(),
258 genesis_header: Default::default(),
259 paris_block_and_final_difficulty: Default::default(),
260 hardforks: Default::default(),
261 deposit_contract: Default::default(),
262 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
263 prune_delete_limit: MAINNET.prune_delete_limit,
264 }
265 }
266}
267
268impl ChainSpec {
269 pub const fn chain(&self) -> Chain {
271 self.chain
272 }
273
274 #[inline]
276 pub const fn is_ethereum(&self) -> bool {
277 self.chain.is_ethereum()
278 }
279
280 #[inline]
282 pub fn is_optimism_mainnet(&self) -> bool {
283 self.chain == Chain::optimism_mainnet()
284 }
285
286 pub const fn genesis(&self) -> &Genesis {
290 &self.genesis
291 }
292
293 pub fn genesis_header(&self) -> &Header {
295 self.genesis_header.get_or_init(|| self.make_genesis_header())
296 }
297
298 fn make_genesis_header(&self) -> Header {
299 let base_fee_per_gas = self.initial_base_fee();
301
302 let withdrawals_root = self
305 .fork(EthereumHardfork::Shanghai)
306 .active_at_timestamp(self.genesis.timestamp)
307 .then_some(EMPTY_WITHDRAWALS);
308
309 let (parent_beacon_block_root, blob_gas_used, excess_blob_gas) =
314 if self.is_cancun_active_at_timestamp(self.genesis.timestamp) {
315 let blob_gas_used = self.genesis.blob_gas_used.unwrap_or(0);
316 let excess_blob_gas = self.genesis.excess_blob_gas.unwrap_or(0);
317 (Some(B256::ZERO), Some(blob_gas_used as u64), Some(excess_blob_gas as u64))
318 } else {
319 (None, None, None)
320 };
321
322 let requests_hash = self
324 .is_prague_active_at_timestamp(self.genesis.timestamp)
325 .then_some(EMPTY_REQUESTS_HASH);
326
327 Header {
328 gas_limit: self.genesis.gas_limit,
329 difficulty: self.genesis.difficulty,
330 nonce: self.genesis.nonce.into(),
331 extra_data: self.genesis.extra_data.clone(),
332 state_root: state_root_ref_unhashed(&self.genesis.alloc),
333 timestamp: self.genesis.timestamp,
334 mix_hash: self.genesis.mix_hash,
335 beneficiary: self.genesis.coinbase,
336 base_fee_per_gas: base_fee_per_gas.map(Into::into),
337 withdrawals_root,
338 parent_beacon_block_root,
339 blob_gas_used: blob_gas_used.map(Into::into),
340 excess_blob_gas: excess_blob_gas.map(Into::into),
341 requests_hash,
342 ..Default::default()
343 }
344 }
345
346 pub fn sealed_genesis_header(&self) -> SealedHeader {
348 SealedHeader::new(self.genesis_header().clone(), self.genesis_hash())
349 }
350
351 pub fn initial_base_fee(&self) -> Option<u64> {
353 let genesis_base_fee =
355 self.genesis.base_fee_per_gas.map(|fee| fee as u64).unwrap_or(INITIAL_BASE_FEE);
356
357 self.hardforks.fork(EthereumHardfork::London).active_at_block(0).then_some(genesis_base_fee)
359 }
360
361 pub fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams {
363 match self.base_fee_params {
364 BaseFeeParamsKind::Constant(bf_params) => bf_params,
365 BaseFeeParamsKind::Variable(ForkBaseFeeParams(ref bf_params)) => {
366 for (fork, params) in bf_params.iter().rev() {
370 if self.hardforks.is_fork_active_at_timestamp(fork.clone(), timestamp) {
371 return *params
372 }
373 }
374
375 bf_params.first().map(|(_, params)| *params).unwrap_or(BaseFeeParams::ethereum())
376 }
377 }
378 }
379
380 pub fn base_fee_params_at_block(&self, block_number: u64) -> BaseFeeParams {
382 match self.base_fee_params {
383 BaseFeeParamsKind::Constant(bf_params) => bf_params,
384 BaseFeeParamsKind::Variable(ForkBaseFeeParams(ref bf_params)) => {
385 for (fork, params) in bf_params.iter().rev() {
389 if self.hardforks.is_fork_active_at_block(fork.clone(), block_number) {
390 return *params
391 }
392 }
393
394 bf_params.first().map(|(_, params)| *params).unwrap_or(BaseFeeParams::ethereum())
395 }
396 }
397 }
398
399 pub fn genesis_hash(&self) -> B256 {
401 *self.genesis_hash.get_or_init(|| self.genesis_header().hash_slow())
402 }
403
404 pub const fn genesis_timestamp(&self) -> u64 {
406 self.genesis.timestamp
407 }
408
409 pub fn get_final_paris_total_difficulty(&self) -> Option<U256> {
411 self.paris_block_and_final_difficulty.map(|(_, final_difficulty)| final_difficulty)
412 }
413
414 #[inline]
419 pub fn final_paris_total_difficulty(&self, block_number: u64) -> Option<U256> {
420 self.paris_block_and_final_difficulty.and_then(|(activated_at, final_difficulty)| {
421 (block_number >= activated_at).then_some(final_difficulty)
422 })
423 }
424
425 pub fn hardfork_fork_filter<H: Hardfork + Clone>(&self, fork: H) -> Option<ForkFilter> {
427 match self.hardforks.fork(fork.clone()) {
428 ForkCondition::Never => None,
429 _ => Some(self.fork_filter(self.satisfy(self.hardforks.fork(fork)))),
430 }
431 }
432
433 pub fn display_hardforks(&self) -> DisplayHardforks {
435 DisplayHardforks::new(&self, self.paris_block_and_final_difficulty.map(|(block, _)| block))
436 }
437
438 #[inline]
440 pub fn hardfork_fork_id<H: Hardfork + Clone>(&self, fork: H) -> Option<ForkId> {
441 let condition = self.hardforks.fork(fork);
442 match condition {
443 ForkCondition::Never => None,
444 _ => Some(self.fork_id(&self.satisfy(condition))),
445 }
446 }
447
448 #[inline]
451 pub fn shanghai_fork_id(&self) -> Option<ForkId> {
452 self.hardfork_fork_id(EthereumHardfork::Shanghai)
453 }
454
455 #[inline]
458 pub fn cancun_fork_id(&self) -> Option<ForkId> {
459 self.hardfork_fork_id(EthereumHardfork::Cancun)
460 }
461
462 #[inline]
465 pub fn latest_fork_id(&self) -> ForkId {
466 self.hardfork_fork_id(self.hardforks.last().unwrap().0).unwrap()
467 }
468
469 pub fn fork_filter(&self, head: Head) -> ForkFilter {
471 let forks = self.hardforks.forks_iter().filter_map(|(_, condition)| {
472 Some(match condition {
475 ForkCondition::Block(block) |
476 ForkCondition::TTD { fork_block: Some(block), .. } => ForkFilterKey::Block(block),
477 ForkCondition::Timestamp(time) => ForkFilterKey::Time(time),
478 _ => return None,
479 })
480 });
481
482 ForkFilter::new(head, self.genesis_hash(), self.genesis_timestamp(), forks)
483 }
484
485 pub fn fork_id(&self, head: &Head) -> ForkId {
487 let mut forkhash = ForkHash::from(self.genesis_hash());
488 let mut current_applied = 0;
489
490 for (_, cond) in self.hardforks.forks_iter() {
492 if let ForkCondition::Block(block) |
495 ForkCondition::TTD { fork_block: Some(block), .. } = cond
496 {
497 if cond.active_at_head(head) {
498 if block != current_applied {
499 forkhash += block;
500 current_applied = block;
501 }
502 } else {
503 return ForkId { hash: forkhash, next: block }
506 }
507 }
508 }
509
510 for timestamp in self.hardforks.forks_iter().filter_map(|(_, cond)| {
514 cond.as_timestamp().filter(|time| time > &self.genesis.timestamp)
515 }) {
516 let cond = ForkCondition::Timestamp(timestamp);
517 if cond.active_at_head(head) {
518 if timestamp != current_applied {
519 forkhash += timestamp;
520 current_applied = timestamp;
521 }
522 } else {
523 return ForkId { hash: forkhash, next: timestamp }
527 }
528 }
529
530 ForkId { hash: forkhash, next: 0 }
531 }
532
533 pub(crate) fn satisfy(&self, cond: ForkCondition) -> Head {
535 match cond {
536 ForkCondition::Block(number) => Head { number, ..Default::default() },
537 ForkCondition::Timestamp(timestamp) => {
538 Head {
541 timestamp,
542 number: self.last_block_fork_before_merge_or_timestamp().unwrap_or_default(),
543 ..Default::default()
544 }
545 }
546 ForkCondition::TTD { total_difficulty, .. } => {
547 Head { total_difficulty, ..Default::default() }
548 }
549 ForkCondition::Never => unreachable!(),
550 }
551 }
552
553 pub(crate) fn last_block_fork_before_merge_or_timestamp(&self) -> Option<u64> {
565 let mut hardforks_iter = self.hardforks.forks_iter().peekable();
566 while let Some((_, curr_cond)) = hardforks_iter.next() {
567 if let Some((_, next_cond)) = hardforks_iter.peek() {
568 match next_cond {
572 ForkCondition::TTD { fork_block: Some(block), .. } => return Some(*block),
575
576 ForkCondition::TTD { .. } | ForkCondition::Timestamp(_) => {
579 if let ForkCondition::Block(block_num) = curr_cond {
582 return Some(block_num);
583 }
584 }
585 ForkCondition::Block(_) | ForkCondition::Never => continue,
586 }
587 }
588 }
589 None
590 }
591
592 pub fn builder() -> ChainSpecBuilder {
594 ChainSpecBuilder::default()
595 }
596
597 pub fn bootnodes(&self) -> Option<Vec<NodeRecord>> {
599 use NamedChain as C;
600 let chain = self.chain;
601 match chain.try_into().ok()? {
602 C::Mainnet => Some(mainnet_nodes()),
603 C::Sepolia => Some(sepolia_nodes()),
604 C::Holesky => Some(holesky_nodes()),
605 C::Base => Some(base_nodes()),
606 C::Optimism => Some(op_nodes()),
607 C::BaseGoerli | C::BaseSepolia => Some(base_testnet_nodes()),
608 C::OptimismSepolia | C::OptimismGoerli | C::OptimismKovan => Some(op_testnet_nodes()),
609 _ => None,
610 }
611 }
612
613 pub fn is_seismic(&self) -> bool {
615 self.chain == SEISMIC_DEV.chain || self.chain == SEISMIC_MAINNET.chain
616 }
617}
618
619impl From<Genesis> for ChainSpec {
620 fn from(genesis: Genesis) -> Self {
621 let hardfork_opts = [
623 (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block),
624 (EthereumHardfork::Dao.boxed(), genesis.config.dao_fork_block),
625 (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block),
626 (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block),
627 (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block),
628 (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block),
629 (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block),
630 (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block),
631 (EthereumHardfork::MuirGlacier.boxed(), genesis.config.muir_glacier_block),
632 (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block),
633 (EthereumHardfork::London.boxed(), genesis.config.london_block),
634 (EthereumHardfork::ArrowGlacier.boxed(), genesis.config.arrow_glacier_block),
635 (EthereumHardfork::GrayGlacier.boxed(), genesis.config.gray_glacier_block),
636 ];
637 let mut hardforks = hardfork_opts
638 .into_iter()
639 .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block))))
640 .collect::<Vec<_>>();
641
642 let paris_block_and_final_difficulty =
644 if let Some(ttd) = genesis.config.terminal_total_difficulty {
645 hardforks.push((
646 EthereumHardfork::Paris.boxed(),
647 ForkCondition::TTD {
648 total_difficulty: ttd,
649 fork_block: genesis.config.merge_netsplit_block,
650 },
651 ));
652
653 genesis.config.merge_netsplit_block.map(|block| (block, ttd))
654 } else {
655 None
656 };
657
658 let time_hardfork_opts = [
660 (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time),
661 (EthereumHardfork::Cancun.boxed(), genesis.config.cancun_time),
662 (EthereumHardfork::Prague.boxed(), genesis.config.prague_time),
663 (EthereumHardfork::Osaka.boxed(), genesis.config.osaka_time),
664 ];
665
666 let mut time_hardforks = time_hardfork_opts
667 .into_iter()
668 .filter_map(|(hardfork, opt)| {
669 opt.map(|time| (hardfork, ForkCondition::Timestamp(time)))
670 })
671 .collect::<Vec<_>>();
672
673 hardforks.append(&mut time_hardforks);
674
675 let mainnet_hardforks: ChainHardforks = EthereumHardfork::mainnet().into();
677 let mainnet_order = mainnet_hardforks.forks_iter();
678
679 let mut ordered_hardforks = Vec::with_capacity(hardforks.len());
680 for (hardfork, _) in mainnet_order {
681 if let Some(pos) = hardforks.iter().position(|(e, _)| **e == *hardfork) {
682 ordered_hardforks.push(hardforks.remove(pos));
683 }
684 }
685
686 ordered_hardforks.append(&mut hardforks);
688
689 let deposit_contract = genesis.config.deposit_contract_address.map(|address| {
694 DepositContract { address, block: 0, topic: MAINNET_DEPOSIT_CONTRACT.topic }
695 });
696
697 Self {
698 chain: genesis.config.chain_id.into(),
699 genesis,
700 genesis_hash: OnceLock::new(),
701 hardforks: ChainHardforks::new(ordered_hardforks),
702 paris_block_and_final_difficulty,
703 deposit_contract,
704 ..Default::default()
705 }
706 }
707}
708
709impl Hardforks for ChainSpec {
710 fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition {
711 self.hardforks.fork(fork)
712 }
713
714 fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
715 self.hardforks.forks_iter()
716 }
717
718 fn fork_id(&self, head: &Head) -> ForkId {
719 self.fork_id(head)
720 }
721
722 fn latest_fork_id(&self) -> ForkId {
723 self.latest_fork_id()
724 }
725
726 fn fork_filter(&self, head: Head) -> ForkFilter {
727 self.fork_filter(head)
728 }
729}
730
731impl EthereumHardforks for ChainSpec {
732 fn get_final_paris_total_difficulty(&self) -> Option<U256> {
733 self.get_final_paris_total_difficulty()
734 }
735
736 fn final_paris_total_difficulty(&self, block_number: u64) -> Option<U256> {
737 self.final_paris_total_difficulty(block_number)
738 }
739}
740
741#[auto_impl::auto_impl(&, Arc)]
743pub trait ChainSpecProvider: Send + Sync {
744 type ChainSpec: EthChainSpec + 'static;
746
747 fn chain_spec(&self) -> Arc<Self::ChainSpec>;
749}
750
751#[derive(Debug, Default, Clone)]
753pub struct ChainSpecBuilder {
754 chain: Option<Chain>,
755 genesis: Option<Genesis>,
756 hardforks: ChainHardforks,
757}
758
759impl ChainSpecBuilder {
760 pub fn mainnet() -> Self {
762 Self {
763 chain: Some(MAINNET.chain),
764 genesis: Some(MAINNET.genesis.clone()),
765 hardforks: MAINNET.hardforks.clone(),
766 }
767 }
768}
769
770impl ChainSpecBuilder {
771 pub const fn chain(mut self, chain: Chain) -> Self {
773 self.chain = Some(chain);
774 self
775 }
776
777 pub fn genesis(mut self, genesis: Genesis) -> Self {
779 self.genesis = Some(genesis);
780 self
781 }
782
783 pub fn with_fork<H: Hardfork>(mut self, fork: H, condition: ForkCondition) -> Self {
785 self.hardforks.insert(fork, condition);
786 self
787 }
788
789 pub fn with_forks(mut self, forks: ChainHardforks) -> Self {
791 self.hardforks = forks;
792 self
793 }
794
795 pub fn without_fork<H: Hardfork>(mut self, fork: H) -> Self {
797 self.hardforks.remove(fork);
798 self
799 }
800
801 pub fn paris_at_ttd(self, ttd: U256) -> Self {
805 self.with_fork(
806 EthereumHardfork::Paris,
807 ForkCondition::TTD { total_difficulty: ttd, fork_block: None },
808 )
809 }
810
811 pub fn frontier_activated(mut self) -> Self {
813 self.hardforks.insert(EthereumHardfork::Frontier, ForkCondition::Block(0));
814 self
815 }
816
817 pub fn homestead_activated(mut self) -> Self {
819 self = self.frontier_activated();
820 self.hardforks.insert(EthereumHardfork::Homestead, ForkCondition::Block(0));
821 self
822 }
823
824 pub fn tangerine_whistle_activated(mut self) -> Self {
826 self = self.homestead_activated();
827 self.hardforks.insert(EthereumHardfork::Tangerine, ForkCondition::Block(0));
828 self
829 }
830
831 pub fn spurious_dragon_activated(mut self) -> Self {
833 self = self.tangerine_whistle_activated();
834 self.hardforks.insert(EthereumHardfork::SpuriousDragon, ForkCondition::Block(0));
835 self
836 }
837
838 pub fn byzantium_activated(mut self) -> Self {
840 self = self.spurious_dragon_activated();
841 self.hardforks.insert(EthereumHardfork::Byzantium, ForkCondition::Block(0));
842 self
843 }
844
845 pub fn constantinople_activated(mut self) -> Self {
847 self = self.byzantium_activated();
848 self.hardforks.insert(EthereumHardfork::Constantinople, ForkCondition::Block(0));
849 self
850 }
851
852 pub fn petersburg_activated(mut self) -> Self {
854 self = self.constantinople_activated();
855 self.hardforks.insert(EthereumHardfork::Petersburg, ForkCondition::Block(0));
856 self
857 }
858
859 pub fn istanbul_activated(mut self) -> Self {
861 self = self.petersburg_activated();
862 self.hardforks.insert(EthereumHardfork::Istanbul, ForkCondition::Block(0));
863 self
864 }
865
866 pub fn berlin_activated(mut self) -> Self {
868 self = self.istanbul_activated();
869 self.hardforks.insert(EthereumHardfork::Berlin, ForkCondition::Block(0));
870 self
871 }
872
873 pub fn london_activated(mut self) -> Self {
875 self = self.berlin_activated();
876 self.hardforks.insert(EthereumHardfork::London, ForkCondition::Block(0));
877 self
878 }
879
880 pub fn paris_activated(mut self) -> Self {
882 self = self.london_activated();
883 self.hardforks.insert(
884 EthereumHardfork::Paris,
885 ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO },
886 );
887 self
888 }
889
890 pub fn shanghai_activated(mut self) -> Self {
892 self = self.paris_activated();
893 self.hardforks.insert(EthereumHardfork::Shanghai, ForkCondition::Timestamp(0));
894 self
895 }
896
897 pub fn cancun_activated(mut self) -> Self {
899 self = self.shanghai_activated();
900 self.hardforks.insert(EthereumHardfork::Cancun, ForkCondition::Timestamp(0));
901 self
902 }
903
904 pub fn prague_activated(mut self) -> Self {
906 self = self.cancun_activated();
907 self.hardforks.insert(EthereumHardfork::Prague, ForkCondition::Timestamp(0));
908 self
909 }
910
911 pub fn osaka_activated(mut self) -> Self {
913 self = self.prague_activated();
914 self.hardforks.insert(EthereumHardfork::Osaka, ForkCondition::Timestamp(0));
915 self
916 }
917
918 pub fn build(self) -> ChainSpec {
925 let paris_block_and_final_difficulty = {
926 self.hardforks.get(EthereumHardfork::Paris).and_then(|cond| {
927 if let ForkCondition::TTD { fork_block, total_difficulty } = cond {
928 fork_block.map(|fork_block| (fork_block, total_difficulty))
929 } else {
930 None
931 }
932 })
933 };
934 ChainSpec {
935 chain: self.chain.expect("The chain is required"),
936 genesis: self.genesis.expect("The genesis is required"),
937 genesis_hash: OnceLock::new(),
938 hardforks: self.hardforks,
939 paris_block_and_final_difficulty,
940 deposit_contract: None,
941 ..Default::default()
942 }
943 }
944}
945
946impl From<&Arc<ChainSpec>> for ChainSpecBuilder {
947 fn from(value: &Arc<ChainSpec>) -> Self {
948 Self {
949 chain: Some(value.chain),
950 genesis: Some(value.genesis.clone()),
951 hardforks: value.hardforks.clone(),
952 }
953 }
954}
955
956#[derive(Debug, Clone, Copy, PartialEq, Eq)]
958pub struct DepositContract {
959 pub address: Address,
961 pub block: BlockNumber,
963 pub topic: B256,
965}
966
967impl DepositContract {
968 pub const fn new(address: Address, block: BlockNumber, topic: B256) -> Self {
970 Self { address, block, topic }
971 }
972}
973
974#[cfg(any(test, feature = "test-utils"))]
976pub fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) {
977 for (block, expected_id) in cases {
978 let computed_id = spec.fork_id(block);
979 assert_eq!(
980 expected_id, &computed_id,
981 "Expected fork ID {:?}, computed fork ID {:?} at block {}",
982 expected_id, computed_id, block.number
983 );
984 }
985}
986
987#[cfg(test)]
988mod tests {
989 use core::ops::Deref;
990 use std::{collections::HashMap, str::FromStr};
991
992 use alloy_chains::Chain;
993 use alloy_genesis::{ChainConfig, GenesisAccount};
994 use alloy_primitives::{b256, hex};
995 use alloy_trie::EMPTY_ROOT_HASH;
996 use reth_ethereum_forks::{ForkCondition, ForkHash, ForkId, Head};
997 use reth_trie_common::TrieAccount;
998
999 use super::*;
1000
1001 fn test_hardfork_fork_ids(spec: &ChainSpec, cases: &[(EthereumHardfork, ForkId)]) {
1002 for (hardfork, expected_id) in cases {
1003 if let Some(computed_id) = spec.hardfork_fork_id(*hardfork) {
1004 assert_eq!(
1005 expected_id, &computed_id,
1006 "Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for hardfork {hardfork}"
1007 );
1008 if matches!(hardfork, EthereumHardfork::Shanghai) {
1009 if let Some(shangai_id) = spec.shanghai_fork_id() {
1010 assert_eq!(
1011 expected_id, &shangai_id,
1012 "Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for Shanghai hardfork"
1013 );
1014 } else {
1015 panic!("Expected ForkCondition to return Some for Hardfork::Shanghai");
1016 }
1017 }
1018 }
1019 }
1020 }
1021
1022 #[test]
1023 fn test_hardfork_list_display_mainnet() {
1024 assert_eq!(
1025 MAINNET.display_hardforks().to_string(),
1026 "Pre-merge hard forks (block based):
1027- Frontier @0
1028- Homestead @1150000
1029- Dao @1920000
1030- Tangerine @2463000
1031- SpuriousDragon @2675000
1032- Byzantium @4370000
1033- Constantinople @7280000
1034- Petersburg @7280000
1035- Istanbul @9069000
1036- MuirGlacier @9200000
1037- Berlin @12244000
1038- London @12965000
1039- ArrowGlacier @13773000
1040- GrayGlacier @15050000
1041Merge hard forks:
1042- Paris @58750000000000000000000 (network is known to be merged)
1043Post-merge hard forks (timestamp based):
1044- Shanghai @1681338455
1045- Cancun @1710338135"
1046 );
1047 }
1048
1049 #[test]
1050 fn test_hardfork_list_ignores_disabled_forks() {
1051 let spec = ChainSpec::builder()
1052 .chain(Chain::mainnet())
1053 .genesis(Genesis::default())
1054 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1055 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Never)
1056 .build();
1057 assert_eq!(
1058 spec.display_hardforks().to_string(),
1059 "Pre-merge hard forks (block based):
1060- Frontier @0"
1061 );
1062 }
1063
1064 #[test]
1066 fn ignores_genesis_fork_blocks() {
1067 let spec = ChainSpec::builder()
1068 .chain(Chain::mainnet())
1069 .genesis(Genesis::default())
1070 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1071 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(0))
1072 .with_fork(EthereumHardfork::Tangerine, ForkCondition::Block(0))
1073 .with_fork(EthereumHardfork::SpuriousDragon, ForkCondition::Block(0))
1074 .with_fork(EthereumHardfork::Byzantium, ForkCondition::Block(0))
1075 .with_fork(EthereumHardfork::Constantinople, ForkCondition::Block(0))
1076 .with_fork(EthereumHardfork::Istanbul, ForkCondition::Block(0))
1077 .with_fork(EthereumHardfork::MuirGlacier, ForkCondition::Block(0))
1078 .with_fork(EthereumHardfork::Berlin, ForkCondition::Block(0))
1079 .with_fork(EthereumHardfork::London, ForkCondition::Block(0))
1080 .with_fork(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0))
1081 .with_fork(EthereumHardfork::GrayGlacier, ForkCondition::Block(0))
1082 .build();
1083
1084 assert_eq!(spec.deref().len(), 12, "12 forks should be active.");
1085 assert_eq!(
1086 spec.fork_id(&Head { number: 1, ..Default::default() }),
1087 ForkId { hash: ForkHash::from(spec.genesis_hash()), next: 0 },
1088 "the fork ID should be the genesis hash; forks at genesis are ignored for fork filters"
1089 );
1090 }
1091
1092 #[test]
1093 fn ignores_duplicate_fork_blocks() {
1094 let empty_genesis = Genesis::default();
1095 let unique_spec = ChainSpec::builder()
1096 .chain(Chain::mainnet())
1097 .genesis(empty_genesis.clone())
1098 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1099 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(1))
1100 .build();
1101
1102 let duplicate_spec = ChainSpec::builder()
1103 .chain(Chain::mainnet())
1104 .genesis(empty_genesis)
1105 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1106 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(1))
1107 .with_fork(EthereumHardfork::Tangerine, ForkCondition::Block(1))
1108 .build();
1109
1110 assert_eq!(
1111 unique_spec.fork_id(&Head { number: 2, ..Default::default() }),
1112 duplicate_spec.fork_id(&Head { number: 2, ..Default::default() }),
1113 "duplicate fork blocks should be deduplicated for fork filters"
1114 );
1115 }
1116
1117 #[test]
1118 fn test_chainspec_satisfy() {
1119 let empty_genesis = Genesis::default();
1120 let happy_path_case = ChainSpec::builder()
1122 .chain(Chain::mainnet())
1123 .genesis(empty_genesis.clone())
1124 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1125 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1126 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1127 .build();
1128 let happy_path_head = happy_path_case.satisfy(ForkCondition::Timestamp(11313123));
1129 let happy_path_expected = Head { number: 73, timestamp: 11313123, ..Default::default() };
1130 assert_eq!(
1131 happy_path_head, happy_path_expected,
1132 "expected satisfy() to return {happy_path_expected:#?}, but got {happy_path_head:#?} "
1133 );
1134 let multiple_timestamp_fork_case = ChainSpec::builder()
1136 .chain(Chain::mainnet())
1137 .genesis(empty_genesis.clone())
1138 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1139 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1140 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1141 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(11313398))
1142 .build();
1143 let multi_timestamp_head =
1144 multiple_timestamp_fork_case.satisfy(ForkCondition::Timestamp(11313398));
1145 let mult_timestamp_expected =
1146 Head { number: 73, timestamp: 11313398, ..Default::default() };
1147 assert_eq!(
1148 multi_timestamp_head, mult_timestamp_expected,
1149 "expected satisfy() to return {mult_timestamp_expected:#?}, but got {multi_timestamp_head:#?} "
1150 );
1151 let no_block_fork_case = ChainSpec::builder()
1153 .chain(Chain::mainnet())
1154 .genesis(empty_genesis.clone())
1155 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1156 .build();
1157 let no_block_fork_head = no_block_fork_case.satisfy(ForkCondition::Timestamp(11313123));
1158 let no_block_fork_expected = Head { number: 0, timestamp: 11313123, ..Default::default() };
1159 assert_eq!(
1160 no_block_fork_head, no_block_fork_expected,
1161 "expected satisfy() to return {no_block_fork_expected:#?}, but got {no_block_fork_head:#?} ",
1162 );
1163 let fork_cond_ttd_blocknum_case = ChainSpec::builder()
1165 .chain(Chain::mainnet())
1166 .genesis(empty_genesis.clone())
1167 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1168 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1169 .with_fork(
1170 EthereumHardfork::Paris,
1171 ForkCondition::TTD {
1172 fork_block: Some(101),
1173 total_difficulty: U256::from(10_790_000),
1174 },
1175 )
1176 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1177 .build();
1178 let fork_cond_ttd_blocknum_head =
1179 fork_cond_ttd_blocknum_case.satisfy(ForkCondition::Timestamp(11313123));
1180 let fork_cond_ttd_blocknum_expected =
1181 Head { number: 101, timestamp: 11313123, ..Default::default() };
1182 assert_eq!(
1183 fork_cond_ttd_blocknum_head, fork_cond_ttd_blocknum_expected,
1184 "expected satisfy() to return {fork_cond_ttd_blocknum_expected:#?}, but got {fork_cond_ttd_blocknum_expected:#?} ",
1185 );
1186
1187 let fork_cond_block_only_case = ChainSpec::builder()
1191 .chain(Chain::mainnet())
1192 .genesis(empty_genesis)
1193 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1194 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1195 .build();
1196 let fork_cond_block_only_head = fork_cond_block_only_case.satisfy(ForkCondition::Block(73));
1197 let fork_cond_block_only_expected = Head { number: 73, ..Default::default() };
1198 assert_eq!(
1199 fork_cond_block_only_head, fork_cond_block_only_expected,
1200 "expected satisfy() to return {fork_cond_block_only_expected:#?}, but got {fork_cond_block_only_head:#?} ",
1201 );
1202 let fork_cond_ttd_no_new_spec = fork_cond_block_only_case.satisfy(ForkCondition::TTD {
1205 fork_block: None,
1206 total_difficulty: U256::from(10_790_000),
1207 });
1208 let fork_cond_ttd_no_new_spec_expected =
1209 Head { total_difficulty: U256::from(10_790_000), ..Default::default() };
1210 assert_eq!(
1211 fork_cond_ttd_no_new_spec, fork_cond_ttd_no_new_spec_expected,
1212 "expected satisfy() to return {fork_cond_ttd_blocknum_expected:#?}, but got {fork_cond_ttd_blocknum_expected:#?} ",
1213 );
1214 }
1215
1216 #[test]
1217 fn mainnet_hardfork_fork_ids() {
1218 test_hardfork_fork_ids(
1219 &MAINNET,
1220 &[
1221 (
1222 EthereumHardfork::Frontier,
1223 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1224 ),
1225 (
1226 EthereumHardfork::Homestead,
1227 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1228 ),
1229 (
1230 EthereumHardfork::Dao,
1231 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1232 ),
1233 (
1234 EthereumHardfork::Tangerine,
1235 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1236 ),
1237 (
1238 EthereumHardfork::SpuriousDragon,
1239 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1240 ),
1241 (
1242 EthereumHardfork::Byzantium,
1243 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1244 ),
1245 (
1246 EthereumHardfork::Constantinople,
1247 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1248 ),
1249 (
1250 EthereumHardfork::Petersburg,
1251 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1252 ),
1253 (
1254 EthereumHardfork::Istanbul,
1255 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1256 ),
1257 (
1258 EthereumHardfork::MuirGlacier,
1259 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1260 ),
1261 (
1262 EthereumHardfork::Berlin,
1263 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1264 ),
1265 (
1266 EthereumHardfork::London,
1267 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1268 ),
1269 (
1270 EthereumHardfork::ArrowGlacier,
1271 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1272 ),
1273 (
1274 EthereumHardfork::GrayGlacier,
1275 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1276 ),
1277 (
1278 EthereumHardfork::Shanghai,
1279 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1280 ),
1281 (
1282 EthereumHardfork::Cancun,
1283 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 0 },
1284 ),
1285 ],
1286 );
1287 }
1288
1289 #[test]
1290 fn sepolia_hardfork_fork_ids() {
1291 test_hardfork_fork_ids(
1292 &SEPOLIA,
1293 &[
1294 (
1295 EthereumHardfork::Frontier,
1296 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1297 ),
1298 (
1299 EthereumHardfork::Homestead,
1300 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1301 ),
1302 (
1303 EthereumHardfork::Tangerine,
1304 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1305 ),
1306 (
1307 EthereumHardfork::SpuriousDragon,
1308 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1309 ),
1310 (
1311 EthereumHardfork::Byzantium,
1312 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1313 ),
1314 (
1315 EthereumHardfork::Constantinople,
1316 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1317 ),
1318 (
1319 EthereumHardfork::Petersburg,
1320 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1321 ),
1322 (
1323 EthereumHardfork::Istanbul,
1324 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1325 ),
1326 (
1327 EthereumHardfork::Berlin,
1328 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1329 ),
1330 (
1331 EthereumHardfork::London,
1332 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1333 ),
1334 (
1335 EthereumHardfork::Paris,
1336 ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
1337 ),
1338 (
1339 EthereumHardfork::Shanghai,
1340 ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
1341 ),
1342 (
1343 EthereumHardfork::Cancun,
1344 ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 0 },
1345 ),
1346 ],
1347 );
1348 }
1349
1350 #[test]
1351 fn mainnet_fork_ids() {
1352 test_fork_ids(
1353 &MAINNET,
1354 &[
1355 (
1356 Head { number: 0, ..Default::default() },
1357 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1358 ),
1359 (
1360 Head { number: 1150000, ..Default::default() },
1361 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1362 ),
1363 (
1364 Head { number: 1920000, ..Default::default() },
1365 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1366 ),
1367 (
1368 Head { number: 2463000, ..Default::default() },
1369 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1370 ),
1371 (
1372 Head { number: 2675000, ..Default::default() },
1373 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1374 ),
1375 (
1376 Head { number: 4370000, ..Default::default() },
1377 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1378 ),
1379 (
1380 Head { number: 7280000, ..Default::default() },
1381 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1382 ),
1383 (
1384 Head { number: 9069000, ..Default::default() },
1385 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1386 ),
1387 (
1388 Head { number: 9200000, ..Default::default() },
1389 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1390 ),
1391 (
1392 Head { number: 12244000, ..Default::default() },
1393 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1394 ),
1395 (
1396 Head { number: 12965000, ..Default::default() },
1397 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1398 ),
1399 (
1400 Head { number: 13773000, ..Default::default() },
1401 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1402 ),
1403 (
1404 Head { number: 15050000, ..Default::default() },
1405 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1406 ),
1407 (
1409 Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
1410 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1411 ),
1412 (
1414 Head { number: 20000001, timestamp: 1710338135, ..Default::default() },
1415 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 0 },
1416 ),
1417 (
1419 Head { number: 20000002, timestamp: 2000000000, ..Default::default() },
1420 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 0 },
1421 ),
1422 ],
1423 );
1424 }
1425
1426 #[test]
1427 fn holesky_fork_ids() {
1428 test_fork_ids(
1429 &HOLESKY,
1430 &[
1431 (
1432 Head { number: 0, ..Default::default() },
1433 ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
1434 ),
1435 (
1437 Head { number: 123, ..Default::default() },
1438 ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
1439 ),
1440 (
1442 Head { number: 123, timestamp: 1696000703, ..Default::default() },
1443 ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
1444 ),
1445 (
1447 Head { number: 123, timestamp: 1696000704, ..Default::default() },
1448 ForkId { hash: ForkHash([0xfd, 0x4f, 0x01, 0x6b]), next: 1707305664 },
1449 ),
1450 (
1452 Head { number: 123, timestamp: 1707305663, ..Default::default() },
1453 ForkId { hash: ForkHash([0xfd, 0x4f, 0x01, 0x6b]), next: 1707305664 },
1454 ),
1455 (
1457 Head { number: 123, timestamp: 1707305664, ..Default::default() },
1458 ForkId { hash: ForkHash([0x9b, 0x19, 0x2a, 0xd0]), next: 0 },
1459 ),
1460 ],
1461 )
1462 }
1463
1464 #[test]
1465 fn sepolia_fork_ids() {
1466 test_fork_ids(
1467 &SEPOLIA,
1468 &[
1469 (
1470 Head { number: 0, ..Default::default() },
1471 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1472 ),
1473 (
1474 Head { number: 1735370, ..Default::default() },
1475 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1476 ),
1477 (
1478 Head { number: 1735371, ..Default::default() },
1479 ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
1480 ),
1481 (
1482 Head { number: 1735372, timestamp: 1677557087, ..Default::default() },
1483 ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
1484 ),
1485 (
1487 Head { number: 1735372, timestamp: 1677557088, ..Default::default() },
1488 ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
1489 ),
1490 (
1492 Head { number: 1735372, timestamp: 1706655071, ..Default::default() },
1493 ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
1494 ),
1495 (
1497 Head { number: 1735372, timestamp: 1706655072, ..Default::default() },
1498 ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 0 },
1499 ),
1500 ],
1501 );
1502 }
1503
1504 #[test]
1505 fn dev_fork_ids() {
1506 test_fork_ids(
1507 &DEV,
1508 &[(
1509 Head { number: 0, ..Default::default() },
1510 ForkId { hash: ForkHash([0x45, 0xb8, 0x36, 0x12]), next: 0 },
1511 )],
1512 )
1513 }
1514
1515 #[test]
1519 fn timestamped_forks() {
1520 let mainnet_with_timestamps = ChainSpecBuilder::mainnet().build();
1521 test_fork_ids(
1522 &mainnet_with_timestamps,
1523 &[
1524 (
1525 Head { number: 0, timestamp: 0, ..Default::default() },
1526 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1527 ), (
1529 Head { number: 1149999, timestamp: 0, ..Default::default() },
1530 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1531 ), (
1533 Head { number: 1150000, timestamp: 0, ..Default::default() },
1534 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1535 ), (
1537 Head { number: 1919999, timestamp: 0, ..Default::default() },
1538 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1539 ), (
1541 Head { number: 1920000, timestamp: 0, ..Default::default() },
1542 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1543 ), (
1545 Head { number: 2462999, timestamp: 0, ..Default::default() },
1546 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1547 ), (
1549 Head { number: 2463000, timestamp: 0, ..Default::default() },
1550 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1551 ), (
1553 Head { number: 2674999, timestamp: 0, ..Default::default() },
1554 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1555 ), (
1557 Head { number: 2675000, timestamp: 0, ..Default::default() },
1558 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1559 ), (
1561 Head { number: 4369999, timestamp: 0, ..Default::default() },
1562 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1563 ), (
1565 Head { number: 4370000, timestamp: 0, ..Default::default() },
1566 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1567 ), (
1569 Head { number: 7279999, timestamp: 0, ..Default::default() },
1570 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1571 ), (
1573 Head { number: 7280000, timestamp: 0, ..Default::default() },
1574 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1575 ), (
1577 Head { number: 9068999, timestamp: 0, ..Default::default() },
1578 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1579 ), (
1581 Head { number: 9069000, timestamp: 0, ..Default::default() },
1582 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1583 ), (
1585 Head { number: 9199999, timestamp: 0, ..Default::default() },
1586 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1587 ), (
1589 Head { number: 9200000, timestamp: 0, ..Default::default() },
1590 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1591 ), (
1593 Head { number: 12243999, timestamp: 0, ..Default::default() },
1594 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1595 ), (
1597 Head { number: 12244000, timestamp: 0, ..Default::default() },
1598 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1599 ), (
1601 Head { number: 12964999, timestamp: 0, ..Default::default() },
1602 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1603 ), (
1605 Head { number: 12965000, timestamp: 0, ..Default::default() },
1606 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1607 ), (
1609 Head { number: 13772999, timestamp: 0, ..Default::default() },
1610 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1611 ), (
1613 Head { number: 13773000, timestamp: 0, ..Default::default() },
1614 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1615 ), (
1617 Head { number: 15049999, timestamp: 0, ..Default::default() },
1618 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1619 ), (
1621 Head { number: 15050000, timestamp: 0, ..Default::default() },
1622 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1623 ), (
1625 Head { number: 19999999, timestamp: 1667999999, ..Default::default() },
1626 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1627 ), (
1629 Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
1630 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1631 ), (
1633 Head { number: 20000001, timestamp: 1710338134, ..Default::default() },
1634 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1635 ), (
1637 Head { number: 20000002, timestamp: 1710338135, ..Default::default() },
1638 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 0 },
1639 ), (
1641 Head { number: 20000003, timestamp: 2000000000, ..Default::default() },
1642 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 0 },
1643 ),
1644 ],
1645 );
1646 }
1647
1648 fn construct_chainspec(
1651 builder: ChainSpecBuilder,
1652 shanghai_time: u64,
1653 cancun_time: u64,
1654 ) -> ChainSpec {
1655 builder
1656 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(shanghai_time))
1657 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(cancun_time))
1658 .build()
1659 }
1660
1661 #[test]
1666 fn test_timestamp_fork_in_genesis() {
1667 let timestamp = 1690475657u64;
1668 let default_spec_builder = ChainSpecBuilder::default()
1669 .chain(Chain::from_id(1337))
1670 .genesis(Genesis::default().with_timestamp(timestamp))
1671 .paris_activated();
1672
1673 let tests = [
1676 (
1677 construct_chainspec(default_spec_builder.clone(), timestamp - 1, timestamp + 1),
1678 timestamp + 1,
1679 ),
1680 (
1681 construct_chainspec(default_spec_builder.clone(), timestamp, timestamp + 1),
1682 timestamp + 1,
1683 ),
1684 (
1685 construct_chainspec(default_spec_builder, timestamp + 1, timestamp + 2),
1686 timestamp + 1,
1687 ),
1688 ];
1689
1690 for (spec, expected_timestamp) in tests {
1691 let got_forkid = spec.fork_id(&Head { number: 0, timestamp: 0, ..Default::default() });
1692 let genesis_hash = spec.genesis_hash();
1697 let expected_forkid =
1698 ForkId { hash: ForkHash::from(genesis_hash), next: expected_timestamp };
1699 assert_eq!(got_forkid, expected_forkid);
1700 }
1701 }
1702
1703 #[test]
1705 fn check_terminal_ttd() {
1706 let chainspec = ChainSpecBuilder::mainnet().build();
1707
1708 let terminal_block_ttd = U256::from(58750003716598352816469_u128);
1710 let terminal_block_difficulty = U256::from(11055787484078698_u128);
1711 assert!(!chainspec
1712 .fork(EthereumHardfork::Paris)
1713 .active_at_ttd(terminal_block_ttd, terminal_block_difficulty));
1714
1715 let first_pos_block_ttd = U256::from(58750003716598352816469_u128);
1717 let first_pos_difficulty = U256::ZERO;
1718 assert!(chainspec
1719 .fork(EthereumHardfork::Paris)
1720 .active_at_ttd(first_pos_block_ttd, first_pos_difficulty));
1721 }
1722
1723 #[test]
1724 fn geth_genesis_with_shanghai() {
1725 let geth_genesis = r#"
1726 {
1727 "config": {
1728 "chainId": 1337,
1729 "homesteadBlock": 0,
1730 "eip150Block": 0,
1731 "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1732 "eip155Block": 0,
1733 "eip158Block": 0,
1734 "byzantiumBlock": 0,
1735 "constantinopleBlock": 0,
1736 "petersburgBlock": 0,
1737 "istanbulBlock": 0,
1738 "muirGlacierBlock": 0,
1739 "berlinBlock": 0,
1740 "londonBlock": 0,
1741 "arrowGlacierBlock": 0,
1742 "grayGlacierBlock": 0,
1743 "shanghaiTime": 0,
1744 "cancunTime": 1,
1745 "terminalTotalDifficulty": 0,
1746 "terminalTotalDifficultyPassed": true,
1747 "ethash": {}
1748 },
1749 "nonce": "0x0",
1750 "timestamp": "0x0",
1751 "extraData": "0x",
1752 "gasLimit": "0x4c4b40",
1753 "difficulty": "0x1",
1754 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1755 "coinbase": "0x0000000000000000000000000000000000000000",
1756 "alloc": {
1757 "658bdf435d810c91414ec09147daa6db62406379": {
1758 "balance": "0x487a9a304539440000"
1759 },
1760 "aa00000000000000000000000000000000000000": {
1761 "code": "0x6042",
1762 "storage": {
1763 "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
1764 "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000",
1765 "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000",
1766 "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303"
1767 },
1768 "balance": "0x1",
1769 "nonce": "0x1"
1770 },
1771 "bb00000000000000000000000000000000000000": {
1772 "code": "0x600154600354",
1773 "storage": {
1774 "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
1775 "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000",
1776 "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000",
1777 "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303"
1778 },
1779 "balance": "0x2",
1780 "nonce": "0x1"
1781 }
1782 },
1783 "number": "0x0",
1784 "gasUsed": "0x0",
1785 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1786 "baseFeePerGas": "0x3b9aca00"
1787 }
1788 "#;
1789
1790 let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
1791 let chainspec = ChainSpec::from(genesis);
1792
1793 assert_eq!(
1795 chainspec.hardforks.get(EthereumHardfork::Homestead).unwrap(),
1796 ForkCondition::Block(0)
1797 );
1798 assert_eq!(
1799 chainspec.hardforks.get(EthereumHardfork::Tangerine).unwrap(),
1800 ForkCondition::Block(0)
1801 );
1802 assert_eq!(
1803 chainspec.hardforks.get(EthereumHardfork::SpuriousDragon).unwrap(),
1804 ForkCondition::Block(0)
1805 );
1806 assert_eq!(
1807 chainspec.hardforks.get(EthereumHardfork::Byzantium).unwrap(),
1808 ForkCondition::Block(0)
1809 );
1810 assert_eq!(
1811 chainspec.hardforks.get(EthereumHardfork::Constantinople).unwrap(),
1812 ForkCondition::Block(0)
1813 );
1814 assert_eq!(
1815 chainspec.hardforks.get(EthereumHardfork::Petersburg).unwrap(),
1816 ForkCondition::Block(0)
1817 );
1818 assert_eq!(
1819 chainspec.hardforks.get(EthereumHardfork::Istanbul).unwrap(),
1820 ForkCondition::Block(0)
1821 );
1822 assert_eq!(
1823 chainspec.hardforks.get(EthereumHardfork::MuirGlacier).unwrap(),
1824 ForkCondition::Block(0)
1825 );
1826 assert_eq!(
1827 chainspec.hardforks.get(EthereumHardfork::Berlin).unwrap(),
1828 ForkCondition::Block(0)
1829 );
1830 assert_eq!(
1831 chainspec.hardforks.get(EthereumHardfork::London).unwrap(),
1832 ForkCondition::Block(0)
1833 );
1834 assert_eq!(
1835 chainspec.hardforks.get(EthereumHardfork::ArrowGlacier).unwrap(),
1836 ForkCondition::Block(0)
1837 );
1838 assert_eq!(
1839 chainspec.hardforks.get(EthereumHardfork::GrayGlacier).unwrap(),
1840 ForkCondition::Block(0)
1841 );
1842
1843 assert_eq!(
1845 chainspec.hardforks.get(EthereumHardfork::Shanghai).unwrap(),
1846 ForkCondition::Timestamp(0)
1847 );
1848
1849 assert_eq!(
1851 chainspec.hardforks.get(EthereumHardfork::Cancun).unwrap(),
1852 ForkCondition::Timestamp(1)
1853 );
1854
1855 let key_rlp = vec![
1857 (hex!("658bdf435d810c91414ec09147daa6db62406379"), &hex!("f84d8089487a9a304539440000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")[..]),
1858 (hex!("aa00000000000000000000000000000000000000"), &hex!("f8440101a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0ce92c756baff35fa740c3557c1a971fd24d2d35b7c8e067880d50cd86bb0bc99")[..]),
1859 (hex!("bb00000000000000000000000000000000000000"), &hex!("f8440102a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0e25a53cbb501cec2976b393719c63d832423dd70a458731a0b64e4847bbca7d2")[..]),
1860 ];
1861
1862 for (key, expected_rlp) in key_rlp {
1863 let account = chainspec.genesis.alloc.get(&key).expect("account should exist");
1864 assert_eq!(&alloy_rlp::encode(TrieAccount::from(account.clone())), expected_rlp);
1865 }
1866
1867 assert_eq!(chainspec.genesis_hash.get(), None);
1868 let expected_state_root: B256 =
1869 hex!("078dc6061b1d8eaa8493384b59c9c65ceb917201221d08b80c4de6770b6ec7e7").into();
1870 assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
1871
1872 assert_eq!(chainspec.genesis_header().withdrawals_root, Some(EMPTY_ROOT_HASH));
1873
1874 let expected_hash: B256 =
1875 hex!("1fc027d65f820d3eef441ebeec139ebe09e471cf98516dce7b5643ccb27f418c").into();
1876 let hash = chainspec.genesis_hash();
1877 assert_eq!(hash, expected_hash);
1878 }
1879
1880 #[test]
1881 fn hive_geth_json() {
1882 let hive_json = r#"
1883 {
1884 "nonce": "0x0000000000000042",
1885 "difficulty": "0x2123456",
1886 "mixHash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
1887 "coinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1888 "timestamp": "0x123456",
1889 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1890 "extraData": "0xfafbfcfd",
1891 "gasLimit": "0x2fefd8",
1892 "alloc": {
1893 "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {
1894 "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1895 },
1896 "e6716f9544a56c530d868e4bfbacb172315bdead": {
1897 "balance": "0x11",
1898 "code": "0x12"
1899 },
1900 "b9c015918bdaba24b4ff057a92a3873d6eb201be": {
1901 "balance": "0x21",
1902 "storage": {
1903 "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22"
1904 }
1905 },
1906 "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {
1907 "balance": "0x31",
1908 "nonce": "0x32"
1909 },
1910 "0000000000000000000000000000000000000001": {
1911 "balance": "0x41"
1912 },
1913 "0000000000000000000000000000000000000002": {
1914 "balance": "0x51"
1915 },
1916 "0000000000000000000000000000000000000003": {
1917 "balance": "0x61"
1918 },
1919 "0000000000000000000000000000000000000004": {
1920 "balance": "0x71"
1921 }
1922 },
1923 "config": {
1924 "ethash": {},
1925 "chainId": 10,
1926 "homesteadBlock": 0,
1927 "eip150Block": 0,
1928 "eip155Block": 0,
1929 "eip158Block": 0,
1930 "byzantiumBlock": 0,
1931 "constantinopleBlock": 0,
1932 "petersburgBlock": 0,
1933 "istanbulBlock": 0
1934 }
1935 }
1936 "#;
1937
1938 let genesis = serde_json::from_str::<Genesis>(hive_json).unwrap();
1939 let chainspec: ChainSpec = genesis.into();
1940 assert_eq!(chainspec.genesis_hash.get(), None);
1941 assert_eq!(chainspec.chain, Chain::from_named(NamedChain::Optimism));
1942 let expected_state_root: B256 =
1943 hex!("9a6049ac535e3dc7436c189eaa81c73f35abd7f282ab67c32944ff0301d63360").into();
1944 assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
1945 let hard_forks = vec![
1946 EthereumHardfork::Byzantium,
1947 EthereumHardfork::Homestead,
1948 EthereumHardfork::Istanbul,
1949 EthereumHardfork::Petersburg,
1950 EthereumHardfork::Constantinople,
1951 ];
1952 for fork in hard_forks {
1953 assert_eq!(chainspec.hardforks.get(fork).unwrap(), ForkCondition::Block(0));
1954 }
1955
1956 let expected_hash: B256 =
1957 hex!("5ae31c6522bd5856129f66be3d582b842e4e9faaa87f21cce547128339a9db3c").into();
1958 let hash = chainspec.genesis_header().hash_slow();
1959 assert_eq!(hash, expected_hash);
1960 }
1961
1962 #[test]
1963 fn test_hive_paris_block_genesis_json() {
1964 let hive_paris = r#"
1967 {
1968 "config": {
1969 "ethash": {},
1970 "chainId": 3503995874084926,
1971 "homesteadBlock": 0,
1972 "eip150Block": 6,
1973 "eip155Block": 12,
1974 "eip158Block": 12,
1975 "byzantiumBlock": 18,
1976 "constantinopleBlock": 24,
1977 "petersburgBlock": 30,
1978 "istanbulBlock": 36,
1979 "muirGlacierBlock": 42,
1980 "berlinBlock": 48,
1981 "londonBlock": 54,
1982 "arrowGlacierBlock": 60,
1983 "grayGlacierBlock": 66,
1984 "mergeNetsplitBlock": 72,
1985 "terminalTotalDifficulty": 9454784,
1986 "shanghaiTime": 780,
1987 "cancunTime": 840
1988 },
1989 "nonce": "0x0",
1990 "timestamp": "0x0",
1991 "extraData": "0x68697665636861696e",
1992 "gasLimit": "0x23f3e20",
1993 "difficulty": "0x20000",
1994 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1995 "coinbase": "0x0000000000000000000000000000000000000000",
1996 "alloc": {
1997 "000f3df6d732807ef1319fb7b8bb8522d0beac02": {
1998 "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
1999 "balance": "0x2a"
2000 },
2001 "0c2c51a0990aee1d73c1228de158688341557508": {
2002 "balance": "0xc097ce7bc90715b34b9f1000000000"
2003 },
2004 "14e46043e63d0e3cdcf2530519f4cfaf35058cb2": {
2005 "balance": "0xc097ce7bc90715b34b9f1000000000"
2006 },
2007 "16c57edf7fa9d9525378b0b81bf8a3ced0620c1c": {
2008 "balance": "0xc097ce7bc90715b34b9f1000000000"
2009 },
2010 "1f4924b14f34e24159387c0a4cdbaa32f3ddb0cf": {
2011 "balance": "0xc097ce7bc90715b34b9f1000000000"
2012 },
2013 "1f5bde34b4afc686f136c7a3cb6ec376f7357759": {
2014 "balance": "0xc097ce7bc90715b34b9f1000000000"
2015 },
2016 "2d389075be5be9f2246ad654ce152cf05990b209": {
2017 "balance": "0xc097ce7bc90715b34b9f1000000000"
2018 },
2019 "3ae75c08b4c907eb63a8960c45b86e1e9ab6123c": {
2020 "balance": "0xc097ce7bc90715b34b9f1000000000"
2021 },
2022 "4340ee1b812acb40a1eb561c019c327b243b92df": {
2023 "balance": "0xc097ce7bc90715b34b9f1000000000"
2024 },
2025 "4a0f1452281bcec5bd90c3dce6162a5995bfe9df": {
2026 "balance": "0xc097ce7bc90715b34b9f1000000000"
2027 },
2028 "4dde844b71bcdf95512fb4dc94e84fb67b512ed8": {
2029 "balance": "0xc097ce7bc90715b34b9f1000000000"
2030 },
2031 "5f552da00dfb4d3749d9e62dcee3c918855a86a0": {
2032 "balance": "0xc097ce7bc90715b34b9f1000000000"
2033 },
2034 "654aa64f5fbefb84c270ec74211b81ca8c44a72e": {
2035 "balance": "0xc097ce7bc90715b34b9f1000000000"
2036 },
2037 "717f8aa2b982bee0e29f573d31df288663e1ce16": {
2038 "balance": "0xc097ce7bc90715b34b9f1000000000"
2039 },
2040 "7435ed30a8b4aeb0877cef0c6e8cffe834eb865f": {
2041 "balance": "0xc097ce7bc90715b34b9f1000000000"
2042 },
2043 "83c7e323d189f18725ac510004fdc2941f8c4a78": {
2044 "balance": "0xc097ce7bc90715b34b9f1000000000"
2045 },
2046 "84e75c28348fb86acea1a93a39426d7d60f4cc46": {
2047 "balance": "0xc097ce7bc90715b34b9f1000000000"
2048 },
2049 "8bebc8ba651aee624937e7d897853ac30c95a067": {
2050 "storage": {
2051 "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001",
2052 "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000002",
2053 "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000003"
2054 },
2055 "balance": "0x1",
2056 "nonce": "0x1"
2057 },
2058 "c7b99a164efd027a93f147376cc7da7c67c6bbe0": {
2059 "balance": "0xc097ce7bc90715b34b9f1000000000"
2060 },
2061 "d803681e487e6ac18053afc5a6cd813c86ec3e4d": {
2062 "balance": "0xc097ce7bc90715b34b9f1000000000"
2063 },
2064 "e7d13f7aa2a838d24c59b40186a0aca1e21cffcc": {
2065 "balance": "0xc097ce7bc90715b34b9f1000000000"
2066 },
2067 "eda8645ba6948855e3b3cd596bbb07596d59c603": {
2068 "balance": "0xc097ce7bc90715b34b9f1000000000"
2069 }
2070 },
2071 "number": "0x0",
2072 "gasUsed": "0x0",
2073 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2074 "baseFeePerGas": null,
2075 "excessBlobGas": null,
2076 "blobGasUsed": null
2077 }
2078 "#;
2079
2080 let genesis: Genesis = serde_json::from_str(hive_paris).unwrap();
2082 let chainspec = ChainSpec::from(genesis);
2083
2084 let expected_forkid = ForkId { hash: ForkHash([0xbc, 0x0c, 0x26, 0x05]), next: 0 };
2086 let got_forkid =
2087 chainspec.fork_id(&Head { number: 73, timestamp: 840, ..Default::default() });
2088
2089 assert_eq!(got_forkid, expected_forkid);
2091 assert_eq!(chainspec.paris_block_and_final_difficulty, Some((72, U256::from(9454784))));
2093 }
2094
2095 #[test]
2096 fn test_parse_genesis_json() {
2097 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"}"#;
2098 let genesis: Genesis = serde_json::from_str(s).unwrap();
2099 let acc = genesis
2100 .alloc
2101 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2102 .unwrap();
2103 assert_eq!(acc.balance, U256::from(1));
2104 assert_eq!(genesis.base_fee_per_gas, Some(0x1337));
2105 }
2106
2107 #[test]
2108 fn test_parse_cancun_genesis_json() {
2109 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"}"#;
2110 let genesis: Genesis = serde_json::from_str(s).unwrap();
2111 let acc = genesis
2112 .alloc
2113 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2114 .unwrap();
2115 assert_eq!(acc.balance, U256::from(1));
2116 assert_eq!(genesis.config.cancun_time, Some(4661));
2118 }
2119
2120 #[test]
2121 fn test_parse_prague_genesis_all_formats() {
2122 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"}"#;
2123 let genesis: Genesis = serde_json::from_str(s).unwrap();
2124
2125 let acc = genesis
2127 .alloc
2128 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2129 .unwrap();
2130 assert_eq!(acc.balance, U256::from(1));
2131 assert_eq!(genesis.config.cancun_time, Some(4661));
2133 assert_eq!(genesis.config.prague_time, Some(4662));
2135 }
2136
2137 #[test]
2138 fn test_parse_cancun_genesis_all_formats() {
2139 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"}"#;
2140 let genesis: Genesis = serde_json::from_str(s).unwrap();
2141
2142 let acc = genesis
2144 .alloc
2145 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2146 .unwrap();
2147 assert_eq!(acc.balance, U256::from(1));
2148 assert_eq!(genesis.config.cancun_time, Some(4661));
2150 }
2151
2152 #[test]
2153 fn test_paris_block_and_total_difficulty() {
2154 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2155 let paris_chainspec = ChainSpecBuilder::default()
2156 .chain(Chain::from_id(1337))
2157 .genesis(genesis)
2158 .paris_activated()
2159 .build();
2160 assert_eq!(paris_chainspec.paris_block_and_final_difficulty, Some((0, U256::ZERO)));
2161 }
2162
2163 #[test]
2164 fn test_default_cancun_header_forkhash() {
2165 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2167 let default_chainspec = ChainSpecBuilder::default()
2168 .chain(Chain::from_id(1337))
2169 .genesis(genesis)
2170 .cancun_activated()
2171 .build();
2172 let mut header = default_chainspec.genesis_header().clone();
2173
2174 header.state_root =
2176 B256::from_str("0x62e2595e017f0ca23e08d17221010721a71c3ae932f4ea3cb12117786bb392d4")
2177 .unwrap();
2178
2179 assert_eq!(header.withdrawals_root, Some(EMPTY_WITHDRAWALS));
2181
2182 assert_eq!(header.parent_beacon_block_root, Some(B256::ZERO));
2185 assert_eq!(header.blob_gas_used, Some(0));
2186 assert_eq!(header.excess_blob_gas, Some(0));
2187
2188 let genesis_hash = header.hash_slow();
2190 let expected_hash =
2191 b256!("16bb7c59613a5bad3f7c04a852fd056545ade2483968d9a25a1abb05af0c4d37");
2192 assert_eq!(genesis_hash, expected_hash);
2193
2194 let expected_forkhash = ForkHash(hex!("8062457a"));
2196 assert_eq!(ForkHash::from(genesis_hash), expected_forkhash);
2197 }
2198
2199 #[test]
2200 fn holesky_paris_activated_at_genesis() {
2201 assert!(HOLESKY
2202 .fork(EthereumHardfork::Paris)
2203 .active_at_ttd(HOLESKY.genesis.difficulty, HOLESKY.genesis.difficulty));
2204 }
2205
2206 #[test]
2207 fn test_genesis_format_deserialization() {
2208 let config = ChainConfig {
2210 chain_id: 2600,
2211 homestead_block: Some(0),
2212 eip150_block: Some(0),
2213 eip155_block: Some(0),
2214 eip158_block: Some(0),
2215 byzantium_block: Some(0),
2216 constantinople_block: Some(0),
2217 petersburg_block: Some(0),
2218 istanbul_block: Some(0),
2219 berlin_block: Some(0),
2220 london_block: Some(0),
2221 shanghai_time: Some(0),
2222 terminal_total_difficulty: Some(U256::ZERO),
2223 terminal_total_difficulty_passed: true,
2224 ..Default::default()
2225 };
2226 let genesis = Genesis {
2228 config,
2229 nonce: 0,
2230 timestamp: 1698688670,
2231 gas_limit: 5000,
2232 difficulty: U256::ZERO,
2233 mix_hash: B256::ZERO,
2234 coinbase: Address::ZERO,
2235 ..Default::default()
2236 };
2237
2238 let address = hex!("6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b").into();
2240 let account = GenesisAccount::default().with_balance(U256::from(33));
2241 let genesis = genesis.extend_accounts(HashMap::from([(address, account)]));
2242
2243 let serialized_genesis = serde_json::to_string(&genesis).unwrap();
2245 let deserialized_genesis: Genesis = serde_json::from_str(&serialized_genesis).unwrap();
2246
2247 assert_eq!(genesis, deserialized_genesis);
2248 }
2249
2250 #[test]
2251 fn check_fork_id_chainspec_with_fork_condition_never() {
2252 let spec = ChainSpec {
2253 chain: Chain::mainnet(),
2254 genesis: Genesis::default(),
2255 genesis_hash: OnceLock::new(),
2256 hardforks: ChainHardforks::new(vec![(
2257 EthereumHardfork::Frontier.boxed(),
2258 ForkCondition::Never,
2259 )]),
2260 paris_block_and_final_difficulty: None,
2261 deposit_contract: None,
2262 ..Default::default()
2263 };
2264
2265 assert_eq!(spec.hardfork_fork_id(EthereumHardfork::Frontier), None);
2266 }
2267
2268 #[test]
2269 fn check_fork_filter_chainspec_with_fork_condition_never() {
2270 let spec = ChainSpec {
2271 chain: Chain::mainnet(),
2272 genesis: Genesis::default(),
2273 genesis_hash: OnceLock::new(),
2274 hardforks: ChainHardforks::new(vec![(
2275 EthereumHardfork::Shanghai.boxed(),
2276 ForkCondition::Never,
2277 )]),
2278 paris_block_and_final_difficulty: None,
2279 deposit_contract: None,
2280 ..Default::default()
2281 };
2282
2283 assert_eq!(spec.hardfork_fork_filter(EthereumHardfork::Shanghai), None);
2284 }
2285
2286 #[test]
2287 fn latest_eth_mainnet_fork_id() {
2288 assert_eq!(
2289 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 0 },
2290 MAINNET.latest_fork_id()
2291 )
2292 }
2293
2294 #[test]
2295 fn test_fork_order_ethereum_mainnet() {
2296 let genesis = Genesis {
2297 config: ChainConfig {
2298 chain_id: 0,
2299 homestead_block: Some(0),
2300 dao_fork_block: Some(0),
2301 dao_fork_support: false,
2302 eip150_block: Some(0),
2303 eip155_block: Some(0),
2304 eip158_block: Some(0),
2305 byzantium_block: Some(0),
2306 constantinople_block: Some(0),
2307 petersburg_block: Some(0),
2308 istanbul_block: Some(0),
2309 muir_glacier_block: Some(0),
2310 berlin_block: Some(0),
2311 london_block: Some(0),
2312 arrow_glacier_block: Some(0),
2313 gray_glacier_block: Some(0),
2314 merge_netsplit_block: Some(0),
2315 shanghai_time: Some(0),
2316 cancun_time: Some(0),
2317 terminal_total_difficulty: Some(U256::ZERO),
2318 ..Default::default()
2319 },
2320 ..Default::default()
2321 };
2322
2323 let chain_spec: ChainSpec = genesis.into();
2324
2325 let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect();
2326 let expected_hardforks = vec![
2327 EthereumHardfork::Homestead.boxed(),
2328 EthereumHardfork::Dao.boxed(),
2329 EthereumHardfork::Tangerine.boxed(),
2330 EthereumHardfork::SpuriousDragon.boxed(),
2331 EthereumHardfork::Byzantium.boxed(),
2332 EthereumHardfork::Constantinople.boxed(),
2333 EthereumHardfork::Petersburg.boxed(),
2334 EthereumHardfork::Istanbul.boxed(),
2335 EthereumHardfork::MuirGlacier.boxed(),
2336 EthereumHardfork::Berlin.boxed(),
2337 EthereumHardfork::London.boxed(),
2338 EthereumHardfork::ArrowGlacier.boxed(),
2339 EthereumHardfork::GrayGlacier.boxed(),
2340 EthereumHardfork::Paris.boxed(),
2341 EthereumHardfork::Shanghai.boxed(),
2342 EthereumHardfork::Cancun.boxed(),
2343 ];
2344
2345 assert!(expected_hardforks
2346 .iter()
2347 .zip(hardforks.iter())
2348 .all(|(expected, actual)| &**expected == *actual));
2349 assert_eq!(expected_hardforks.len(), hardforks.len());
2350 }
2351}