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 EthChainSpec,
7};
8use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc, vec::Vec};
9use alloy_chains::{Chain, NamedChain};
10use alloy_consensus::{
11 constants::{
12 DEV_GENESIS_HASH, EMPTY_WITHDRAWALS, HOLESKY_GENESIS_HASH, HOODI_GENESIS_HASH,
13 MAINNET_GENESIS_HASH, SEPOLIA_GENESIS_HASH,
14 },
15 Header,
16};
17use alloy_eips::{
18 eip1559::INITIAL_BASE_FEE, eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS,
19 eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams,
20};
21use alloy_genesis::Genesis;
22use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256};
23use alloy_trie::root::state_root_ref_unhashed;
24use core::fmt::Debug;
25use derive_more::From;
26use reth_ethereum_forks::{
27 ChainHardforks, DisplayHardforks, EthereumHardfork, EthereumHardforks, ForkCondition,
28 ForkFilter, ForkFilterKey, ForkHash, ForkId, Hardfork, Hardforks, Head, DEV_HARDFORKS,
29};
30use reth_network_peers::{
31 holesky_nodes, hoodi_nodes, mainnet_nodes, op_nodes, op_testnet_nodes, sepolia_nodes,
32 NodeRecord,
33};
34use reth_primitives_traits::{sync::LazyLock, SealedHeader};
35
36pub fn make_genesis_header(genesis: &Genesis, hardforks: &ChainHardforks) -> Header {
38 let base_fee_per_gas = hardforks
40 .fork(EthereumHardfork::London)
41 .active_at_block(0)
42 .then(|| genesis.base_fee_per_gas.map(|fee| fee as u64).unwrap_or(INITIAL_BASE_FEE));
43
44 let withdrawals_root = hardforks
47 .fork(EthereumHardfork::Shanghai)
48 .active_at_timestamp(genesis.timestamp)
49 .then_some(EMPTY_WITHDRAWALS);
50
51 let (parent_beacon_block_root, blob_gas_used, excess_blob_gas) =
56 if hardforks.fork(EthereumHardfork::Cancun).active_at_timestamp(genesis.timestamp) {
57 let blob_gas_used = genesis.blob_gas_used.unwrap_or(0);
58 let excess_blob_gas = genesis.excess_blob_gas.unwrap_or(0);
59 (Some(B256::ZERO), Some(blob_gas_used), Some(excess_blob_gas))
60 } else {
61 (None, None, None)
62 };
63
64 let requests_hash = hardforks
66 .fork(EthereumHardfork::Prague)
67 .active_at_timestamp(genesis.timestamp)
68 .then_some(EMPTY_REQUESTS_HASH);
69
70 Header {
71 gas_limit: genesis.gas_limit,
72 difficulty: genesis.difficulty,
73 nonce: genesis.nonce.into(),
74 extra_data: genesis.extra_data.clone(),
75 state_root: state_root_ref_unhashed(&genesis.alloc),
76 timestamp: genesis.timestamp,
77 mix_hash: genesis.mix_hash,
78 beneficiary: genesis.coinbase,
79 base_fee_per_gas,
80 withdrawals_root,
81 parent_beacon_block_root,
82 blob_gas_used,
83 excess_blob_gas,
84 requests_hash,
85 ..Default::default()
86 }
87}
88
89pub static MAINNET: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
91 let genesis = serde_json::from_str(include_str!("../res/genesis/mainnet.json"))
92 .expect("Can't deserialize Mainnet genesis json");
93 let hardforks = EthereumHardfork::mainnet().into();
94 let mut spec = ChainSpec {
95 chain: Chain::mainnet(),
96 genesis_header: SealedHeader::new(
97 make_genesis_header(&genesis, &hardforks),
98 MAINNET_GENESIS_HASH,
99 ),
100 genesis,
101 paris_block_and_final_difficulty: Some((
103 15537394,
104 U256::from(58_750_003_716_598_352_816_469u128),
105 )),
106 hardforks,
107 deposit_contract: Some(DepositContract::new(
109 MAINNET_DEPOSIT_CONTRACT_ADDRESS,
110 11052984,
111 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
112 )),
113 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
114 prune_delete_limit: MAINNET_PRUNE_DELETE_LIMIT,
115 blob_params: HardforkBlobParams::default(),
116 };
117 spec.genesis.config.dao_fork_support = true;
118 spec.into()
119});
120
121pub static SEPOLIA: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
123 let genesis = serde_json::from_str(include_str!("../res/genesis/sepolia.json"))
124 .expect("Can't deserialize Sepolia genesis json");
125 let hardforks = EthereumHardfork::sepolia().into();
126 let mut spec = ChainSpec {
127 chain: Chain::sepolia(),
128 genesis_header: SealedHeader::new(
129 make_genesis_header(&genesis, &hardforks),
130 SEPOLIA_GENESIS_HASH,
131 ),
132 genesis,
133 paris_block_and_final_difficulty: Some((1450409, U256::from(17_000_018_015_853_232u128))),
135 hardforks,
136 deposit_contract: Some(DepositContract::new(
138 address!("0x7f02c3e3c98b133055b8b348b2ac625669ed295d"),
139 1273020,
140 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
141 )),
142 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
143 prune_delete_limit: 10000,
144 blob_params: HardforkBlobParams::default(),
145 };
146 spec.genesis.config.dao_fork_support = true;
147 spec.into()
148});
149
150pub static HOLESKY: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
152 let genesis = serde_json::from_str(include_str!("../res/genesis/holesky.json"))
153 .expect("Can't deserialize Holesky genesis json");
154 let hardforks = EthereumHardfork::holesky().into();
155 let mut spec = ChainSpec {
156 chain: Chain::holesky(),
157 genesis_header: SealedHeader::new(
158 make_genesis_header(&genesis, &hardforks),
159 HOLESKY_GENESIS_HASH,
160 ),
161 genesis,
162 paris_block_and_final_difficulty: Some((0, U256::from(1))),
163 hardforks,
164 deposit_contract: Some(DepositContract::new(
165 address!("0x4242424242424242424242424242424242424242"),
166 0,
167 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
168 )),
169 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
170 prune_delete_limit: 10000,
171 blob_params: HardforkBlobParams::default(),
172 };
173 spec.genesis.config.dao_fork_support = true;
174 spec.into()
175});
176
177pub static HOODI: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
181 let genesis = serde_json::from_str(include_str!("../res/genesis/hoodi.json"))
182 .expect("Can't deserialize Hoodi genesis json");
183 let hardforks = EthereumHardfork::hoodi().into();
184 let mut spec = ChainSpec {
185 chain: Chain::hoodi(),
186 genesis_header: SealedHeader::new(
187 make_genesis_header(&genesis, &hardforks),
188 HOODI_GENESIS_HASH,
189 ),
190 genesis,
191 paris_block_and_final_difficulty: Some((0, U256::from(0))),
192 hardforks,
193 deposit_contract: Some(DepositContract::new(
194 address!("0x00000000219ab540356cBB839Cbe05303d7705Fa"),
195 0,
196 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
197 )),
198 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
199 prune_delete_limit: 10000,
200 blob_params: HardforkBlobParams::default(),
201 };
202 spec.genesis.config.dao_fork_support = true;
203 spec.into()
204});
205
206pub static DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
211 let genesis = serde_json::from_str(include_str!("../res/genesis/dev.json"))
212 .expect("Can't deserialize Dev testnet genesis json");
213 let hardforks = DEV_HARDFORKS.clone();
214 ChainSpec {
215 chain: Chain::dev(),
216 genesis_header: SealedHeader::new(
217 make_genesis_header(&genesis, &hardforks),
218 DEV_GENESIS_HASH,
219 ),
220 genesis,
221 paris_block_and_final_difficulty: Some((0, U256::from(0))),
222 hardforks: DEV_HARDFORKS.clone(),
223 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
224 deposit_contract: None, ..Default::default()
226 }
227 .into()
228});
229
230#[derive(Clone, Debug, PartialEq, Eq)]
233pub enum BaseFeeParamsKind {
234 Constant(BaseFeeParams),
236 Variable(ForkBaseFeeParams),
239}
240
241impl Default for BaseFeeParamsKind {
242 fn default() -> Self {
243 BaseFeeParams::ethereum().into()
244 }
245}
246
247impl From<BaseFeeParams> for BaseFeeParamsKind {
248 fn from(params: BaseFeeParams) -> Self {
249 Self::Constant(params)
250 }
251}
252
253impl From<ForkBaseFeeParams> for BaseFeeParamsKind {
254 fn from(params: ForkBaseFeeParams) -> Self {
255 Self::Variable(params)
256 }
257}
258
259#[derive(Clone, Debug, PartialEq, Eq, From)]
262pub struct ForkBaseFeeParams(Vec<(Box<dyn Hardfork>, BaseFeeParams)>);
263
264#[derive(Debug, Clone, PartialEq, Eq)]
266pub struct HardforkBlobParams {
267 pub cancun: BlobParams,
269 pub prague: BlobParams,
271}
272
273impl HardforkBlobParams {
274 pub fn from_schedule(blob_schedule: &BTreeMap<String, BlobParams>) -> Self {
277 Self {
278 cancun: blob_schedule.get("cancun").copied().unwrap_or_else(BlobParams::cancun),
279 prague: blob_schedule.get("prague").copied().unwrap_or_else(BlobParams::prague),
280 }
281 }
282}
283
284impl Default for HardforkBlobParams {
285 fn default() -> Self {
286 Self { cancun: BlobParams::cancun(), prague: BlobParams::prague() }
287 }
288}
289
290impl core::ops::Deref for ChainSpec {
291 type Target = ChainHardforks;
292
293 fn deref(&self) -> &Self::Target {
294 &self.hardforks
295 }
296}
297
298#[derive(Debug, Clone, PartialEq, Eq)]
306pub struct ChainSpec {
307 pub chain: Chain,
309
310 pub genesis: Genesis,
312
313 pub genesis_header: SealedHeader,
315
316 pub paris_block_and_final_difficulty: Option<(u64, U256)>,
319
320 pub hardforks: ChainHardforks,
322
323 pub deposit_contract: Option<DepositContract>,
325
326 pub base_fee_params: BaseFeeParamsKind,
328
329 pub prune_delete_limit: usize,
331
332 pub blob_params: HardforkBlobParams,
334}
335
336impl Default for ChainSpec {
337 fn default() -> Self {
338 Self {
339 chain: Default::default(),
340 genesis: Default::default(),
341 genesis_header: Default::default(),
342 paris_block_and_final_difficulty: Default::default(),
343 hardforks: Default::default(),
344 deposit_contract: Default::default(),
345 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
346 prune_delete_limit: MAINNET_PRUNE_DELETE_LIMIT,
347 blob_params: Default::default(),
348 }
349 }
350}
351
352impl ChainSpec {
353 pub fn from_genesis(genesis: Genesis) -> Self {
355 genesis.into()
356 }
357
358 pub const fn chain(&self) -> Chain {
360 self.chain
361 }
362
363 #[inline]
365 pub const fn is_ethereum(&self) -> bool {
366 self.chain.is_ethereum()
367 }
368
369 #[inline]
371 pub fn is_optimism_mainnet(&self) -> bool {
372 self.chain == Chain::optimism_mainnet()
373 }
374
375 #[inline]
377 pub fn paris_block(&self) -> Option<u64> {
378 self.paris_block_and_final_difficulty.map(|(block, _)| block)
379 }
380
381 pub const fn genesis(&self) -> &Genesis {
385 &self.genesis
386 }
387
388 pub fn genesis_header(&self) -> &Header {
390 &self.genesis_header
391 }
392
393 pub fn sealed_genesis_header(&self) -> SealedHeader {
395 SealedHeader::new(self.genesis_header().clone(), self.genesis_hash())
396 }
397
398 pub fn initial_base_fee(&self) -> Option<u64> {
400 let genesis_base_fee =
402 self.genesis.base_fee_per_gas.map(|fee| fee as u64).unwrap_or(INITIAL_BASE_FEE);
403
404 self.hardforks.fork(EthereumHardfork::London).active_at_block(0).then_some(genesis_base_fee)
406 }
407
408 pub fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams {
410 match self.base_fee_params {
411 BaseFeeParamsKind::Constant(bf_params) => bf_params,
412 BaseFeeParamsKind::Variable(ForkBaseFeeParams(ref bf_params)) => {
413 for (fork, params) in bf_params.iter().rev() {
417 if self.hardforks.is_fork_active_at_timestamp(fork.clone(), timestamp) {
418 return *params
419 }
420 }
421
422 bf_params.first().map(|(_, params)| *params).unwrap_or(BaseFeeParams::ethereum())
423 }
424 }
425 }
426
427 pub fn base_fee_params_at_block(&self, block_number: u64) -> BaseFeeParams {
429 match self.base_fee_params {
430 BaseFeeParamsKind::Constant(bf_params) => bf_params,
431 BaseFeeParamsKind::Variable(ForkBaseFeeParams(ref bf_params)) => {
432 for (fork, params) in bf_params.iter().rev() {
436 if self.hardforks.is_fork_active_at_block(fork.clone(), block_number) {
437 return *params
438 }
439 }
440
441 bf_params.first().map(|(_, params)| *params).unwrap_or(BaseFeeParams::ethereum())
442 }
443 }
444 }
445
446 pub fn genesis_hash(&self) -> B256 {
448 self.genesis_header.hash()
449 }
450
451 pub const fn genesis_timestamp(&self) -> u64 {
453 self.genesis.timestamp
454 }
455
456 pub fn get_final_paris_total_difficulty(&self) -> Option<U256> {
458 self.paris_block_and_final_difficulty.map(|(_, final_difficulty)| final_difficulty)
459 }
460
461 pub fn hardfork_fork_filter<H: Hardfork + Clone>(&self, fork: H) -> Option<ForkFilter> {
463 match self.hardforks.fork(fork.clone()) {
464 ForkCondition::Never => None,
465 _ => Some(self.fork_filter(self.satisfy(self.hardforks.fork(fork)))),
466 }
467 }
468
469 pub fn display_hardforks(&self) -> DisplayHardforks {
471 DisplayHardforks::new(self.hardforks.forks_iter())
472 }
473
474 #[inline]
476 pub fn hardfork_fork_id<H: Hardfork + Clone>(&self, fork: H) -> Option<ForkId> {
477 let condition = self.hardforks.fork(fork);
478 match condition {
479 ForkCondition::Never => None,
480 _ => Some(self.fork_id(&self.satisfy(condition))),
481 }
482 }
483
484 #[inline]
487 pub fn shanghai_fork_id(&self) -> Option<ForkId> {
488 self.hardfork_fork_id(EthereumHardfork::Shanghai)
489 }
490
491 #[inline]
494 pub fn cancun_fork_id(&self) -> Option<ForkId> {
495 self.hardfork_fork_id(EthereumHardfork::Cancun)
496 }
497
498 #[inline]
501 pub fn latest_fork_id(&self) -> ForkId {
502 self.hardfork_fork_id(self.hardforks.last().unwrap().0).unwrap()
503 }
504
505 pub fn fork_filter(&self, head: Head) -> ForkFilter {
507 let forks = self.hardforks.forks_iter().filter_map(|(_, condition)| {
508 Some(match condition {
511 ForkCondition::Block(block) |
512 ForkCondition::TTD { fork_block: Some(block), .. } => ForkFilterKey::Block(block),
513 ForkCondition::Timestamp(time) => ForkFilterKey::Time(time),
514 _ => return None,
515 })
516 });
517
518 ForkFilter::new(head, self.genesis_hash(), self.genesis_timestamp(), forks)
519 }
520
521 pub fn fork_id(&self, head: &Head) -> ForkId {
526 let mut forkhash = ForkHash::from(self.genesis_hash());
527
528 let mut current_applied = 0;
534
535 for (_, cond) in self.hardforks.forks_iter() {
537 if let ForkCondition::Block(block) |
540 ForkCondition::TTD { fork_block: Some(block), .. } = cond
541 {
542 if head.number >= block {
543 if block != current_applied {
545 forkhash += block;
546 current_applied = block;
547 }
548 } else {
549 return ForkId { hash: forkhash, next: block }
552 }
553 }
554 }
555
556 for timestamp in self.hardforks.forks_iter().filter_map(|(_, cond)| {
560 cond.as_timestamp().filter(|time| time > &self.genesis.timestamp)
562 }) {
563 if head.timestamp >= timestamp {
564 if timestamp != current_applied {
566 forkhash += timestamp;
567 current_applied = timestamp;
568 }
569 } else {
570 return ForkId { hash: forkhash, next: timestamp }
574 }
575 }
576
577 ForkId { hash: forkhash, next: 0 }
578 }
579
580 pub(crate) fn satisfy(&self, cond: ForkCondition) -> Head {
582 match cond {
583 ForkCondition::Block(number) => Head { number, ..Default::default() },
584 ForkCondition::Timestamp(timestamp) => {
585 Head {
588 timestamp,
589 number: self.last_block_fork_before_merge_or_timestamp().unwrap_or_default(),
590 ..Default::default()
591 }
592 }
593 ForkCondition::TTD { total_difficulty, fork_block, .. } => Head {
594 total_difficulty,
595 number: fork_block.unwrap_or_default(),
596 ..Default::default()
597 },
598 ForkCondition::Never => unreachable!(),
599 }
600 }
601
602 pub(crate) fn last_block_fork_before_merge_or_timestamp(&self) -> Option<u64> {
614 let mut hardforks_iter = self.hardforks.forks_iter().peekable();
615 while let Some((_, curr_cond)) = hardforks_iter.next() {
616 if let Some((_, next_cond)) = hardforks_iter.peek() {
617 match next_cond {
621 ForkCondition::TTD { fork_block: Some(block), .. } => return Some(*block),
624
625 ForkCondition::TTD { .. } | ForkCondition::Timestamp(_) => {
628 if let ForkCondition::Block(block_num) = curr_cond {
631 return Some(block_num);
632 }
633 }
634 ForkCondition::Block(_) | ForkCondition::Never => {}
635 }
636 }
637 }
638 None
639 }
640
641 pub fn builder() -> ChainSpecBuilder {
643 ChainSpecBuilder::default()
644 }
645
646 pub fn bootnodes(&self) -> Option<Vec<NodeRecord>> {
648 use NamedChain as C;
649
650 match self.chain.try_into().ok()? {
651 C::Mainnet => Some(mainnet_nodes()),
652 C::Sepolia => Some(sepolia_nodes()),
653 C::Holesky => Some(holesky_nodes()),
654 C::Hoodi => Some(hoodi_nodes()),
655 C::Base | C::Optimism | C::Unichain | C::World => Some(op_nodes()),
657 C::OptimismSepolia | C::BaseSepolia | C::UnichainSepolia | C::WorldSepolia => {
658 Some(op_testnet_nodes())
659 }
660
661 chain if chain.is_optimism() && chain.is_testnet() => Some(op_testnet_nodes()),
663 chain if chain.is_optimism() => Some(op_nodes()),
664 _ => None,
665 }
666 }
667}
668
669impl From<Genesis> for ChainSpec {
670 fn from(genesis: Genesis) -> Self {
671 let hardfork_opts = [
673 (EthereumHardfork::Frontier.boxed(), Some(0)),
674 (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block),
675 (EthereumHardfork::Dao.boxed(), genesis.config.dao_fork_block),
676 (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block),
677 (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block),
678 (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block),
679 (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block),
680 (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block),
681 (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block),
682 (EthereumHardfork::MuirGlacier.boxed(), genesis.config.muir_glacier_block),
683 (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block),
684 (EthereumHardfork::London.boxed(), genesis.config.london_block),
685 (EthereumHardfork::ArrowGlacier.boxed(), genesis.config.arrow_glacier_block),
686 (EthereumHardfork::GrayGlacier.boxed(), genesis.config.gray_glacier_block),
687 ];
688 let mut hardforks = hardfork_opts
689 .into_iter()
690 .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block))))
691 .collect::<Vec<_>>();
692
693 let paris_block_and_final_difficulty =
697 if let Some(ttd) = genesis.config.terminal_total_difficulty {
698 hardforks.push((
699 EthereumHardfork::Paris.boxed(),
700 ForkCondition::TTD {
701 activation_block_number: genesis
704 .config
705 .merge_netsplit_block
706 .unwrap_or_default(),
707 total_difficulty: ttd,
708 fork_block: genesis.config.merge_netsplit_block,
709 },
710 ));
711
712 genesis.config.merge_netsplit_block.map(|block| (block, ttd))
713 } else {
714 None
715 };
716
717 let time_hardfork_opts = [
719 (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time),
720 (EthereumHardfork::Cancun.boxed(), genesis.config.cancun_time),
721 (EthereumHardfork::Prague.boxed(), genesis.config.prague_time),
722 (EthereumHardfork::Osaka.boxed(), genesis.config.osaka_time),
723 ];
724
725 let mut time_hardforks = time_hardfork_opts
726 .into_iter()
727 .filter_map(|(hardfork, opt)| {
728 opt.map(|time| (hardfork, ForkCondition::Timestamp(time)))
729 })
730 .collect::<Vec<_>>();
731
732 hardforks.append(&mut time_hardforks);
733
734 let mainnet_hardforks: ChainHardforks = EthereumHardfork::mainnet().into();
736 let mainnet_order = mainnet_hardforks.forks_iter();
737
738 let mut ordered_hardforks = Vec::with_capacity(hardforks.len());
739 for (hardfork, _) in mainnet_order {
740 if let Some(pos) = hardforks.iter().position(|(e, _)| **e == *hardfork) {
741 ordered_hardforks.push(hardforks.remove(pos));
742 }
743 }
744
745 ordered_hardforks.append(&mut hardforks);
747
748 let blob_params = HardforkBlobParams::from_schedule(&genesis.config.blob_schedule);
750
751 let deposit_contract = genesis.config.deposit_contract_address.map(|address| {
756 DepositContract { address, block: 0, topic: MAINNET_DEPOSIT_CONTRACT.topic }
757 });
758
759 let hardforks = ChainHardforks::new(ordered_hardforks);
760
761 Self {
762 chain: genesis.config.chain_id.into(),
763 genesis_header: SealedHeader::new_unhashed(make_genesis_header(&genesis, &hardforks)),
764 genesis,
765 hardforks,
766 paris_block_and_final_difficulty,
767 deposit_contract,
768 blob_params,
769 ..Default::default()
770 }
771 }
772}
773
774impl Hardforks for ChainSpec {
775 fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition {
776 self.hardforks.fork(fork)
777 }
778
779 fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
780 self.hardforks.forks_iter()
781 }
782
783 fn fork_id(&self, head: &Head) -> ForkId {
784 self.fork_id(head)
785 }
786
787 fn latest_fork_id(&self) -> ForkId {
788 self.latest_fork_id()
789 }
790
791 fn fork_filter(&self, head: Head) -> ForkFilter {
792 self.fork_filter(head)
793 }
794}
795
796impl EthereumHardforks for ChainSpec {
797 fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition {
798 self.fork(fork)
799 }
800}
801
802#[auto_impl::auto_impl(&, Arc)]
804pub trait ChainSpecProvider: Debug + Send + Sync {
805 type ChainSpec: EthChainSpec + 'static;
807
808 fn chain_spec(&self) -> Arc<Self::ChainSpec>;
810}
811
812#[derive(Debug, Default, Clone)]
814pub struct ChainSpecBuilder {
815 chain: Option<Chain>,
816 genesis: Option<Genesis>,
817 hardforks: ChainHardforks,
818}
819
820impl ChainSpecBuilder {
821 pub fn mainnet() -> Self {
823 Self {
824 chain: Some(MAINNET.chain),
825 genesis: Some(MAINNET.genesis.clone()),
826 hardforks: MAINNET.hardforks.clone(),
827 }
828 }
829}
830
831impl ChainSpecBuilder {
832 pub const fn chain(mut self, chain: Chain) -> Self {
834 self.chain = Some(chain);
835 self
836 }
837
838 pub fn genesis(mut self, genesis: Genesis) -> Self {
840 self.genesis = Some(genesis);
841 self
842 }
843
844 pub fn with_fork<H: Hardfork>(mut self, fork: H, condition: ForkCondition) -> Self {
846 self.hardforks.insert(fork, condition);
847 self
848 }
849
850 pub fn with_forks(mut self, forks: ChainHardforks) -> Self {
852 self.hardforks = forks;
853 self
854 }
855
856 pub fn without_fork<H: Hardfork>(mut self, fork: H) -> Self {
858 self.hardforks.remove(fork);
859 self
860 }
861
862 pub fn paris_at_ttd(self, ttd: U256, activation_block_number: BlockNumber) -> Self {
866 self.with_fork(
867 EthereumHardfork::Paris,
868 ForkCondition::TTD { activation_block_number, total_difficulty: ttd, fork_block: None },
869 )
870 }
871
872 pub fn frontier_activated(mut self) -> Self {
874 self.hardforks.insert(EthereumHardfork::Frontier, ForkCondition::Block(0));
875 self
876 }
877
878 pub fn homestead_activated(mut self) -> Self {
880 self = self.frontier_activated();
881 self.hardforks.insert(EthereumHardfork::Homestead, ForkCondition::Block(0));
882 self
883 }
884
885 pub fn tangerine_whistle_activated(mut self) -> Self {
887 self = self.homestead_activated();
888 self.hardforks.insert(EthereumHardfork::Tangerine, ForkCondition::Block(0));
889 self
890 }
891
892 pub fn spurious_dragon_activated(mut self) -> Self {
894 self = self.tangerine_whistle_activated();
895 self.hardforks.insert(EthereumHardfork::SpuriousDragon, ForkCondition::Block(0));
896 self
897 }
898
899 pub fn byzantium_activated(mut self) -> Self {
901 self = self.spurious_dragon_activated();
902 self.hardforks.insert(EthereumHardfork::Byzantium, ForkCondition::Block(0));
903 self
904 }
905
906 pub fn constantinople_activated(mut self) -> Self {
908 self = self.byzantium_activated();
909 self.hardforks.insert(EthereumHardfork::Constantinople, ForkCondition::Block(0));
910 self
911 }
912
913 pub fn petersburg_activated(mut self) -> Self {
915 self = self.constantinople_activated();
916 self.hardforks.insert(EthereumHardfork::Petersburg, ForkCondition::Block(0));
917 self
918 }
919
920 pub fn istanbul_activated(mut self) -> Self {
922 self = self.petersburg_activated();
923 self.hardforks.insert(EthereumHardfork::Istanbul, ForkCondition::Block(0));
924 self
925 }
926
927 pub fn berlin_activated(mut self) -> Self {
929 self = self.istanbul_activated();
930 self.hardforks.insert(EthereumHardfork::Berlin, ForkCondition::Block(0));
931 self
932 }
933
934 pub fn london_activated(mut self) -> Self {
936 self = self.berlin_activated();
937 self.hardforks.insert(EthereumHardfork::London, ForkCondition::Block(0));
938 self
939 }
940
941 pub fn paris_activated(mut self) -> Self {
943 self = self.london_activated();
944 self.hardforks.insert(
945 EthereumHardfork::Paris,
946 ForkCondition::TTD {
947 activation_block_number: 0,
948 total_difficulty: U256::ZERO,
949 fork_block: None,
950 },
951 );
952 self
953 }
954
955 pub fn shanghai_activated(mut self) -> Self {
957 self = self.paris_activated();
958 self.hardforks.insert(EthereumHardfork::Shanghai, ForkCondition::Timestamp(0));
959 self
960 }
961
962 pub fn cancun_activated(mut self) -> Self {
964 self = self.shanghai_activated();
965 self.hardforks.insert(EthereumHardfork::Cancun, ForkCondition::Timestamp(0));
966 self
967 }
968
969 pub fn prague_activated(mut self) -> Self {
971 self = self.cancun_activated();
972 self.hardforks.insert(EthereumHardfork::Prague, ForkCondition::Timestamp(0));
973 self
974 }
975
976 pub fn osaka_activated(mut self) -> Self {
978 self = self.prague_activated();
979 self.hardforks.insert(EthereumHardfork::Osaka, ForkCondition::Timestamp(0));
980 self
981 }
982
983 pub fn build(self) -> ChainSpec {
990 let paris_block_and_final_difficulty = {
991 self.hardforks.get(EthereumHardfork::Paris).and_then(|cond| {
992 if let ForkCondition::TTD { total_difficulty, activation_block_number, .. } = cond {
993 Some((activation_block_number, total_difficulty))
994 } else {
995 None
996 }
997 })
998 };
999 let genesis = self.genesis.expect("The genesis is required");
1000 ChainSpec {
1001 chain: self.chain.expect("The chain is required"),
1002 genesis_header: SealedHeader::new_unhashed(make_genesis_header(
1003 &genesis,
1004 &self.hardforks,
1005 )),
1006 genesis,
1007 hardforks: self.hardforks,
1008 paris_block_and_final_difficulty,
1009 deposit_contract: None,
1010 ..Default::default()
1011 }
1012 }
1013}
1014
1015impl From<&Arc<ChainSpec>> for ChainSpecBuilder {
1016 fn from(value: &Arc<ChainSpec>) -> Self {
1017 Self {
1018 chain: Some(value.chain),
1019 genesis: Some(value.genesis.clone()),
1020 hardforks: value.hardforks.clone(),
1021 }
1022 }
1023}
1024
1025impl EthExecutorSpec for ChainSpec {
1026 fn deposit_contract_address(&self) -> Option<Address> {
1027 self.deposit_contract.map(|deposit_contract| deposit_contract.address)
1028 }
1029}
1030
1031#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1033pub struct DepositContract {
1034 pub address: Address,
1036 pub block: BlockNumber,
1038 pub topic: B256,
1040}
1041
1042impl DepositContract {
1043 pub const fn new(address: Address, block: BlockNumber, topic: B256) -> Self {
1045 Self { address, block, topic }
1046 }
1047}
1048
1049#[cfg(any(test, feature = "test-utils"))]
1051pub fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) {
1052 for (block, expected_id) in cases {
1053 let computed_id = spec.fork_id(block);
1054 assert_eq!(
1055 expected_id, &computed_id,
1056 "Expected fork ID {:?}, computed fork ID {:?} at block {}",
1057 expected_id, computed_id, block.number
1058 );
1059 }
1060}
1061
1062#[cfg(test)]
1063mod tests {
1064 use super::*;
1065 use alloy_chains::Chain;
1066 use alloy_consensus::constants::ETH_TO_WEI;
1067 use alloy_eips::eip4844::BLOB_TX_MIN_BLOB_GASPRICE;
1068 use alloy_evm::block::calc::{base_block_reward, block_reward};
1069 use alloy_genesis::{ChainConfig, GenesisAccount};
1070 use alloy_primitives::{b256, hex};
1071 use alloy_trie::{TrieAccount, EMPTY_ROOT_HASH};
1072 use core::ops::Deref;
1073 use reth_ethereum_forks::{ForkCondition, ForkHash, ForkId, Head};
1074 use std::{collections::HashMap, str::FromStr};
1075
1076 fn test_hardfork_fork_ids(spec: &ChainSpec, cases: &[(EthereumHardfork, ForkId)]) {
1077 for (hardfork, expected_id) in cases {
1078 if let Some(computed_id) = spec.hardfork_fork_id(*hardfork) {
1079 assert_eq!(
1080 expected_id, &computed_id,
1081 "Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for hardfork {hardfork}"
1082 );
1083 if matches!(hardfork, EthereumHardfork::Shanghai) {
1084 if let Some(shangai_id) = spec.shanghai_fork_id() {
1085 assert_eq!(
1086 expected_id, &shangai_id,
1087 "Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for Shanghai hardfork"
1088 );
1089 } else {
1090 panic!("Expected ForkCondition to return Some for Hardfork::Shanghai");
1091 }
1092 }
1093 }
1094 }
1095 }
1096
1097 #[test]
1098 fn test_hardfork_list_display_mainnet() {
1099 assert_eq!(
1100 MAINNET.display_hardforks().to_string(),
1101 "Pre-merge hard forks (block based):
1102- Frontier @0
1103- Homestead @1150000
1104- Dao @1920000
1105- Tangerine @2463000
1106- SpuriousDragon @2675000
1107- Byzantium @4370000
1108- Constantinople @7280000
1109- Petersburg @7280000
1110- Istanbul @9069000
1111- MuirGlacier @9200000
1112- Berlin @12244000
1113- London @12965000
1114- ArrowGlacier @13773000
1115- GrayGlacier @15050000
1116Merge hard forks:
1117- Paris @58750000000000000000000 (network is known to be merged)
1118Post-merge hard forks (timestamp based):
1119- Shanghai @1681338455
1120- Cancun @1710338135
1121- Prague @1746612311"
1122 );
1123 }
1124
1125 #[test]
1126 fn test_hardfork_list_ignores_disabled_forks() {
1127 let spec = ChainSpec::builder()
1128 .chain(Chain::mainnet())
1129 .genesis(Genesis::default())
1130 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1131 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Never)
1132 .build();
1133 assert_eq!(
1134 spec.display_hardforks().to_string(),
1135 "Pre-merge hard forks (block based):
1136- Frontier @0"
1137 );
1138 }
1139
1140 #[test]
1142 fn ignores_genesis_fork_blocks() {
1143 let spec = ChainSpec::builder()
1144 .chain(Chain::mainnet())
1145 .genesis(Genesis::default())
1146 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1147 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(0))
1148 .with_fork(EthereumHardfork::Tangerine, ForkCondition::Block(0))
1149 .with_fork(EthereumHardfork::SpuriousDragon, ForkCondition::Block(0))
1150 .with_fork(EthereumHardfork::Byzantium, ForkCondition::Block(0))
1151 .with_fork(EthereumHardfork::Constantinople, ForkCondition::Block(0))
1152 .with_fork(EthereumHardfork::Istanbul, ForkCondition::Block(0))
1153 .with_fork(EthereumHardfork::MuirGlacier, ForkCondition::Block(0))
1154 .with_fork(EthereumHardfork::Berlin, ForkCondition::Block(0))
1155 .with_fork(EthereumHardfork::London, ForkCondition::Block(0))
1156 .with_fork(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0))
1157 .with_fork(EthereumHardfork::GrayGlacier, ForkCondition::Block(0))
1158 .build();
1159
1160 assert_eq!(spec.deref().len(), 12, "12 forks should be active.");
1161 assert_eq!(
1162 spec.fork_id(&Head { number: 1, ..Default::default() }),
1163 ForkId { hash: ForkHash::from(spec.genesis_hash()), next: 0 },
1164 "the fork ID should be the genesis hash; forks at genesis are ignored for fork filters"
1165 );
1166 }
1167
1168 #[test]
1169 fn ignores_duplicate_fork_blocks() {
1170 let empty_genesis = Genesis::default();
1171 let unique_spec = ChainSpec::builder()
1172 .chain(Chain::mainnet())
1173 .genesis(empty_genesis.clone())
1174 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1175 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(1))
1176 .build();
1177
1178 let duplicate_spec = ChainSpec::builder()
1179 .chain(Chain::mainnet())
1180 .genesis(empty_genesis)
1181 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1182 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(1))
1183 .with_fork(EthereumHardfork::Tangerine, ForkCondition::Block(1))
1184 .build();
1185
1186 assert_eq!(
1187 unique_spec.fork_id(&Head { number: 2, ..Default::default() }),
1188 duplicate_spec.fork_id(&Head { number: 2, ..Default::default() }),
1189 "duplicate fork blocks should be deduplicated for fork filters"
1190 );
1191 }
1192
1193 #[test]
1194 fn test_chainspec_satisfy() {
1195 let empty_genesis = Genesis::default();
1196 let happy_path_case = ChainSpec::builder()
1198 .chain(Chain::mainnet())
1199 .genesis(empty_genesis.clone())
1200 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1201 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1202 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1203 .build();
1204 let happy_path_head = happy_path_case.satisfy(ForkCondition::Timestamp(11313123));
1205 let happy_path_expected = Head { number: 73, timestamp: 11313123, ..Default::default() };
1206 assert_eq!(
1207 happy_path_head, happy_path_expected,
1208 "expected satisfy() to return {happy_path_expected:#?}, but got {happy_path_head:#?} "
1209 );
1210 let multiple_timestamp_fork_case = ChainSpec::builder()
1212 .chain(Chain::mainnet())
1213 .genesis(empty_genesis.clone())
1214 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1215 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1216 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1217 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(11313398))
1218 .build();
1219 let multi_timestamp_head =
1220 multiple_timestamp_fork_case.satisfy(ForkCondition::Timestamp(11313398));
1221 let mult_timestamp_expected =
1222 Head { number: 73, timestamp: 11313398, ..Default::default() };
1223 assert_eq!(
1224 multi_timestamp_head, mult_timestamp_expected,
1225 "expected satisfy() to return {mult_timestamp_expected:#?}, but got {multi_timestamp_head:#?} "
1226 );
1227 let no_block_fork_case = ChainSpec::builder()
1229 .chain(Chain::mainnet())
1230 .genesis(empty_genesis.clone())
1231 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1232 .build();
1233 let no_block_fork_head = no_block_fork_case.satisfy(ForkCondition::Timestamp(11313123));
1234 let no_block_fork_expected = Head { number: 0, timestamp: 11313123, ..Default::default() };
1235 assert_eq!(
1236 no_block_fork_head, no_block_fork_expected,
1237 "expected satisfy() to return {no_block_fork_expected:#?}, but got {no_block_fork_head:#?} ",
1238 );
1239 let fork_cond_ttd_blocknum_case = ChainSpec::builder()
1241 .chain(Chain::mainnet())
1242 .genesis(empty_genesis.clone())
1243 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1244 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1245 .with_fork(
1246 EthereumHardfork::Paris,
1247 ForkCondition::TTD {
1248 activation_block_number: 101,
1249 fork_block: Some(101),
1250 total_difficulty: U256::from(10_790_000),
1251 },
1252 )
1253 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1254 .build();
1255 let fork_cond_ttd_blocknum_head =
1256 fork_cond_ttd_blocknum_case.satisfy(ForkCondition::Timestamp(11313123));
1257 let fork_cond_ttd_blocknum_expected =
1258 Head { number: 101, timestamp: 11313123, ..Default::default() };
1259 assert_eq!(
1260 fork_cond_ttd_blocknum_head, fork_cond_ttd_blocknum_expected,
1261 "expected satisfy() to return {fork_cond_ttd_blocknum_expected:#?}, but got {fork_cond_ttd_blocknum_expected:#?} ",
1262 );
1263
1264 let fork_cond_block_only_case = ChainSpec::builder()
1268 .chain(Chain::mainnet())
1269 .genesis(empty_genesis)
1270 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1271 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1272 .build();
1273 let fork_cond_block_only_head = fork_cond_block_only_case.satisfy(ForkCondition::Block(73));
1274 let fork_cond_block_only_expected = Head { number: 73, ..Default::default() };
1275 assert_eq!(
1276 fork_cond_block_only_head, fork_cond_block_only_expected,
1277 "expected satisfy() to return {fork_cond_block_only_expected:#?}, but got {fork_cond_block_only_head:#?} ",
1278 );
1279 let fork_cond_ttd_no_new_spec = fork_cond_block_only_case.satisfy(ForkCondition::TTD {
1282 activation_block_number: 101,
1283 fork_block: None,
1284 total_difficulty: U256::from(10_790_000),
1285 });
1286 let fork_cond_ttd_no_new_spec_expected =
1287 Head { total_difficulty: U256::from(10_790_000), ..Default::default() };
1288 assert_eq!(
1289 fork_cond_ttd_no_new_spec, fork_cond_ttd_no_new_spec_expected,
1290 "expected satisfy() to return {fork_cond_ttd_blocknum_expected:#?}, but got {fork_cond_ttd_blocknum_expected:#?} ",
1291 );
1292 }
1293
1294 #[test]
1295 fn mainnet_hardfork_fork_ids() {
1296 test_hardfork_fork_ids(
1297 &MAINNET,
1298 &[
1299 (
1300 EthereumHardfork::Frontier,
1301 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1302 ),
1303 (
1304 EthereumHardfork::Homestead,
1305 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1306 ),
1307 (
1308 EthereumHardfork::Dao,
1309 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1310 ),
1311 (
1312 EthereumHardfork::Tangerine,
1313 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1314 ),
1315 (
1316 EthereumHardfork::SpuriousDragon,
1317 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1318 ),
1319 (
1320 EthereumHardfork::Byzantium,
1321 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1322 ),
1323 (
1324 EthereumHardfork::Constantinople,
1325 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1326 ),
1327 (
1328 EthereumHardfork::Petersburg,
1329 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1330 ),
1331 (
1332 EthereumHardfork::Istanbul,
1333 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1334 ),
1335 (
1336 EthereumHardfork::MuirGlacier,
1337 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1338 ),
1339 (
1340 EthereumHardfork::Berlin,
1341 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1342 ),
1343 (
1344 EthereumHardfork::London,
1345 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1346 ),
1347 (
1348 EthereumHardfork::ArrowGlacier,
1349 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1350 ),
1351 (
1352 EthereumHardfork::GrayGlacier,
1353 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1354 ),
1355 (
1356 EthereumHardfork::Shanghai,
1357 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1358 ),
1359 (
1360 EthereumHardfork::Cancun,
1361 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
1362 ),
1363 (
1364 EthereumHardfork::Prague,
1365 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
1366 ),
1367 ],
1368 );
1369 }
1370
1371 #[test]
1372 fn sepolia_hardfork_fork_ids() {
1373 test_hardfork_fork_ids(
1374 &SEPOLIA,
1375 &[
1376 (
1377 EthereumHardfork::Frontier,
1378 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1379 ),
1380 (
1381 EthereumHardfork::Homestead,
1382 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1383 ),
1384 (
1385 EthereumHardfork::Tangerine,
1386 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1387 ),
1388 (
1389 EthereumHardfork::SpuriousDragon,
1390 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1391 ),
1392 (
1393 EthereumHardfork::Byzantium,
1394 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1395 ),
1396 (
1397 EthereumHardfork::Constantinople,
1398 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1399 ),
1400 (
1401 EthereumHardfork::Petersburg,
1402 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1403 ),
1404 (
1405 EthereumHardfork::Istanbul,
1406 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1407 ),
1408 (
1409 EthereumHardfork::Berlin,
1410 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1411 ),
1412 (
1413 EthereumHardfork::London,
1414 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1415 ),
1416 (
1417 EthereumHardfork::Paris,
1418 ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
1419 ),
1420 (
1421 EthereumHardfork::Shanghai,
1422 ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
1423 ),
1424 (
1425 EthereumHardfork::Cancun,
1426 ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
1427 ),
1428 (
1429 EthereumHardfork::Prague,
1430 ForkId { hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]), next: 0 },
1431 ),
1432 ],
1433 );
1434 }
1435
1436 #[test]
1437 fn mainnet_fork_ids() {
1438 test_fork_ids(
1439 &MAINNET,
1440 &[
1441 (
1442 Head { number: 0, ..Default::default() },
1443 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1444 ),
1445 (
1446 Head { number: 1150000, ..Default::default() },
1447 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1448 ),
1449 (
1450 Head { number: 1920000, ..Default::default() },
1451 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1452 ),
1453 (
1454 Head { number: 2463000, ..Default::default() },
1455 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1456 ),
1457 (
1458 Head { number: 2675000, ..Default::default() },
1459 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1460 ),
1461 (
1462 Head { number: 4370000, ..Default::default() },
1463 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1464 ),
1465 (
1466 Head { number: 7280000, ..Default::default() },
1467 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1468 ),
1469 (
1470 Head { number: 9069000, ..Default::default() },
1471 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1472 ),
1473 (
1474 Head { number: 9200000, ..Default::default() },
1475 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1476 ),
1477 (
1478 Head { number: 12244000, ..Default::default() },
1479 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1480 ),
1481 (
1482 Head { number: 12965000, ..Default::default() },
1483 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1484 ),
1485 (
1486 Head { number: 13773000, ..Default::default() },
1487 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1488 ),
1489 (
1490 Head { number: 15050000, ..Default::default() },
1491 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1492 ),
1493 (
1495 Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
1496 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1497 ),
1498 (
1500 Head { number: 20000001, timestamp: 1710338135, ..Default::default() },
1501 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
1502 ),
1503 (
1505 Head { number: 20000002, timestamp: 1746612311, ..Default::default() },
1506 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
1507 ),
1508 (
1510 Head { number: 20000002, timestamp: 2000000000, ..Default::default() },
1511 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
1512 ),
1513 ],
1514 );
1515 }
1516
1517 #[test]
1518 fn hoodi_fork_ids() {
1519 test_fork_ids(
1520 &HOODI,
1521 &[
1522 (
1523 Head { number: 0, ..Default::default() },
1524 ForkId { hash: ForkHash([0xbe, 0xf7, 0x1d, 0x30]), next: 1742999832 },
1525 ),
1526 (
1528 Head { number: 0, timestamp: 1742999833, ..Default::default() },
1529 ForkId { hash: ForkHash([0x09, 0x29, 0xe2, 0x4e]), next: 0 },
1530 ),
1531 ],
1532 )
1533 }
1534
1535 #[test]
1536 fn holesky_fork_ids() {
1537 test_fork_ids(
1538 &HOLESKY,
1539 &[
1540 (
1541 Head { number: 0, ..Default::default() },
1542 ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
1543 ),
1544 (
1546 Head { number: 123, ..Default::default() },
1547 ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
1548 ),
1549 (
1551 Head { number: 123, timestamp: 1696000703, ..Default::default() },
1552 ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
1553 ),
1554 (
1556 Head { number: 123, timestamp: 1696000704, ..Default::default() },
1557 ForkId { hash: ForkHash([0xfd, 0x4f, 0x01, 0x6b]), next: 1707305664 },
1558 ),
1559 (
1561 Head { number: 123, timestamp: 1707305663, ..Default::default() },
1562 ForkId { hash: ForkHash([0xfd, 0x4f, 0x01, 0x6b]), next: 1707305664 },
1563 ),
1564 (
1566 Head { number: 123, timestamp: 1707305664, ..Default::default() },
1567 ForkId { hash: ForkHash([0x9b, 0x19, 0x2a, 0xd0]), next: 1740434112 },
1568 ),
1569 (
1571 Head { number: 123, timestamp: 1740434111, ..Default::default() },
1572 ForkId { hash: ForkHash([0x9b, 0x19, 0x2a, 0xd0]), next: 1740434112 },
1573 ),
1574 (
1576 Head { number: 123, timestamp: 1740434112, ..Default::default() },
1577 ForkId { hash: ForkHash([0xdf, 0xbd, 0x9b, 0xed]), next: 0 },
1578 ),
1579 ],
1580 )
1581 }
1582
1583 #[test]
1584 fn sepolia_fork_ids() {
1585 test_fork_ids(
1586 &SEPOLIA,
1587 &[
1588 (
1589 Head { number: 0, ..Default::default() },
1590 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1591 ),
1592 (
1593 Head { number: 1735370, ..Default::default() },
1594 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1595 ),
1596 (
1597 Head { number: 1735371, ..Default::default() },
1598 ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
1599 ),
1600 (
1601 Head { number: 1735372, timestamp: 1677557087, ..Default::default() },
1602 ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
1603 ),
1604 (
1606 Head { number: 1735373, timestamp: 1677557088, ..Default::default() },
1607 ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
1608 ),
1609 (
1611 Head { number: 1735374, timestamp: 1706655071, ..Default::default() },
1612 ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
1613 ),
1614 (
1616 Head { number: 1735375, timestamp: 1706655072, ..Default::default() },
1617 ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
1618 ),
1619 (
1621 Head { number: 1735376, timestamp: 1741159775, ..Default::default() },
1622 ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
1623 ),
1624 (
1626 Head { number: 1735377, timestamp: 1741159776, ..Default::default() },
1627 ForkId { hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]), next: 0 },
1628 ),
1629 ],
1630 );
1631 }
1632
1633 #[test]
1634 fn dev_fork_ids() {
1635 test_fork_ids(
1636 &DEV,
1637 &[(
1638 Head { number: 0, ..Default::default() },
1639 ForkId { hash: ForkHash([0x45, 0xb8, 0x36, 0x12]), next: 0 },
1640 )],
1641 )
1642 }
1643
1644 #[test]
1648 fn timestamped_forks() {
1649 let mainnet_with_timestamps = ChainSpecBuilder::mainnet().build();
1650 test_fork_ids(
1651 &mainnet_with_timestamps,
1652 &[
1653 (
1654 Head { number: 0, timestamp: 0, ..Default::default() },
1655 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1656 ), (
1658 Head { number: 1149999, timestamp: 0, ..Default::default() },
1659 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1660 ), (
1662 Head { number: 1150000, timestamp: 0, ..Default::default() },
1663 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1664 ), (
1666 Head { number: 1919999, timestamp: 0, ..Default::default() },
1667 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1668 ), (
1670 Head { number: 1920000, timestamp: 0, ..Default::default() },
1671 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1672 ), (
1674 Head { number: 2462999, timestamp: 0, ..Default::default() },
1675 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1676 ), (
1678 Head { number: 2463000, timestamp: 0, ..Default::default() },
1679 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1680 ), (
1682 Head { number: 2674999, timestamp: 0, ..Default::default() },
1683 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1684 ), (
1686 Head { number: 2675000, timestamp: 0, ..Default::default() },
1687 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1688 ), (
1690 Head { number: 4369999, timestamp: 0, ..Default::default() },
1691 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1692 ), (
1694 Head { number: 4370000, timestamp: 0, ..Default::default() },
1695 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1696 ), (
1698 Head { number: 7279999, timestamp: 0, ..Default::default() },
1699 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1700 ), (
1702 Head { number: 7280000, timestamp: 0, ..Default::default() },
1703 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1704 ), (
1706 Head { number: 9068999, timestamp: 0, ..Default::default() },
1707 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1708 ), (
1710 Head { number: 9069000, timestamp: 0, ..Default::default() },
1711 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1712 ), (
1714 Head { number: 9199999, timestamp: 0, ..Default::default() },
1715 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1716 ), (
1718 Head { number: 9200000, timestamp: 0, ..Default::default() },
1719 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1720 ), (
1722 Head { number: 12243999, timestamp: 0, ..Default::default() },
1723 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1724 ), (
1726 Head { number: 12244000, timestamp: 0, ..Default::default() },
1727 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1728 ), (
1730 Head { number: 12964999, timestamp: 0, ..Default::default() },
1731 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1732 ), (
1734 Head { number: 12965000, timestamp: 0, ..Default::default() },
1735 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1736 ), (
1738 Head { number: 13772999, timestamp: 0, ..Default::default() },
1739 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1740 ), (
1742 Head { number: 13773000, timestamp: 0, ..Default::default() },
1743 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1744 ), (
1746 Head { number: 15049999, timestamp: 0, ..Default::default() },
1747 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1748 ), (
1750 Head { number: 15050000, timestamp: 0, ..Default::default() },
1751 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1752 ), (
1754 Head { number: 19999999, timestamp: 1667999999, ..Default::default() },
1755 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1756 ), (
1758 Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
1759 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1760 ), (
1762 Head { number: 20000001, timestamp: 1710338134, ..Default::default() },
1763 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1764 ), (
1766 Head { number: 20000002, timestamp: 1710338135, ..Default::default() },
1767 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
1768 ), (
1770 Head { number: 20000003, timestamp: 1746612310, ..Default::default() },
1771 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
1772 ), (
1774 Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
1775 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
1776 ), (
1778 Head { number: 20000004, timestamp: 2000000000, ..Default::default() },
1779 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
1780 ),
1781 ],
1782 );
1783 }
1784
1785 fn construct_chainspec(
1788 builder: ChainSpecBuilder,
1789 shanghai_time: u64,
1790 cancun_time: u64,
1791 ) -> ChainSpec {
1792 builder
1793 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(shanghai_time))
1794 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(cancun_time))
1795 .build()
1796 }
1797
1798 #[test]
1803 fn test_timestamp_fork_in_genesis() {
1804 let timestamp = 1690475657u64;
1805 let default_spec_builder = ChainSpecBuilder::default()
1806 .chain(Chain::from_id(1337))
1807 .genesis(Genesis::default().with_timestamp(timestamp))
1808 .paris_activated();
1809
1810 let tests = [
1813 (
1814 construct_chainspec(default_spec_builder.clone(), timestamp - 1, timestamp + 1),
1815 timestamp + 1,
1816 ),
1817 (
1818 construct_chainspec(default_spec_builder.clone(), timestamp, timestamp + 1),
1819 timestamp + 1,
1820 ),
1821 (
1822 construct_chainspec(default_spec_builder, timestamp + 1, timestamp + 2),
1823 timestamp + 1,
1824 ),
1825 ];
1826
1827 for (spec, expected_timestamp) in tests {
1828 let got_forkid = spec.fork_id(&Head { number: 0, timestamp: 0, ..Default::default() });
1829 let genesis_hash = spec.genesis_hash();
1834 let expected_forkid =
1835 ForkId { hash: ForkHash::from(genesis_hash), next: expected_timestamp };
1836 assert_eq!(got_forkid, expected_forkid);
1837 }
1838 }
1839
1840 #[test]
1842 fn check_terminal_ttd() {
1843 let chainspec = ChainSpecBuilder::mainnet().build();
1844
1845 let terminal_block_ttd = U256::from(58750003716598352816469_u128);
1847 let terminal_block_difficulty = U256::from(11055787484078698_u128);
1848 assert!(!chainspec
1849 .fork(EthereumHardfork::Paris)
1850 .active_at_ttd(terminal_block_ttd, terminal_block_difficulty));
1851
1852 let first_pos_block_ttd = U256::from(58750003716598352816469_u128);
1854 let first_pos_difficulty = U256::ZERO;
1855 assert!(chainspec
1856 .fork(EthereumHardfork::Paris)
1857 .active_at_ttd(first_pos_block_ttd, first_pos_difficulty));
1858 }
1859
1860 #[test]
1861 fn geth_genesis_with_shanghai() {
1862 let geth_genesis = r#"
1863 {
1864 "config": {
1865 "chainId": 1337,
1866 "homesteadBlock": 0,
1867 "eip150Block": 0,
1868 "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1869 "eip155Block": 0,
1870 "eip158Block": 0,
1871 "byzantiumBlock": 0,
1872 "constantinopleBlock": 0,
1873 "petersburgBlock": 0,
1874 "istanbulBlock": 0,
1875 "muirGlacierBlock": 0,
1876 "berlinBlock": 0,
1877 "londonBlock": 0,
1878 "arrowGlacierBlock": 0,
1879 "grayGlacierBlock": 0,
1880 "shanghaiTime": 0,
1881 "cancunTime": 1,
1882 "terminalTotalDifficulty": 0,
1883 "terminalTotalDifficultyPassed": true,
1884 "ethash": {}
1885 },
1886 "nonce": "0x0",
1887 "timestamp": "0x0",
1888 "extraData": "0x",
1889 "gasLimit": "0x4c4b40",
1890 "difficulty": "0x1",
1891 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1892 "coinbase": "0x0000000000000000000000000000000000000000",
1893 "alloc": {
1894 "658bdf435d810c91414ec09147daa6db62406379": {
1895 "balance": "0x487a9a304539440000"
1896 },
1897 "aa00000000000000000000000000000000000000": {
1898 "code": "0x6042",
1899 "storage": {
1900 "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
1901 "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000",
1902 "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000",
1903 "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303"
1904 },
1905 "balance": "0x1",
1906 "nonce": "0x1"
1907 },
1908 "bb00000000000000000000000000000000000000": {
1909 "code": "0x600154600354",
1910 "storage": {
1911 "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
1912 "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000",
1913 "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000",
1914 "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303"
1915 },
1916 "balance": "0x2",
1917 "nonce": "0x1"
1918 }
1919 },
1920 "number": "0x0",
1921 "gasUsed": "0x0",
1922 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1923 "baseFeePerGas": "0x3b9aca00"
1924 }
1925 "#;
1926
1927 let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
1928 let chainspec = ChainSpec::from(genesis);
1929
1930 assert_eq!(
1932 chainspec.hardforks.get(EthereumHardfork::Homestead).unwrap(),
1933 ForkCondition::Block(0)
1934 );
1935 assert_eq!(
1936 chainspec.hardforks.get(EthereumHardfork::Tangerine).unwrap(),
1937 ForkCondition::Block(0)
1938 );
1939 assert_eq!(
1940 chainspec.hardforks.get(EthereumHardfork::SpuriousDragon).unwrap(),
1941 ForkCondition::Block(0)
1942 );
1943 assert_eq!(
1944 chainspec.hardforks.get(EthereumHardfork::Byzantium).unwrap(),
1945 ForkCondition::Block(0)
1946 );
1947 assert_eq!(
1948 chainspec.hardforks.get(EthereumHardfork::Constantinople).unwrap(),
1949 ForkCondition::Block(0)
1950 );
1951 assert_eq!(
1952 chainspec.hardforks.get(EthereumHardfork::Petersburg).unwrap(),
1953 ForkCondition::Block(0)
1954 );
1955 assert_eq!(
1956 chainspec.hardforks.get(EthereumHardfork::Istanbul).unwrap(),
1957 ForkCondition::Block(0)
1958 );
1959 assert_eq!(
1960 chainspec.hardforks.get(EthereumHardfork::MuirGlacier).unwrap(),
1961 ForkCondition::Block(0)
1962 );
1963 assert_eq!(
1964 chainspec.hardforks.get(EthereumHardfork::Berlin).unwrap(),
1965 ForkCondition::Block(0)
1966 );
1967 assert_eq!(
1968 chainspec.hardforks.get(EthereumHardfork::London).unwrap(),
1969 ForkCondition::Block(0)
1970 );
1971 assert_eq!(
1972 chainspec.hardforks.get(EthereumHardfork::ArrowGlacier).unwrap(),
1973 ForkCondition::Block(0)
1974 );
1975 assert_eq!(
1976 chainspec.hardforks.get(EthereumHardfork::GrayGlacier).unwrap(),
1977 ForkCondition::Block(0)
1978 );
1979
1980 assert_eq!(
1982 chainspec.hardforks.get(EthereumHardfork::Shanghai).unwrap(),
1983 ForkCondition::Timestamp(0)
1984 );
1985
1986 assert_eq!(
1988 chainspec.hardforks.get(EthereumHardfork::Cancun).unwrap(),
1989 ForkCondition::Timestamp(1)
1990 );
1991
1992 let key_rlp = vec![
1994 (hex!("0x658bdf435d810c91414ec09147daa6db62406379"), &hex!("0xf84d8089487a9a304539440000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")[..]),
1995 (hex!("0xaa00000000000000000000000000000000000000"), &hex!("0xf8440101a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0ce92c756baff35fa740c3557c1a971fd24d2d35b7c8e067880d50cd86bb0bc99")[..]),
1996 (hex!("0xbb00000000000000000000000000000000000000"), &hex!("0xf8440102a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0e25a53cbb501cec2976b393719c63d832423dd70a458731a0b64e4847bbca7d2")[..]),
1997 ];
1998
1999 for (key, expected_rlp) in key_rlp {
2000 let account = chainspec.genesis.alloc.get(&key).expect("account should exist");
2001 assert_eq!(&alloy_rlp::encode(TrieAccount::from(account.clone())), expected_rlp);
2002 }
2003
2004 let expected_state_root: B256 =
2005 hex!("0x078dc6061b1d8eaa8493384b59c9c65ceb917201221d08b80c4de6770b6ec7e7").into();
2006 assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
2007
2008 assert_eq!(chainspec.genesis_header().withdrawals_root, Some(EMPTY_ROOT_HASH));
2009
2010 let expected_hash: B256 =
2011 hex!("0x1fc027d65f820d3eef441ebeec139ebe09e471cf98516dce7b5643ccb27f418c").into();
2012 let hash = chainspec.genesis_hash();
2013 assert_eq!(hash, expected_hash);
2014 }
2015
2016 #[test]
2017 fn hive_geth_json() {
2018 let hive_json = r#"
2019 {
2020 "nonce": "0x0000000000000042",
2021 "difficulty": "0x2123456",
2022 "mixHash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
2023 "coinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
2024 "timestamp": "0x123456",
2025 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2026 "extraData": "0xfafbfcfd",
2027 "gasLimit": "0x2fefd8",
2028 "alloc": {
2029 "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {
2030 "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
2031 },
2032 "e6716f9544a56c530d868e4bfbacb172315bdead": {
2033 "balance": "0x11",
2034 "code": "0x12"
2035 },
2036 "b9c015918bdaba24b4ff057a92a3873d6eb201be": {
2037 "balance": "0x21",
2038 "storage": {
2039 "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22"
2040 }
2041 },
2042 "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {
2043 "balance": "0x31",
2044 "nonce": "0x32"
2045 },
2046 "0000000000000000000000000000000000000001": {
2047 "balance": "0x41"
2048 },
2049 "0000000000000000000000000000000000000002": {
2050 "balance": "0x51"
2051 },
2052 "0000000000000000000000000000000000000003": {
2053 "balance": "0x61"
2054 },
2055 "0000000000000000000000000000000000000004": {
2056 "balance": "0x71"
2057 }
2058 },
2059 "config": {
2060 "ethash": {},
2061 "chainId": 10,
2062 "homesteadBlock": 0,
2063 "eip150Block": 0,
2064 "eip155Block": 0,
2065 "eip158Block": 0,
2066 "byzantiumBlock": 0,
2067 "constantinopleBlock": 0,
2068 "petersburgBlock": 0,
2069 "istanbulBlock": 0
2070 }
2071 }
2072 "#;
2073
2074 let genesis = serde_json::from_str::<Genesis>(hive_json).unwrap();
2075 let chainspec: ChainSpec = genesis.into();
2076 assert_eq!(chainspec.chain, Chain::from_named(NamedChain::Optimism));
2077 let expected_state_root: B256 =
2078 hex!("0x9a6049ac535e3dc7436c189eaa81c73f35abd7f282ab67c32944ff0301d63360").into();
2079 assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
2080 let hard_forks = vec![
2081 EthereumHardfork::Byzantium,
2082 EthereumHardfork::Homestead,
2083 EthereumHardfork::Istanbul,
2084 EthereumHardfork::Petersburg,
2085 EthereumHardfork::Constantinople,
2086 ];
2087 for fork in hard_forks {
2088 assert_eq!(chainspec.hardforks.get(fork).unwrap(), ForkCondition::Block(0));
2089 }
2090
2091 let expected_hash: B256 =
2092 hex!("0x5ae31c6522bd5856129f66be3d582b842e4e9faaa87f21cce547128339a9db3c").into();
2093 let hash = chainspec.genesis_header().hash_slow();
2094 assert_eq!(hash, expected_hash);
2095 }
2096
2097 #[test]
2098 fn test_hive_paris_block_genesis_json() {
2099 let hive_paris = r#"
2102 {
2103 "config": {
2104 "ethash": {},
2105 "chainId": 3503995874084926,
2106 "homesteadBlock": 0,
2107 "eip150Block": 6,
2108 "eip155Block": 12,
2109 "eip158Block": 12,
2110 "byzantiumBlock": 18,
2111 "constantinopleBlock": 24,
2112 "petersburgBlock": 30,
2113 "istanbulBlock": 36,
2114 "muirGlacierBlock": 42,
2115 "berlinBlock": 48,
2116 "londonBlock": 54,
2117 "arrowGlacierBlock": 60,
2118 "grayGlacierBlock": 66,
2119 "mergeNetsplitBlock": 72,
2120 "terminalTotalDifficulty": 9454784,
2121 "shanghaiTime": 780,
2122 "cancunTime": 840
2123 },
2124 "nonce": "0x0",
2125 "timestamp": "0x0",
2126 "extraData": "0x68697665636861696e",
2127 "gasLimit": "0x23f3e20",
2128 "difficulty": "0x20000",
2129 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2130 "coinbase": "0x0000000000000000000000000000000000000000",
2131 "alloc": {
2132 "000f3df6d732807ef1319fb7b8bb8522d0beac02": {
2133 "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
2134 "balance": "0x2a"
2135 },
2136 "0c2c51a0990aee1d73c1228de158688341557508": {
2137 "balance": "0xc097ce7bc90715b34b9f1000000000"
2138 },
2139 "14e46043e63d0e3cdcf2530519f4cfaf35058cb2": {
2140 "balance": "0xc097ce7bc90715b34b9f1000000000"
2141 },
2142 "16c57edf7fa9d9525378b0b81bf8a3ced0620c1c": {
2143 "balance": "0xc097ce7bc90715b34b9f1000000000"
2144 },
2145 "1f4924b14f34e24159387c0a4cdbaa32f3ddb0cf": {
2146 "balance": "0xc097ce7bc90715b34b9f1000000000"
2147 },
2148 "1f5bde34b4afc686f136c7a3cb6ec376f7357759": {
2149 "balance": "0xc097ce7bc90715b34b9f1000000000"
2150 },
2151 "2d389075be5be9f2246ad654ce152cf05990b209": {
2152 "balance": "0xc097ce7bc90715b34b9f1000000000"
2153 },
2154 "3ae75c08b4c907eb63a8960c45b86e1e9ab6123c": {
2155 "balance": "0xc097ce7bc90715b34b9f1000000000"
2156 },
2157 "4340ee1b812acb40a1eb561c019c327b243b92df": {
2158 "balance": "0xc097ce7bc90715b34b9f1000000000"
2159 },
2160 "4a0f1452281bcec5bd90c3dce6162a5995bfe9df": {
2161 "balance": "0xc097ce7bc90715b34b9f1000000000"
2162 },
2163 "4dde844b71bcdf95512fb4dc94e84fb67b512ed8": {
2164 "balance": "0xc097ce7bc90715b34b9f1000000000"
2165 },
2166 "5f552da00dfb4d3749d9e62dcee3c918855a86a0": {
2167 "balance": "0xc097ce7bc90715b34b9f1000000000"
2168 },
2169 "654aa64f5fbefb84c270ec74211b81ca8c44a72e": {
2170 "balance": "0xc097ce7bc90715b34b9f1000000000"
2171 },
2172 "717f8aa2b982bee0e29f573d31df288663e1ce16": {
2173 "balance": "0xc097ce7bc90715b34b9f1000000000"
2174 },
2175 "7435ed30a8b4aeb0877cef0c6e8cffe834eb865f": {
2176 "balance": "0xc097ce7bc90715b34b9f1000000000"
2177 },
2178 "83c7e323d189f18725ac510004fdc2941f8c4a78": {
2179 "balance": "0xc097ce7bc90715b34b9f1000000000"
2180 },
2181 "84e75c28348fb86acea1a93a39426d7d60f4cc46": {
2182 "balance": "0xc097ce7bc90715b34b9f1000000000"
2183 },
2184 "8bebc8ba651aee624937e7d897853ac30c95a067": {
2185 "storage": {
2186 "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001",
2187 "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000002",
2188 "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000003"
2189 },
2190 "balance": "0x1",
2191 "nonce": "0x1"
2192 },
2193 "c7b99a164efd027a93f147376cc7da7c67c6bbe0": {
2194 "balance": "0xc097ce7bc90715b34b9f1000000000"
2195 },
2196 "d803681e487e6ac18053afc5a6cd813c86ec3e4d": {
2197 "balance": "0xc097ce7bc90715b34b9f1000000000"
2198 },
2199 "e7d13f7aa2a838d24c59b40186a0aca1e21cffcc": {
2200 "balance": "0xc097ce7bc90715b34b9f1000000000"
2201 },
2202 "eda8645ba6948855e3b3cd596bbb07596d59c603": {
2203 "balance": "0xc097ce7bc90715b34b9f1000000000"
2204 }
2205 },
2206 "number": "0x0",
2207 "gasUsed": "0x0",
2208 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2209 "baseFeePerGas": null,
2210 "excessBlobGas": null,
2211 "blobGasUsed": null
2212 }
2213 "#;
2214
2215 let genesis: Genesis = serde_json::from_str(hive_paris).unwrap();
2217 let chainspec = ChainSpec::from(genesis);
2218
2219 let expected_forkid = ForkId { hash: ForkHash([0xbc, 0x0c, 0x26, 0x05]), next: 0 };
2221 let got_forkid =
2222 chainspec.fork_id(&Head { number: 73, timestamp: 840, ..Default::default() });
2223
2224 assert_eq!(got_forkid, expected_forkid);
2226 assert_eq!(chainspec.paris_block_and_final_difficulty, Some((72, U256::from(9454784))));
2228 }
2229
2230 #[test]
2231 fn test_parse_genesis_json() {
2232 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"}"#;
2233 let genesis: Genesis = serde_json::from_str(s).unwrap();
2234 let acc = genesis
2235 .alloc
2236 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2237 .unwrap();
2238 assert_eq!(acc.balance, U256::from(1));
2239 assert_eq!(genesis.base_fee_per_gas, Some(0x1337));
2240 }
2241
2242 #[test]
2243 fn test_parse_cancun_genesis_json() {
2244 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"}"#;
2245 let genesis: Genesis = serde_json::from_str(s).unwrap();
2246 let acc = genesis
2247 .alloc
2248 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2249 .unwrap();
2250 assert_eq!(acc.balance, U256::from(1));
2251 assert_eq!(genesis.config.cancun_time, Some(4661));
2253 }
2254
2255 #[test]
2256 fn test_parse_prague_genesis_all_formats() {
2257 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"}"#;
2258 let genesis: Genesis = serde_json::from_str(s).unwrap();
2259
2260 let acc = genesis
2262 .alloc
2263 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2264 .unwrap();
2265 assert_eq!(acc.balance, U256::from(1));
2266 assert_eq!(genesis.config.cancun_time, Some(4661));
2268 assert_eq!(genesis.config.prague_time, Some(4662));
2270 }
2271
2272 #[test]
2273 fn test_parse_cancun_genesis_all_formats() {
2274 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"}"#;
2275 let genesis: Genesis = serde_json::from_str(s).unwrap();
2276
2277 let acc = genesis
2279 .alloc
2280 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2281 .unwrap();
2282 assert_eq!(acc.balance, U256::from(1));
2283 assert_eq!(genesis.config.cancun_time, Some(4661));
2285 }
2286
2287 #[test]
2288 fn test_paris_block_and_total_difficulty() {
2289 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2290 let paris_chainspec = ChainSpecBuilder::default()
2291 .chain(Chain::from_id(1337))
2292 .genesis(genesis)
2293 .paris_activated()
2294 .build();
2295 assert_eq!(paris_chainspec.paris_block_and_final_difficulty, Some((0, U256::ZERO)));
2296 }
2297
2298 #[test]
2299 fn test_default_cancun_header_forkhash() {
2300 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2302 let default_chainspec = ChainSpecBuilder::default()
2303 .chain(Chain::from_id(1337))
2304 .genesis(genesis)
2305 .cancun_activated()
2306 .build();
2307 let mut header = default_chainspec.genesis_header().clone();
2308
2309 header.state_root =
2311 B256::from_str("0x62e2595e017f0ca23e08d17221010721a71c3ae932f4ea3cb12117786bb392d4")
2312 .unwrap();
2313
2314 assert_eq!(header.withdrawals_root, Some(EMPTY_WITHDRAWALS));
2316
2317 assert_eq!(header.parent_beacon_block_root, Some(B256::ZERO));
2320 assert_eq!(header.blob_gas_used, Some(0));
2321 assert_eq!(header.excess_blob_gas, Some(0));
2322
2323 let genesis_hash = header.hash_slow();
2325 let expected_hash =
2326 b256!("0x16bb7c59613a5bad3f7c04a852fd056545ade2483968d9a25a1abb05af0c4d37");
2327 assert_eq!(genesis_hash, expected_hash);
2328
2329 let expected_forkhash = ForkHash(hex!("8062457a"));
2331 assert_eq!(ForkHash::from(genesis_hash), expected_forkhash);
2332 }
2333
2334 #[test]
2335 fn holesky_paris_activated_at_genesis() {
2336 assert!(HOLESKY
2337 .fork(EthereumHardfork::Paris)
2338 .active_at_ttd(HOLESKY.genesis.difficulty, HOLESKY.genesis.difficulty));
2339 }
2340
2341 #[test]
2342 fn test_genesis_format_deserialization() {
2343 let config = ChainConfig {
2345 chain_id: 2600,
2346 homestead_block: Some(0),
2347 eip150_block: Some(0),
2348 eip155_block: Some(0),
2349 eip158_block: Some(0),
2350 byzantium_block: Some(0),
2351 constantinople_block: Some(0),
2352 petersburg_block: Some(0),
2353 istanbul_block: Some(0),
2354 berlin_block: Some(0),
2355 london_block: Some(0),
2356 shanghai_time: Some(0),
2357 terminal_total_difficulty: Some(U256::ZERO),
2358 terminal_total_difficulty_passed: true,
2359 ..Default::default()
2360 };
2361 let genesis = Genesis {
2363 config,
2364 nonce: 0,
2365 timestamp: 1698688670,
2366 gas_limit: 5000,
2367 difficulty: U256::ZERO,
2368 mix_hash: B256::ZERO,
2369 coinbase: Address::ZERO,
2370 ..Default::default()
2371 };
2372
2373 let address = hex!("0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b").into();
2375 let account = GenesisAccount::default().with_balance(U256::from(33));
2376 let genesis = genesis.extend_accounts(HashMap::from([(address, account)]));
2377
2378 let serialized_genesis = serde_json::to_string(&genesis).unwrap();
2380 let deserialized_genesis: Genesis = serde_json::from_str(&serialized_genesis).unwrap();
2381
2382 assert_eq!(genesis, deserialized_genesis);
2383 }
2384
2385 #[test]
2386 fn check_fork_id_chainspec_with_fork_condition_never() {
2387 let spec = ChainSpec {
2388 chain: Chain::mainnet(),
2389 genesis: Genesis::default(),
2390 hardforks: ChainHardforks::new(vec![(
2391 EthereumHardfork::Frontier.boxed(),
2392 ForkCondition::Never,
2393 )]),
2394 paris_block_and_final_difficulty: None,
2395 deposit_contract: None,
2396 ..Default::default()
2397 };
2398
2399 assert_eq!(spec.hardfork_fork_id(EthereumHardfork::Frontier), None);
2400 }
2401
2402 #[test]
2403 fn check_fork_filter_chainspec_with_fork_condition_never() {
2404 let spec = ChainSpec {
2405 chain: Chain::mainnet(),
2406 genesis: Genesis::default(),
2407 hardforks: ChainHardforks::new(vec![(
2408 EthereumHardfork::Shanghai.boxed(),
2409 ForkCondition::Never,
2410 )]),
2411 paris_block_and_final_difficulty: None,
2412 deposit_contract: None,
2413 ..Default::default()
2414 };
2415
2416 assert_eq!(spec.hardfork_fork_filter(EthereumHardfork::Shanghai), None);
2417 }
2418
2419 #[test]
2420 fn latest_eth_mainnet_fork_id() {
2421 assert_eq!(
2422 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
2423 MAINNET.latest_fork_id()
2424 )
2425 }
2426
2427 #[test]
2428 fn test_fork_order_ethereum_mainnet() {
2429 let genesis = Genesis {
2430 config: ChainConfig {
2431 chain_id: 0,
2432 homestead_block: Some(0),
2433 dao_fork_block: Some(0),
2434 dao_fork_support: false,
2435 eip150_block: Some(0),
2436 eip155_block: Some(0),
2437 eip158_block: Some(0),
2438 byzantium_block: Some(0),
2439 constantinople_block: Some(0),
2440 petersburg_block: Some(0),
2441 istanbul_block: Some(0),
2442 muir_glacier_block: Some(0),
2443 berlin_block: Some(0),
2444 london_block: Some(0),
2445 arrow_glacier_block: Some(0),
2446 gray_glacier_block: Some(0),
2447 merge_netsplit_block: Some(0),
2448 shanghai_time: Some(0),
2449 cancun_time: Some(0),
2450 terminal_total_difficulty: Some(U256::ZERO),
2451 ..Default::default()
2452 },
2453 ..Default::default()
2454 };
2455
2456 let chain_spec: ChainSpec = genesis.into();
2457
2458 let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect();
2459 let expected_hardforks = vec![
2460 EthereumHardfork::Frontier.boxed(),
2461 EthereumHardfork::Homestead.boxed(),
2462 EthereumHardfork::Dao.boxed(),
2463 EthereumHardfork::Tangerine.boxed(),
2464 EthereumHardfork::SpuriousDragon.boxed(),
2465 EthereumHardfork::Byzantium.boxed(),
2466 EthereumHardfork::Constantinople.boxed(),
2467 EthereumHardfork::Petersburg.boxed(),
2468 EthereumHardfork::Istanbul.boxed(),
2469 EthereumHardfork::MuirGlacier.boxed(),
2470 EthereumHardfork::Berlin.boxed(),
2471 EthereumHardfork::London.boxed(),
2472 EthereumHardfork::ArrowGlacier.boxed(),
2473 EthereumHardfork::GrayGlacier.boxed(),
2474 EthereumHardfork::Paris.boxed(),
2475 EthereumHardfork::Shanghai.boxed(),
2476 EthereumHardfork::Cancun.boxed(),
2477 ];
2478
2479 assert!(expected_hardforks
2480 .iter()
2481 .zip(hardforks.iter())
2482 .all(|(expected, actual)| &**expected == *actual));
2483 assert_eq!(expected_hardforks.len(), hardforks.len());
2484 }
2485
2486 #[test]
2487 fn test_calc_base_block_reward() {
2488 let cases = [
2490 ((0, U256::ZERO), Some(ETH_TO_WEI * 5)),
2492 ((4370000, U256::ZERO), Some(ETH_TO_WEI * 3)),
2494 ((7280000, U256::ZERO), Some(ETH_TO_WEI * 2)),
2496 ((15537394, U256::from(58_750_000_000_000_000_000_000_u128)), None),
2498 ];
2499
2500 for ((block_number, _td), expected_reward) in cases {
2501 assert_eq!(base_block_reward(&*MAINNET, block_number), expected_reward);
2502 }
2503 }
2504
2505 #[test]
2506 fn test_calc_full_block_reward() {
2507 let base_reward = ETH_TO_WEI;
2508 let one_thirty_twoth_reward = base_reward >> 5;
2509
2510 let cases = [
2512 (0, base_reward),
2513 (1, base_reward + one_thirty_twoth_reward),
2514 (2, base_reward + one_thirty_twoth_reward * 2),
2515 ];
2516
2517 for (num_ommers, expected_reward) in cases {
2518 assert_eq!(block_reward(base_reward, num_ommers), expected_reward);
2519 }
2520 }
2521
2522 #[test]
2523 fn blob_params_from_genesis() {
2524 let s = r#"{
2525 "cancun":{
2526 "baseFeeUpdateFraction":3338477,
2527 "max":6,
2528 "target":3
2529 },
2530 "prague":{
2531 "baseFeeUpdateFraction":3338477,
2532 "max":6,
2533 "target":3
2534 }
2535 }"#;
2536 let schedule: BTreeMap<String, BlobParams> = serde_json::from_str(s).unwrap();
2537 let hardfork_params = HardforkBlobParams::from_schedule(&schedule);
2538 let expected = HardforkBlobParams {
2539 cancun: BlobParams {
2540 target_blob_count: 3,
2541 max_blob_count: 6,
2542 update_fraction: 3338477,
2543 min_blob_fee: BLOB_TX_MIN_BLOB_GASPRICE,
2544 },
2545 prague: BlobParams {
2546 target_blob_count: 3,
2547 max_blob_count: 6,
2548 update_fraction: 3338477,
2549 min_blob_fee: BLOB_TX_MIN_BLOB_GASPRICE,
2550 },
2551 };
2552 assert_eq!(hardfork_params, expected);
2553 }
2554}