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