1pub use alloy_eips::eip1559::BaseFeeParams;
2use alloy_evm::eth::spec::EthExecutorSpec;
3
4use crate::{
5 constants::{MAINNET_DEPOSIT_CONTRACT, MAINNET_PRUNE_DELETE_LIMIT},
6 ethereum::SEPOLIA_PARIS_TTD,
7 holesky, hoodi,
8 mainnet::{MAINNET_PARIS_BLOCK, MAINNET_PARIS_TTD},
9 sepolia,
10 sepolia::SEPOLIA_PARIS_BLOCK,
11 EthChainSpec,
12};
13use alloc::{boxed::Box, format, sync::Arc, vec::Vec};
14use alloy_chains::{Chain, NamedChain};
15use alloy_consensus::{
16 constants::{
17 EMPTY_WITHDRAWALS, HOLESKY_GENESIS_HASH, HOODI_GENESIS_HASH, MAINNET_GENESIS_HASH,
18 SEPOLIA_GENESIS_HASH,
19 },
20 Header,
21};
22use alloy_eips::{
23 eip1559::INITIAL_BASE_FEE, eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams,
24 eip7892::BlobScheduleBlobParams,
25};
26use alloy_genesis::Genesis;
27use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256};
28use alloy_trie::root::state_root_ref_unhashed;
29use core::fmt::Debug;
30use derive_more::From;
31use reth_ethereum_forks::{
32 ChainHardforks, DisplayHardforks, EthereumHardfork, EthereumHardforks, ForkCondition,
33 ForkFilter, ForkFilterKey, ForkHash, ForkId, Hardfork, Hardforks, Head, DEV_HARDFORKS,
34};
35use reth_network_peers::{
36 holesky_nodes, hoodi_nodes, mainnet_nodes, op_nodes, op_testnet_nodes, sepolia_nodes,
37 NodeRecord,
38};
39use reth_primitives_traits::{sync::LazyLock, BlockHeader, SealedHeader};
40
41pub fn make_genesis_header(genesis: &Genesis, hardforks: &ChainHardforks) -> Header {
43 let base_fee_per_gas = hardforks
45 .fork(EthereumHardfork::London)
46 .active_at_block(0)
47 .then(|| genesis.base_fee_per_gas.map(|fee| fee as u64).unwrap_or(INITIAL_BASE_FEE));
48
49 let withdrawals_root = hardforks
52 .fork(EthereumHardfork::Shanghai)
53 .active_at_timestamp(genesis.timestamp)
54 .then_some(EMPTY_WITHDRAWALS);
55
56 let (parent_beacon_block_root, blob_gas_used, excess_blob_gas) =
61 if hardforks.fork(EthereumHardfork::Cancun).active_at_timestamp(genesis.timestamp) {
62 let blob_gas_used = genesis.blob_gas_used.unwrap_or(0);
63 let excess_blob_gas = genesis.excess_blob_gas.unwrap_or(0);
64 (Some(B256::ZERO), Some(blob_gas_used), Some(excess_blob_gas))
65 } else {
66 (None, None, None)
67 };
68
69 let requests_hash = hardforks
71 .fork(EthereumHardfork::Prague)
72 .active_at_timestamp(genesis.timestamp)
73 .then_some(EMPTY_REQUESTS_HASH);
74
75 Header {
76 gas_limit: genesis.gas_limit,
77 difficulty: genesis.difficulty,
78 nonce: genesis.nonce.into(),
79 extra_data: genesis.extra_data.clone(),
80 state_root: state_root_ref_unhashed(&genesis.alloc),
81 timestamp: genesis.timestamp,
82 mix_hash: genesis.mix_hash,
83 beneficiary: genesis.coinbase,
84 base_fee_per_gas,
85 withdrawals_root,
86 parent_beacon_block_root,
87 blob_gas_used,
88 excess_blob_gas,
89 requests_hash,
90 ..Default::default()
91 }
92}
93
94pub static MAINNET: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
96 let genesis = serde_json::from_str(include_str!("../res/genesis/mainnet.json"))
97 .expect("Can't deserialize Mainnet genesis json");
98 let hardforks = EthereumHardfork::mainnet().into();
99 let mut spec = ChainSpec {
100 chain: Chain::mainnet(),
101 genesis_header: SealedHeader::new(
102 make_genesis_header(&genesis, &hardforks),
103 MAINNET_GENESIS_HASH,
104 ),
105 genesis,
106 paris_block_and_final_difficulty: Some((
108 MAINNET_PARIS_BLOCK,
109 U256::from(58_750_003_716_598_352_816_469u128),
110 )),
111 hardforks,
112 deposit_contract: Some(MAINNET_DEPOSIT_CONTRACT),
114 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
115 prune_delete_limit: MAINNET_PRUNE_DELETE_LIMIT,
116 blob_params: BlobScheduleBlobParams::default(),
117 };
118 spec.genesis.config.dao_fork_support = true;
119 spec.into()
120});
121
122pub static SEPOLIA: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
124 let genesis = serde_json::from_str(include_str!("../res/genesis/sepolia.json"))
125 .expect("Can't deserialize Sepolia genesis json");
126 let hardforks = EthereumHardfork::sepolia().into();
127 let mut spec = ChainSpec {
128 chain: Chain::sepolia(),
129 genesis_header: SealedHeader::new(
130 make_genesis_header(&genesis, &hardforks),
131 SEPOLIA_GENESIS_HASH,
132 ),
133 genesis,
134 paris_block_and_final_difficulty: Some((
136 SEPOLIA_PARIS_BLOCK,
137 U256::from(17_000_018_015_853_232u128),
138 )),
139 hardforks,
140 deposit_contract: Some(DepositContract::new(
142 address!("0x7f02c3e3c98b133055b8b348b2ac625669ed295d"),
143 1273020,
144 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
145 )),
146 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
147 prune_delete_limit: 10000,
148 blob_params: BlobScheduleBlobParams::default().with_scheduled([
149 (sepolia::SEPOLIA_BPO1_TIMESTAMP, BlobParams::bpo1()),
150 (sepolia::SEPOLIA_BPO2_TIMESTAMP, BlobParams::bpo2()),
151 ]),
152 };
153 spec.genesis.config.dao_fork_support = true;
154 spec.into()
155});
156
157pub static HOLESKY: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
159 let genesis = serde_json::from_str(include_str!("../res/genesis/holesky.json"))
160 .expect("Can't deserialize Holesky genesis json");
161 let hardforks = EthereumHardfork::holesky().into();
162 let mut spec = ChainSpec {
163 chain: Chain::holesky(),
164 genesis_header: SealedHeader::new(
165 make_genesis_header(&genesis, &hardforks),
166 HOLESKY_GENESIS_HASH,
167 ),
168 genesis,
169 paris_block_and_final_difficulty: Some((0, U256::from(1))),
170 hardforks,
171 deposit_contract: Some(DepositContract::new(
172 address!("0x4242424242424242424242424242424242424242"),
173 0,
174 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
175 )),
176 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
177 prune_delete_limit: 10000,
178 blob_params: BlobScheduleBlobParams::default().with_scheduled([
179 (holesky::HOLESKY_BPO1_TIMESTAMP, BlobParams::bpo1()),
180 (holesky::HOLESKY_BPO2_TIMESTAMP, BlobParams::bpo2()),
181 ]),
182 };
183 spec.genesis.config.dao_fork_support = true;
184 spec.into()
185});
186
187pub static HOODI: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
191 let genesis = serde_json::from_str(include_str!("../res/genesis/hoodi.json"))
192 .expect("Can't deserialize Hoodi genesis json");
193 let hardforks = EthereumHardfork::hoodi().into();
194 let mut spec = ChainSpec {
195 chain: Chain::hoodi(),
196 genesis_header: SealedHeader::new(
197 make_genesis_header(&genesis, &hardforks),
198 HOODI_GENESIS_HASH,
199 ),
200 genesis,
201 paris_block_and_final_difficulty: Some((0, U256::from(0))),
202 hardforks,
203 deposit_contract: Some(DepositContract::new(
204 address!("0x00000000219ab540356cBB839Cbe05303d7705Fa"),
205 0,
206 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
207 )),
208 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
209 prune_delete_limit: 10000,
210 blob_params: BlobScheduleBlobParams::default().with_scheduled([
211 (hoodi::HOODI_BPO1_TIMESTAMP, BlobParams::bpo1()),
212 (hoodi::HOODI_BPO2_TIMESTAMP, BlobParams::bpo2()),
213 ]),
214 };
215 spec.genesis.config.dao_fork_support = true;
216 spec.into()
217});
218
219pub static DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
224 let genesis = serde_json::from_str(include_str!("../res/genesis/dev.json"))
225 .expect("Can't deserialize Dev testnet genesis json");
226 let hardforks = DEV_HARDFORKS.clone();
227 ChainSpec {
228 chain: Chain::dev(),
229 genesis_header: SealedHeader::seal_slow(make_genesis_header(&genesis, &hardforks)),
230 genesis,
231 paris_block_and_final_difficulty: Some((0, U256::from(0))),
232 hardforks,
233 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
234 deposit_contract: None, ..Default::default()
236 }
237 .into()
238});
239
240#[derive(Clone, Debug, PartialEq, Eq)]
243pub enum BaseFeeParamsKind {
244 Constant(BaseFeeParams),
246 Variable(ForkBaseFeeParams),
249}
250
251impl Default for BaseFeeParamsKind {
252 fn default() -> Self {
253 BaseFeeParams::ethereum().into()
254 }
255}
256
257impl From<BaseFeeParams> for BaseFeeParamsKind {
258 fn from(params: BaseFeeParams) -> Self {
259 Self::Constant(params)
260 }
261}
262
263impl From<ForkBaseFeeParams> for BaseFeeParamsKind {
264 fn from(params: ForkBaseFeeParams) -> Self {
265 Self::Variable(params)
266 }
267}
268
269#[derive(Clone, Debug, PartialEq, Eq, From)]
272pub struct ForkBaseFeeParams(Vec<(Box<dyn Hardfork>, BaseFeeParams)>);
273
274impl<H: BlockHeader> core::ops::Deref for ChainSpec<H> {
275 type Target = ChainHardforks;
276
277 fn deref(&self) -> &Self::Target {
278 &self.hardforks
279 }
280}
281
282#[derive(Debug, Clone, PartialEq, Eq)]
290pub struct ChainSpec<H: BlockHeader = Header> {
291 pub chain: Chain,
293
294 pub genesis: Genesis,
296
297 pub genesis_header: SealedHeader<H>,
299
300 pub paris_block_and_final_difficulty: Option<(u64, U256)>,
303
304 pub hardforks: ChainHardforks,
306
307 pub deposit_contract: Option<DepositContract>,
309
310 pub base_fee_params: BaseFeeParamsKind,
312
313 pub prune_delete_limit: usize,
315
316 pub blob_params: BlobScheduleBlobParams,
318}
319
320impl<H: BlockHeader> Default for ChainSpec<H> {
321 fn default() -> Self {
322 Self {
323 chain: Default::default(),
324 genesis: Default::default(),
325 genesis_header: Default::default(),
326 paris_block_and_final_difficulty: Default::default(),
327 hardforks: Default::default(),
328 deposit_contract: Default::default(),
329 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
330 prune_delete_limit: MAINNET_PRUNE_DELETE_LIMIT,
331 blob_params: Default::default(),
332 }
333 }
334}
335
336impl ChainSpec {
337 pub fn from_genesis(genesis: Genesis) -> Self {
339 genesis.into()
340 }
341
342 pub fn builder() -> ChainSpecBuilder {
344 ChainSpecBuilder::default()
345 }
346}
347
348impl<H: BlockHeader> ChainSpec<H> {
349 pub const fn chain(&self) -> Chain {
351 self.chain
352 }
353
354 #[inline]
356 pub const fn is_ethereum(&self) -> bool {
357 self.chain.is_ethereum()
358 }
359
360 #[inline]
362 pub fn is_optimism_mainnet(&self) -> bool {
363 self.chain == Chain::optimism_mainnet()
364 }
365
366 #[inline]
368 pub fn paris_block(&self) -> Option<u64> {
369 self.paris_block_and_final_difficulty.map(|(block, _)| block)
370 }
371
372 pub const fn genesis(&self) -> &Genesis {
376 &self.genesis
377 }
378
379 pub fn genesis_header(&self) -> &H {
381 &self.genesis_header
382 }
383
384 pub fn sealed_genesis_header(&self) -> SealedHeader<H> {
386 SealedHeader::new(self.genesis_header().clone(), self.genesis_hash())
387 }
388
389 pub fn initial_base_fee(&self) -> Option<u64> {
391 let genesis_base_fee =
393 self.genesis.base_fee_per_gas.map(|fee| fee as u64).unwrap_or(INITIAL_BASE_FEE);
394
395 self.hardforks.fork(EthereumHardfork::London).active_at_block(0).then_some(genesis_base_fee)
397 }
398
399 pub fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams {
401 match self.base_fee_params {
402 BaseFeeParamsKind::Constant(bf_params) => bf_params,
403 BaseFeeParamsKind::Variable(ForkBaseFeeParams(ref bf_params)) => {
404 for (fork, params) in bf_params.iter().rev() {
408 if self.hardforks.is_fork_active_at_timestamp(fork.clone(), timestamp) {
409 return *params
410 }
411 }
412
413 bf_params.first().map(|(_, params)| *params).unwrap_or(BaseFeeParams::ethereum())
414 }
415 }
416 }
417
418 pub fn genesis_hash(&self) -> B256 {
420 self.genesis_header.hash()
421 }
422
423 pub const fn genesis_timestamp(&self) -> u64 {
425 self.genesis.timestamp
426 }
427
428 pub fn get_final_paris_total_difficulty(&self) -> Option<U256> {
430 self.paris_block_and_final_difficulty.map(|(_, final_difficulty)| final_difficulty)
431 }
432
433 pub fn hardfork_fork_filter<HF: Hardfork + Clone>(&self, fork: HF) -> Option<ForkFilter> {
435 match self.hardforks.fork(fork.clone()) {
436 ForkCondition::Never => None,
437 _ => Some(self.fork_filter(self.satisfy(self.hardforks.fork(fork)))),
438 }
439 }
440
441 pub fn display_hardforks(&self) -> DisplayHardforks {
443 let hardforks_with_meta = self.hardforks.forks_iter().map(|(fork, condition)| {
445 let metadata = match condition {
447 ForkCondition::Timestamp(timestamp) => {
448 EthChainSpec::blob_params_at_timestamp(self, timestamp).map(|params| {
451 format!(
452 "blob: (target: {}, max: {}, fraction: {})",
453 params.target_blob_count, params.max_blob_count, params.update_fraction
454 )
455 })
456 }
457 _ => None,
458 };
459 (fork, condition, metadata)
460 });
461
462 DisplayHardforks::with_meta(hardforks_with_meta)
463 }
464
465 #[inline]
467 pub fn hardfork_fork_id<HF: Hardfork + Clone>(&self, fork: HF) -> Option<ForkId> {
468 let condition = self.hardforks.fork(fork);
469 match condition {
470 ForkCondition::Never => None,
471 _ => Some(self.fork_id(&self.satisfy(condition))),
472 }
473 }
474
475 #[inline]
478 pub fn shanghai_fork_id(&self) -> Option<ForkId> {
479 self.hardfork_fork_id(EthereumHardfork::Shanghai)
480 }
481
482 #[inline]
485 pub fn cancun_fork_id(&self) -> Option<ForkId> {
486 self.hardfork_fork_id(EthereumHardfork::Cancun)
487 }
488
489 #[inline]
492 pub fn latest_fork_id(&self) -> ForkId {
493 self.hardfork_fork_id(self.hardforks.last().unwrap().0).unwrap()
494 }
495
496 pub fn fork_filter(&self, head: Head) -> ForkFilter {
498 let forks = self.hardforks.forks_iter().filter_map(|(_, condition)| {
499 Some(match condition {
502 ForkCondition::Block(block) |
503 ForkCondition::TTD { fork_block: Some(block), .. } => ForkFilterKey::Block(block),
504 ForkCondition::Timestamp(time) => ForkFilterKey::Time(time),
505 _ => return None,
506 })
507 });
508
509 ForkFilter::new(head, self.genesis_hash(), self.genesis_timestamp(), forks)
510 }
511
512 pub fn fork_id(&self, head: &Head) -> ForkId {
517 let mut forkhash = ForkHash::from(self.genesis_hash());
518
519 let mut current_applied = 0;
525
526 for (_, cond) in self.hardforks.forks_iter() {
528 if let ForkCondition::Block(block) |
531 ForkCondition::TTD { fork_block: Some(block), .. } = cond
532 {
533 if head.number >= block {
534 if block != current_applied {
536 forkhash += block;
537 current_applied = block;
538 }
539 } else {
540 return ForkId { hash: forkhash, next: block }
543 }
544 }
545 }
546
547 for timestamp in self.hardforks.forks_iter().filter_map(|(_, cond)| {
551 cond.as_timestamp().filter(|time| time > &self.genesis.timestamp)
553 }) {
554 if head.timestamp >= timestamp {
555 if timestamp != current_applied {
557 forkhash += timestamp;
558 current_applied = timestamp;
559 }
560 } else {
561 return ForkId { hash: forkhash, next: timestamp }
565 }
566 }
567
568 ForkId { hash: forkhash, next: 0 }
569 }
570
571 pub(crate) fn satisfy(&self, cond: ForkCondition) -> Head {
573 match cond {
574 ForkCondition::Block(number) => Head { number, ..Default::default() },
575 ForkCondition::Timestamp(timestamp) => {
576 Head {
579 timestamp,
580 number: self.last_block_fork_before_merge_or_timestamp().unwrap_or_default(),
581 ..Default::default()
582 }
583 }
584 ForkCondition::TTD { total_difficulty, fork_block, .. } => Head {
585 total_difficulty,
586 number: fork_block.unwrap_or_default(),
587 ..Default::default()
588 },
589 ForkCondition::Never => unreachable!(),
590 }
591 }
592
593 pub(crate) fn last_block_fork_before_merge_or_timestamp(&self) -> Option<u64> {
605 let mut hardforks_iter = self.hardforks.forks_iter().peekable();
606 while let Some((_, curr_cond)) = hardforks_iter.next() {
607 if let Some((_, next_cond)) = hardforks_iter.peek() {
608 match next_cond {
612 ForkCondition::TTD { fork_block: Some(block), .. } => return Some(*block),
615
616 ForkCondition::TTD { .. } | ForkCondition::Timestamp(_) => {
619 if let ForkCondition::Block(block_num) = curr_cond {
622 return Some(block_num);
623 }
624 }
625 ForkCondition::Block(_) | ForkCondition::Never => {}
626 }
627 }
628 }
629 None
630 }
631
632 pub fn bootnodes(&self) -> Option<Vec<NodeRecord>> {
634 use NamedChain as C;
635
636 match self.chain.try_into().ok()? {
637 C::Mainnet => Some(mainnet_nodes()),
638 C::Sepolia => Some(sepolia_nodes()),
639 C::Holesky => Some(holesky_nodes()),
640 C::Hoodi => Some(hoodi_nodes()),
641 C::Base | C::Optimism | C::Unichain | C::World => Some(op_nodes()),
643 C::OptimismSepolia | C::BaseSepolia | C::UnichainSepolia | C::WorldSepolia => {
644 Some(op_testnet_nodes())
645 }
646
647 chain if chain.is_optimism() && chain.is_testnet() => Some(op_testnet_nodes()),
649 chain if chain.is_optimism() => Some(op_nodes()),
650 _ => None,
651 }
652 }
653
654 pub fn map_header<NewH: BlockHeader>(self, f: impl FnOnce(H) -> NewH) -> ChainSpec<NewH> {
656 let Self {
657 chain,
658 genesis,
659 genesis_header,
660 paris_block_and_final_difficulty,
661 hardforks,
662 deposit_contract,
663 base_fee_params,
664 prune_delete_limit,
665 blob_params,
666 } = self;
667 ChainSpec {
668 chain,
669 genesis,
670 genesis_header: SealedHeader::new_unhashed(f(genesis_header.into_header())),
671 paris_block_and_final_difficulty,
672 hardforks,
673 deposit_contract,
674 base_fee_params,
675 prune_delete_limit,
676 blob_params,
677 }
678 }
679}
680
681impl From<Genesis> for ChainSpec {
682 fn from(genesis: Genesis) -> Self {
683 let hardfork_opts = [
685 (EthereumHardfork::Frontier.boxed(), Some(0)),
686 (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block),
687 (EthereumHardfork::Dao.boxed(), genesis.config.dao_fork_block),
688 (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block),
689 (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block),
690 (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block),
691 (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block),
692 (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block),
693 (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block),
694 (EthereumHardfork::MuirGlacier.boxed(), genesis.config.muir_glacier_block),
695 (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block),
696 (EthereumHardfork::London.boxed(), genesis.config.london_block),
697 (EthereumHardfork::ArrowGlacier.boxed(), genesis.config.arrow_glacier_block),
698 (EthereumHardfork::GrayGlacier.boxed(), genesis.config.gray_glacier_block),
699 ];
700 let mut hardforks = hardfork_opts
701 .into_iter()
702 .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block))))
703 .collect::<Vec<_>>();
704
705 let paris_block_and_final_difficulty = if let Some(ttd) =
709 genesis.config.terminal_total_difficulty
710 {
711 hardforks.push((
712 EthereumHardfork::Paris.boxed(),
713 ForkCondition::TTD {
714 activation_block_number: genesis
717 .config
718 .merge_netsplit_block
719 .or_else(|| {
720 match genesis.config.chain_id {
728 1 => {
729 if ttd == MAINNET_PARIS_TTD {
730 return Some(MAINNET_PARIS_BLOCK)
731 }
732 }
733 11155111 => {
734 if ttd == SEPOLIA_PARIS_TTD {
735 return Some(SEPOLIA_PARIS_BLOCK)
736 }
737 }
738 _ => {}
739 };
740 None
741 })
742 .unwrap_or_default(),
743 total_difficulty: ttd,
744 fork_block: genesis.config.merge_netsplit_block,
745 },
746 ));
747
748 genesis.config.merge_netsplit_block.map(|block| (block, ttd))
749 } else {
750 None
751 };
752
753 let time_hardfork_opts = [
755 (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time),
756 (EthereumHardfork::Cancun.boxed(), genesis.config.cancun_time),
757 (EthereumHardfork::Prague.boxed(), genesis.config.prague_time),
758 (EthereumHardfork::Osaka.boxed(), genesis.config.osaka_time),
759 (EthereumHardfork::Bpo1.boxed(), genesis.config.bpo1_time),
760 (EthereumHardfork::Bpo2.boxed(), genesis.config.bpo2_time),
761 (EthereumHardfork::Bpo3.boxed(), genesis.config.bpo3_time),
762 (EthereumHardfork::Bpo4.boxed(), genesis.config.bpo4_time),
763 (EthereumHardfork::Bpo5.boxed(), genesis.config.bpo5_time),
764 ];
765
766 let mut time_hardforks = time_hardfork_opts
767 .into_iter()
768 .filter_map(|(hardfork, opt)| {
769 opt.map(|time| (hardfork, ForkCondition::Timestamp(time)))
770 })
771 .collect::<Vec<_>>();
772
773 hardforks.append(&mut time_hardforks);
774
775 let mainnet_hardforks: ChainHardforks = EthereumHardfork::mainnet().into();
777 let mainnet_order = mainnet_hardforks.forks_iter();
778
779 let mut ordered_hardforks = Vec::with_capacity(hardforks.len());
780 for (hardfork, _) in mainnet_order {
781 if let Some(pos) = hardforks.iter().position(|(e, _)| **e == *hardfork) {
782 ordered_hardforks.push(hardforks.remove(pos));
783 }
784 }
785
786 ordered_hardforks.append(&mut hardforks);
788
789 let blob_params = genesis.config.blob_schedule_blob_params();
791
792 let deposit_contract = genesis.config.deposit_contract_address.map(|address| {
797 DepositContract { address, block: 0, topic: MAINNET_DEPOSIT_CONTRACT.topic }
798 });
799
800 let hardforks = ChainHardforks::new(ordered_hardforks);
801
802 Self {
803 chain: genesis.config.chain_id.into(),
804 genesis_header: SealedHeader::new_unhashed(make_genesis_header(&genesis, &hardforks)),
805 genesis,
806 hardforks,
807 paris_block_and_final_difficulty,
808 deposit_contract,
809 blob_params,
810 ..Default::default()
811 }
812 }
813}
814
815impl<H: BlockHeader> Hardforks for ChainSpec<H> {
816 fn fork<HF: Hardfork>(&self, fork: HF) -> ForkCondition {
817 self.hardforks.fork(fork)
818 }
819
820 fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
821 self.hardforks.forks_iter()
822 }
823
824 fn fork_id(&self, head: &Head) -> ForkId {
825 self.fork_id(head)
826 }
827
828 fn latest_fork_id(&self) -> ForkId {
829 self.latest_fork_id()
830 }
831
832 fn fork_filter(&self, head: Head) -> ForkFilter {
833 self.fork_filter(head)
834 }
835}
836
837impl<H: BlockHeader> EthereumHardforks for ChainSpec<H> {
838 fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition {
839 self.fork(fork)
840 }
841}
842
843#[auto_impl::auto_impl(&, Arc)]
845pub trait ChainSpecProvider: Debug + Send + Sync {
846 type ChainSpec: EthChainSpec + 'static;
848
849 fn chain_spec(&self) -> Arc<Self::ChainSpec>;
851}
852
853#[derive(Debug, Default, Clone)]
855pub struct ChainSpecBuilder {
856 chain: Option<Chain>,
857 genesis: Option<Genesis>,
858 hardforks: ChainHardforks,
859}
860
861impl ChainSpecBuilder {
862 pub fn mainnet() -> Self {
864 Self {
865 chain: Some(MAINNET.chain),
866 genesis: Some(MAINNET.genesis.clone()),
867 hardforks: MAINNET.hardforks.clone(),
868 }
869 }
870}
871
872impl ChainSpecBuilder {
873 pub const fn chain(mut self, chain: Chain) -> Self {
875 self.chain = Some(chain);
876 self
877 }
878
879 pub fn reset(mut self) -> Self {
881 self.hardforks = ChainHardforks::default();
882 self
883 }
884
885 pub fn genesis(mut self, genesis: Genesis) -> Self {
887 self.genesis = Some(genesis);
888 self
889 }
890
891 pub fn with_fork<H: Hardfork>(mut self, fork: H, condition: ForkCondition) -> Self {
893 self.hardforks.insert(fork, condition);
894 self
895 }
896
897 pub fn with_forks(mut self, forks: ChainHardforks) -> Self {
899 self.hardforks = forks;
900 self
901 }
902
903 pub fn without_fork<H: Hardfork>(mut self, fork: H) -> Self {
905 self.hardforks.remove(&fork);
906 self
907 }
908
909 pub fn paris_at_ttd(self, ttd: U256, activation_block_number: BlockNumber) -> Self {
913 self.with_fork(
914 EthereumHardfork::Paris,
915 ForkCondition::TTD { activation_block_number, total_difficulty: ttd, fork_block: None },
916 )
917 }
918
919 pub fn frontier_activated(mut self) -> Self {
921 self.hardforks.insert(EthereumHardfork::Frontier, ForkCondition::Block(0));
922 self
923 }
924
925 pub fn homestead_activated(mut self) -> Self {
927 self = self.frontier_activated();
928 self.hardforks.insert(EthereumHardfork::Homestead, ForkCondition::Block(0));
929 self
930 }
931
932 pub fn tangerine_whistle_activated(mut self) -> Self {
934 self = self.homestead_activated();
935 self.hardforks.insert(EthereumHardfork::Tangerine, ForkCondition::Block(0));
936 self
937 }
938
939 pub fn spurious_dragon_activated(mut self) -> Self {
941 self = self.tangerine_whistle_activated();
942 self.hardforks.insert(EthereumHardfork::SpuriousDragon, ForkCondition::Block(0));
943 self
944 }
945
946 pub fn byzantium_activated(mut self) -> Self {
948 self = self.spurious_dragon_activated();
949 self.hardforks.insert(EthereumHardfork::Byzantium, ForkCondition::Block(0));
950 self
951 }
952
953 pub fn constantinople_activated(mut self) -> Self {
955 self = self.byzantium_activated();
956 self.hardforks.insert(EthereumHardfork::Constantinople, ForkCondition::Block(0));
957 self
958 }
959
960 pub fn petersburg_activated(mut self) -> Self {
962 self = self.constantinople_activated();
963 self.hardforks.insert(EthereumHardfork::Petersburg, ForkCondition::Block(0));
964 self
965 }
966
967 pub fn istanbul_activated(mut self) -> Self {
969 self = self.petersburg_activated();
970 self.hardforks.insert(EthereumHardfork::Istanbul, ForkCondition::Block(0));
971 self
972 }
973
974 pub fn berlin_activated(mut self) -> Self {
976 self = self.istanbul_activated();
977 self.hardforks.insert(EthereumHardfork::Berlin, ForkCondition::Block(0));
978 self
979 }
980
981 pub fn london_activated(mut self) -> Self {
983 self = self.berlin_activated();
984 self.hardforks.insert(EthereumHardfork::London, ForkCondition::Block(0));
985 self
986 }
987
988 pub fn paris_activated(mut self) -> Self {
990 self = self.london_activated();
991 self.hardforks.insert(
992 EthereumHardfork::Paris,
993 ForkCondition::TTD {
994 activation_block_number: 0,
995 total_difficulty: U256::ZERO,
996 fork_block: None,
997 },
998 );
999 self
1000 }
1001
1002 pub fn shanghai_activated(mut self) -> Self {
1004 self = self.paris_activated();
1005 self.hardforks.insert(EthereumHardfork::Shanghai, ForkCondition::Timestamp(0));
1006 self
1007 }
1008
1009 pub fn cancun_activated(mut self) -> Self {
1011 self = self.shanghai_activated();
1012 self.hardforks.insert(EthereumHardfork::Cancun, ForkCondition::Timestamp(0));
1013 self
1014 }
1015
1016 pub fn prague_activated(mut self) -> Self {
1018 self = self.cancun_activated();
1019 self.hardforks.insert(EthereumHardfork::Prague, ForkCondition::Timestamp(0));
1020 self
1021 }
1022
1023 pub fn with_prague_at(mut self, timestamp: u64) -> Self {
1025 self.hardforks.insert(EthereumHardfork::Prague, ForkCondition::Timestamp(timestamp));
1026 self
1027 }
1028
1029 pub fn osaka_activated(mut self) -> Self {
1031 self = self.prague_activated();
1032 self.hardforks.insert(EthereumHardfork::Osaka, ForkCondition::Timestamp(0));
1033 self
1034 }
1035
1036 pub fn with_osaka_at(mut self, timestamp: u64) -> Self {
1038 self.hardforks.insert(EthereumHardfork::Osaka, ForkCondition::Timestamp(timestamp));
1039 self
1040 }
1041
1042 pub fn build(self) -> ChainSpec {
1049 let paris_block_and_final_difficulty = {
1050 self.hardforks.get(EthereumHardfork::Paris).and_then(|cond| {
1051 if let ForkCondition::TTD { total_difficulty, activation_block_number, .. } = cond {
1052 Some((activation_block_number, total_difficulty))
1053 } else {
1054 None
1055 }
1056 })
1057 };
1058 let genesis = self.genesis.expect("The genesis is required");
1059 ChainSpec {
1060 chain: self.chain.expect("The chain is required"),
1061 genesis_header: SealedHeader::new_unhashed(make_genesis_header(
1062 &genesis,
1063 &self.hardforks,
1064 )),
1065 genesis,
1066 hardforks: self.hardforks,
1067 paris_block_and_final_difficulty,
1068 deposit_contract: None,
1069 ..Default::default()
1070 }
1071 }
1072}
1073
1074impl From<&Arc<ChainSpec>> for ChainSpecBuilder {
1075 fn from(value: &Arc<ChainSpec>) -> Self {
1076 Self {
1077 chain: Some(value.chain),
1078 genesis: Some(value.genesis.clone()),
1079 hardforks: value.hardforks.clone(),
1080 }
1081 }
1082}
1083
1084impl<H: BlockHeader> EthExecutorSpec for ChainSpec<H> {
1085 fn deposit_contract_address(&self) -> Option<Address> {
1086 self.deposit_contract.map(|deposit_contract| deposit_contract.address)
1087 }
1088}
1089
1090#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1092pub struct DepositContract {
1093 pub address: Address,
1095 pub block: BlockNumber,
1097 pub topic: B256,
1099}
1100
1101impl DepositContract {
1102 pub const fn new(address: Address, block: BlockNumber, topic: B256) -> Self {
1104 Self { address, block, topic }
1105 }
1106}
1107
1108#[cfg(any(test, feature = "test-utils"))]
1110pub fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) {
1111 for (block, expected_id) in cases {
1112 let computed_id = spec.fork_id(block);
1113 assert_eq!(
1114 expected_id, &computed_id,
1115 "Expected fork ID {:?}, computed fork ID {:?} at block {}",
1116 expected_id, computed_id, block.number
1117 );
1118 }
1119}
1120
1121#[cfg(test)]
1122mod tests {
1123 use super::*;
1124 use alloy_chains::Chain;
1125 use alloy_consensus::constants::ETH_TO_WEI;
1126 use alloy_eips::{eip4844::BLOB_TX_MIN_BLOB_GASPRICE, eip7840::BlobParams};
1127 use alloy_evm::block::calc::{base_block_reward, block_reward};
1128 use alloy_genesis::{ChainConfig, GenesisAccount};
1129 use alloy_primitives::{b256, hex};
1130 use alloy_trie::{TrieAccount, EMPTY_ROOT_HASH};
1131 use core::ops::Deref;
1132 use reth_ethereum_forks::{ForkCondition, ForkHash, ForkId, Head};
1133 use std::{collections::HashMap, str::FromStr};
1134
1135 fn test_hardfork_fork_ids(spec: &ChainSpec, cases: &[(EthereumHardfork, ForkId)]) {
1136 for (hardfork, expected_id) in cases {
1137 if let Some(computed_id) = spec.hardfork_fork_id(*hardfork) {
1138 assert_eq!(
1139 expected_id, &computed_id,
1140 "Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for hardfork {hardfork}"
1141 );
1142 if matches!(hardfork, EthereumHardfork::Shanghai) {
1143 if let Some(shanghai_id) = spec.shanghai_fork_id() {
1144 assert_eq!(
1145 expected_id, &shanghai_id,
1146 "Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for Shanghai hardfork"
1147 );
1148 } else {
1149 panic!("Expected ForkCondition to return Some for Hardfork::Shanghai");
1150 }
1151 }
1152 }
1153 }
1154 }
1155
1156 #[test]
1157 fn test_hardfork_list_display_mainnet() {
1158 assert_eq!(
1159 MAINNET.display_hardforks().to_string(),
1160 "Pre-merge hard forks (block based):
1161- Frontier @0
1162- Homestead @1150000
1163- Dao @1920000
1164- Tangerine @2463000
1165- SpuriousDragon @2675000
1166- Byzantium @4370000
1167- Constantinople @7280000
1168- Petersburg @7280000
1169- Istanbul @9069000
1170- MuirGlacier @9200000
1171- Berlin @12244000
1172- London @12965000
1173- ArrowGlacier @13773000
1174- GrayGlacier @15050000
1175Merge hard forks:
1176- Paris @58750000000000000000000 (network is known to be merged)
1177Post-merge hard forks (timestamp based):
1178- Shanghai @1681338455
1179- Cancun @1710338135 blob: (target: 3, max: 6, fraction: 3338477)
1180- Prague @1746612311 blob: (target: 6, max: 9, fraction: 5007716)"
1181 );
1182 }
1183
1184 #[test]
1185 fn test_hardfork_list_ignores_disabled_forks() {
1186 let spec = ChainSpec::builder()
1187 .chain(Chain::mainnet())
1188 .genesis(Genesis::default())
1189 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1190 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Never)
1191 .build();
1192 assert_eq!(
1193 spec.display_hardforks().to_string(),
1194 "Pre-merge hard forks (block based):
1195- Frontier @0"
1196 );
1197 }
1198
1199 #[test]
1201 fn ignores_genesis_fork_blocks() {
1202 let spec = ChainSpec::builder()
1203 .chain(Chain::mainnet())
1204 .genesis(Genesis::default())
1205 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1206 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(0))
1207 .with_fork(EthereumHardfork::Tangerine, ForkCondition::Block(0))
1208 .with_fork(EthereumHardfork::SpuriousDragon, ForkCondition::Block(0))
1209 .with_fork(EthereumHardfork::Byzantium, ForkCondition::Block(0))
1210 .with_fork(EthereumHardfork::Constantinople, ForkCondition::Block(0))
1211 .with_fork(EthereumHardfork::Istanbul, ForkCondition::Block(0))
1212 .with_fork(EthereumHardfork::MuirGlacier, ForkCondition::Block(0))
1213 .with_fork(EthereumHardfork::Berlin, ForkCondition::Block(0))
1214 .with_fork(EthereumHardfork::London, ForkCondition::Block(0))
1215 .with_fork(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0))
1216 .with_fork(EthereumHardfork::GrayGlacier, ForkCondition::Block(0))
1217 .build();
1218
1219 assert_eq!(spec.deref().len(), 12, "12 forks should be active.");
1220 assert_eq!(
1221 spec.fork_id(&Head { number: 1, ..Default::default() }),
1222 ForkId { hash: ForkHash::from(spec.genesis_hash()), next: 0 },
1223 "the fork ID should be the genesis hash; forks at genesis are ignored for fork filters"
1224 );
1225 }
1226
1227 #[test]
1228 fn ignores_duplicate_fork_blocks() {
1229 let empty_genesis = Genesis::default();
1230 let unique_spec = ChainSpec::builder()
1231 .chain(Chain::mainnet())
1232 .genesis(empty_genesis.clone())
1233 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1234 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(1))
1235 .build();
1236
1237 let duplicate_spec = ChainSpec::builder()
1238 .chain(Chain::mainnet())
1239 .genesis(empty_genesis)
1240 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1241 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(1))
1242 .with_fork(EthereumHardfork::Tangerine, ForkCondition::Block(1))
1243 .build();
1244
1245 assert_eq!(
1246 unique_spec.fork_id(&Head { number: 2, ..Default::default() }),
1247 duplicate_spec.fork_id(&Head { number: 2, ..Default::default() }),
1248 "duplicate fork blocks should be deduplicated for fork filters"
1249 );
1250 }
1251
1252 #[test]
1253 fn test_chainspec_satisfy() {
1254 let empty_genesis = Genesis::default();
1255 let happy_path_case = ChainSpec::builder()
1257 .chain(Chain::mainnet())
1258 .genesis(empty_genesis.clone())
1259 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1260 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1261 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1262 .build();
1263 let happy_path_head = happy_path_case.satisfy(ForkCondition::Timestamp(11313123));
1264 let happy_path_expected = Head { number: 73, timestamp: 11313123, ..Default::default() };
1265 assert_eq!(
1266 happy_path_head, happy_path_expected,
1267 "expected satisfy() to return {happy_path_expected:#?}, but got {happy_path_head:#?} "
1268 );
1269 let multiple_timestamp_fork_case = ChainSpec::builder()
1271 .chain(Chain::mainnet())
1272 .genesis(empty_genesis.clone())
1273 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1274 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1275 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1276 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(11313398))
1277 .build();
1278 let multi_timestamp_head =
1279 multiple_timestamp_fork_case.satisfy(ForkCondition::Timestamp(11313398));
1280 let mult_timestamp_expected =
1281 Head { number: 73, timestamp: 11313398, ..Default::default() };
1282 assert_eq!(
1283 multi_timestamp_head, mult_timestamp_expected,
1284 "expected satisfy() to return {mult_timestamp_expected:#?}, but got {multi_timestamp_head:#?} "
1285 );
1286 let no_block_fork_case = ChainSpec::builder()
1288 .chain(Chain::mainnet())
1289 .genesis(empty_genesis.clone())
1290 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1291 .build();
1292 let no_block_fork_head = no_block_fork_case.satisfy(ForkCondition::Timestamp(11313123));
1293 let no_block_fork_expected = Head { number: 0, timestamp: 11313123, ..Default::default() };
1294 assert_eq!(
1295 no_block_fork_head, no_block_fork_expected,
1296 "expected satisfy() to return {no_block_fork_expected:#?}, but got {no_block_fork_head:#?} ",
1297 );
1298 let fork_cond_ttd_blocknum_case = ChainSpec::builder()
1300 .chain(Chain::mainnet())
1301 .genesis(empty_genesis.clone())
1302 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1303 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1304 .with_fork(
1305 EthereumHardfork::Paris,
1306 ForkCondition::TTD {
1307 activation_block_number: 101,
1308 fork_block: Some(101),
1309 total_difficulty: U256::from(10_790_000),
1310 },
1311 )
1312 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1313 .build();
1314 let fork_cond_ttd_blocknum_head =
1315 fork_cond_ttd_blocknum_case.satisfy(ForkCondition::Timestamp(11313123));
1316 let fork_cond_ttd_blocknum_expected =
1317 Head { number: 101, timestamp: 11313123, ..Default::default() };
1318 assert_eq!(
1319 fork_cond_ttd_blocknum_head, fork_cond_ttd_blocknum_expected,
1320 "expected satisfy() to return {fork_cond_ttd_blocknum_expected:#?}, but got {fork_cond_ttd_blocknum_head:#?} ",
1321 );
1322
1323 let fork_cond_block_only_case = ChainSpec::builder()
1327 .chain(Chain::mainnet())
1328 .genesis(empty_genesis)
1329 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1330 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1331 .build();
1332 let fork_cond_block_only_head = fork_cond_block_only_case.satisfy(ForkCondition::Block(73));
1333 let fork_cond_block_only_expected = Head { number: 73, ..Default::default() };
1334 assert_eq!(
1335 fork_cond_block_only_head, fork_cond_block_only_expected,
1336 "expected satisfy() to return {fork_cond_block_only_expected:#?}, but got {fork_cond_block_only_head:#?} ",
1337 );
1338 let fork_cond_ttd_no_new_spec = fork_cond_block_only_case.satisfy(ForkCondition::TTD {
1341 activation_block_number: 101,
1342 fork_block: None,
1343 total_difficulty: U256::from(10_790_000),
1344 });
1345 let fork_cond_ttd_no_new_spec_expected =
1346 Head { total_difficulty: U256::from(10_790_000), ..Default::default() };
1347 assert_eq!(
1348 fork_cond_ttd_no_new_spec, fork_cond_ttd_no_new_spec_expected,
1349 "expected satisfy() to return {fork_cond_ttd_no_new_spec_expected:#?}, but got {fork_cond_ttd_no_new_spec:#?} ",
1350 );
1351 }
1352
1353 #[test]
1354 fn mainnet_hardfork_fork_ids() {
1355 test_hardfork_fork_ids(
1356 &MAINNET,
1357 &[
1358 (
1359 EthereumHardfork::Frontier,
1360 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1361 ),
1362 (
1363 EthereumHardfork::Homestead,
1364 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1365 ),
1366 (
1367 EthereumHardfork::Dao,
1368 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1369 ),
1370 (
1371 EthereumHardfork::Tangerine,
1372 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1373 ),
1374 (
1375 EthereumHardfork::SpuriousDragon,
1376 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1377 ),
1378 (
1379 EthereumHardfork::Byzantium,
1380 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1381 ),
1382 (
1383 EthereumHardfork::Constantinople,
1384 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1385 ),
1386 (
1387 EthereumHardfork::Petersburg,
1388 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1389 ),
1390 (
1391 EthereumHardfork::Istanbul,
1392 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1393 ),
1394 (
1395 EthereumHardfork::MuirGlacier,
1396 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1397 ),
1398 (
1399 EthereumHardfork::Berlin,
1400 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1401 ),
1402 (
1403 EthereumHardfork::London,
1404 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1405 ),
1406 (
1407 EthereumHardfork::ArrowGlacier,
1408 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1409 ),
1410 (
1411 EthereumHardfork::GrayGlacier,
1412 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1413 ),
1414 (
1415 EthereumHardfork::Shanghai,
1416 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1417 ),
1418 (
1419 EthereumHardfork::Cancun,
1420 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
1421 ),
1422 (
1423 EthereumHardfork::Prague,
1424 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
1425 ),
1426 ],
1427 );
1428 }
1429
1430 #[test]
1431 fn sepolia_hardfork_fork_ids() {
1432 test_hardfork_fork_ids(
1433 &SEPOLIA,
1434 &[
1435 (
1436 EthereumHardfork::Frontier,
1437 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1438 ),
1439 (
1440 EthereumHardfork::Homestead,
1441 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1442 ),
1443 (
1444 EthereumHardfork::Tangerine,
1445 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1446 ),
1447 (
1448 EthereumHardfork::SpuriousDragon,
1449 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1450 ),
1451 (
1452 EthereumHardfork::Byzantium,
1453 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1454 ),
1455 (
1456 EthereumHardfork::Constantinople,
1457 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1458 ),
1459 (
1460 EthereumHardfork::Petersburg,
1461 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1462 ),
1463 (
1464 EthereumHardfork::Istanbul,
1465 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1466 ),
1467 (
1468 EthereumHardfork::Berlin,
1469 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1470 ),
1471 (
1472 EthereumHardfork::London,
1473 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1474 ),
1475 (
1476 EthereumHardfork::Paris,
1477 ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
1478 ),
1479 (
1480 EthereumHardfork::Shanghai,
1481 ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
1482 ),
1483 (
1484 EthereumHardfork::Cancun,
1485 ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
1486 ),
1487 (
1488 EthereumHardfork::Prague,
1489 ForkId {
1490 hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]),
1491 next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
1492 },
1493 ),
1494 ],
1495 );
1496 }
1497
1498 #[test]
1499 fn mainnet_fork_ids() {
1500 test_fork_ids(
1501 &MAINNET,
1502 &[
1503 (
1504 Head { number: 0, ..Default::default() },
1505 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1506 ),
1507 (
1508 Head { number: 1150000, ..Default::default() },
1509 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1510 ),
1511 (
1512 Head { number: 1920000, ..Default::default() },
1513 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1514 ),
1515 (
1516 Head { number: 2463000, ..Default::default() },
1517 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1518 ),
1519 (
1520 Head { number: 2675000, ..Default::default() },
1521 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1522 ),
1523 (
1524 Head { number: 4370000, ..Default::default() },
1525 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1526 ),
1527 (
1528 Head { number: 7280000, ..Default::default() },
1529 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1530 ),
1531 (
1532 Head { number: 9069000, ..Default::default() },
1533 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1534 ),
1535 (
1536 Head { number: 9200000, ..Default::default() },
1537 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1538 ),
1539 (
1540 Head { number: 12244000, ..Default::default() },
1541 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1542 ),
1543 (
1544 Head { number: 12965000, ..Default::default() },
1545 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1546 ),
1547 (
1548 Head { number: 13773000, ..Default::default() },
1549 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1550 ),
1551 (
1552 Head { number: 15050000, ..Default::default() },
1553 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1554 ),
1555 (
1557 Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
1558 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1559 ),
1560 (
1562 Head { number: 20000001, timestamp: 1710338135, ..Default::default() },
1563 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
1564 ),
1565 (
1567 Head { number: 20000002, timestamp: 1746612311, ..Default::default() },
1568 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
1569 ),
1570 (
1572 Head { number: 20000002, timestamp: 2000000000, ..Default::default() },
1573 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
1574 ),
1575 ],
1576 );
1577 }
1578
1579 #[test]
1580 fn hoodi_fork_ids() {
1581 test_fork_ids(
1582 &HOODI,
1583 &[
1584 (
1585 Head { number: 0, ..Default::default() },
1586 ForkId { hash: ForkHash([0xbe, 0xf7, 0x1d, 0x30]), next: 1742999832 },
1587 ),
1588 (
1590 Head { number: 0, timestamp: 1742999833, ..Default::default() },
1591 ForkId {
1592 hash: ForkHash([0x09, 0x29, 0xe2, 0x4e]),
1593 next: hoodi::HOODI_OSAKA_TIMESTAMP,
1594 },
1595 ),
1596 (
1598 Head {
1599 number: 0,
1600 timestamp: hoodi::HOODI_OSAKA_TIMESTAMP,
1601 ..Default::default()
1602 },
1603 ForkId {
1604 hash: ForkHash(hex!("0xe7e0e7ff")),
1605 next: hoodi::HOODI_BPO1_TIMESTAMP,
1606 },
1607 ),
1608 ],
1609 )
1610 }
1611
1612 #[test]
1613 fn holesky_fork_ids() {
1614 test_fork_ids(
1615 &HOLESKY,
1616 &[
1617 (
1618 Head { number: 0, ..Default::default() },
1619 ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
1620 ),
1621 (
1623 Head { number: 123, ..Default::default() },
1624 ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
1625 ),
1626 (
1628 Head { number: 123, timestamp: 1696000703, ..Default::default() },
1629 ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
1630 ),
1631 (
1633 Head { number: 123, timestamp: 1696000704, ..Default::default() },
1634 ForkId { hash: ForkHash([0xfd, 0x4f, 0x01, 0x6b]), next: 1707305664 },
1635 ),
1636 (
1638 Head { number: 123, timestamp: 1707305663, ..Default::default() },
1639 ForkId { hash: ForkHash([0xfd, 0x4f, 0x01, 0x6b]), next: 1707305664 },
1640 ),
1641 (
1643 Head { number: 123, timestamp: 1707305664, ..Default::default() },
1644 ForkId { hash: ForkHash([0x9b, 0x19, 0x2a, 0xd0]), next: 1740434112 },
1645 ),
1646 (
1648 Head { number: 123, timestamp: 1740434111, ..Default::default() },
1649 ForkId { hash: ForkHash([0x9b, 0x19, 0x2a, 0xd0]), next: 1740434112 },
1650 ),
1651 (
1653 Head { number: 123, timestamp: 1740434112, ..Default::default() },
1654 ForkId {
1655 hash: ForkHash([0xdf, 0xbd, 0x9b, 0xed]),
1656 next: holesky::HOLESKY_OSAKA_TIMESTAMP,
1657 },
1658 ),
1659 (
1661 Head {
1662 number: 123,
1663 timestamp: holesky::HOLESKY_OSAKA_TIMESTAMP,
1664 ..Default::default()
1665 },
1666 ForkId {
1667 hash: ForkHash(hex!("0x783def52")),
1668 next: holesky::HOLESKY_BPO1_TIMESTAMP,
1669 },
1670 ),
1671 ],
1672 )
1673 }
1674
1675 #[test]
1676 fn sepolia_fork_ids() {
1677 test_fork_ids(
1678 &SEPOLIA,
1679 &[
1680 (
1681 Head { number: 0, ..Default::default() },
1682 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1683 ),
1684 (
1685 Head { number: 1735370, ..Default::default() },
1686 ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
1687 ),
1688 (
1689 Head { number: 1735371, ..Default::default() },
1690 ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
1691 ),
1692 (
1693 Head { number: 1735372, timestamp: 1677557087, ..Default::default() },
1694 ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
1695 ),
1696 (
1698 Head { number: 1735373, timestamp: 1677557088, ..Default::default() },
1699 ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
1700 ),
1701 (
1703 Head { number: 1735374, timestamp: 1706655071, ..Default::default() },
1704 ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
1705 ),
1706 (
1708 Head { number: 1735375, timestamp: 1706655072, ..Default::default() },
1709 ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
1710 ),
1711 (
1713 Head { number: 1735376, timestamp: 1741159775, ..Default::default() },
1714 ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
1715 ),
1716 (
1718 Head { number: 1735377, timestamp: 1741159776, ..Default::default() },
1719 ForkId {
1720 hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]),
1721 next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
1722 },
1723 ),
1724 (
1726 Head {
1727 number: 1735377,
1728 timestamp: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
1729 ..Default::default()
1730 },
1731 ForkId {
1732 hash: ForkHash(hex!("0xe2ae4999")),
1733 next: sepolia::SEPOLIA_BPO1_TIMESTAMP,
1734 },
1735 ),
1736 ],
1737 );
1738 }
1739
1740 #[test]
1741 fn dev_fork_ids() {
1742 test_fork_ids(
1743 &DEV,
1744 &[(
1745 Head { number: 0, ..Default::default() },
1746 ForkId { hash: ForkHash([0x0b, 0x1a, 0x4e, 0xf7]), next: 0 },
1747 )],
1748 )
1749 }
1750
1751 #[test]
1755 fn timestamped_forks() {
1756 let mainnet_with_timestamps = ChainSpecBuilder::mainnet().build();
1757 test_fork_ids(
1758 &mainnet_with_timestamps,
1759 &[
1760 (
1761 Head { number: 0, timestamp: 0, ..Default::default() },
1762 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1763 ), (
1765 Head { number: 1149999, timestamp: 0, ..Default::default() },
1766 ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
1767 ), (
1769 Head { number: 1150000, timestamp: 0, ..Default::default() },
1770 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1771 ), (
1773 Head { number: 1919999, timestamp: 0, ..Default::default() },
1774 ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
1775 ), (
1777 Head { number: 1920000, timestamp: 0, ..Default::default() },
1778 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1779 ), (
1781 Head { number: 2462999, timestamp: 0, ..Default::default() },
1782 ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
1783 ), (
1785 Head { number: 2463000, timestamp: 0, ..Default::default() },
1786 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1787 ), (
1789 Head { number: 2674999, timestamp: 0, ..Default::default() },
1790 ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
1791 ), (
1793 Head { number: 2675000, timestamp: 0, ..Default::default() },
1794 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1795 ), (
1797 Head { number: 4369999, timestamp: 0, ..Default::default() },
1798 ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
1799 ), (
1801 Head { number: 4370000, timestamp: 0, ..Default::default() },
1802 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1803 ), (
1805 Head { number: 7279999, timestamp: 0, ..Default::default() },
1806 ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
1807 ), (
1809 Head { number: 7280000, timestamp: 0, ..Default::default() },
1810 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1811 ), (
1813 Head { number: 9068999, timestamp: 0, ..Default::default() },
1814 ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
1815 ), (
1817 Head { number: 9069000, timestamp: 0, ..Default::default() },
1818 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1819 ), (
1821 Head { number: 9199999, timestamp: 0, ..Default::default() },
1822 ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
1823 ), (
1825 Head { number: 9200000, timestamp: 0, ..Default::default() },
1826 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1827 ), (
1829 Head { number: 12243999, timestamp: 0, ..Default::default() },
1830 ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
1831 ), (
1833 Head { number: 12244000, timestamp: 0, ..Default::default() },
1834 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1835 ), (
1837 Head { number: 12964999, timestamp: 0, ..Default::default() },
1838 ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
1839 ), (
1841 Head { number: 12965000, timestamp: 0, ..Default::default() },
1842 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1843 ), (
1845 Head { number: 13772999, timestamp: 0, ..Default::default() },
1846 ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
1847 ), (
1849 Head { number: 13773000, timestamp: 0, ..Default::default() },
1850 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1851 ), (
1853 Head { number: 15049999, timestamp: 0, ..Default::default() },
1854 ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
1855 ), (
1857 Head { number: 15050000, timestamp: 0, ..Default::default() },
1858 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1859 ), (
1861 Head { number: 19999999, timestamp: 1667999999, ..Default::default() },
1862 ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
1863 ), (
1865 Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
1866 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1867 ), (
1869 Head { number: 20000001, timestamp: 1710338134, ..Default::default() },
1870 ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
1871 ), (
1873 Head { number: 20000002, timestamp: 1710338135, ..Default::default() },
1874 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
1875 ), (
1877 Head { number: 20000003, timestamp: 1746612310, ..Default::default() },
1878 ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
1879 ), (
1881 Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
1882 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
1883 ), (
1885 Head { number: 20000004, timestamp: 2000000000, ..Default::default() },
1886 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
1887 ),
1888 ],
1889 );
1890 }
1891
1892 fn construct_chainspec(
1895 builder: ChainSpecBuilder,
1896 shanghai_time: u64,
1897 cancun_time: u64,
1898 ) -> ChainSpec {
1899 builder
1900 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(shanghai_time))
1901 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(cancun_time))
1902 .build()
1903 }
1904
1905 #[test]
1910 fn test_timestamp_fork_in_genesis() {
1911 let timestamp = 1690475657u64;
1912 let default_spec_builder = ChainSpecBuilder::default()
1913 .chain(Chain::from_id(1337))
1914 .genesis(Genesis::default().with_timestamp(timestamp))
1915 .paris_activated();
1916
1917 let tests = [
1920 (
1921 construct_chainspec(default_spec_builder.clone(), timestamp - 1, timestamp + 1),
1922 timestamp + 1,
1923 ),
1924 (
1925 construct_chainspec(default_spec_builder.clone(), timestamp, timestamp + 1),
1926 timestamp + 1,
1927 ),
1928 (
1929 construct_chainspec(default_spec_builder, timestamp + 1, timestamp + 2),
1930 timestamp + 1,
1931 ),
1932 ];
1933
1934 for (spec, expected_timestamp) in tests {
1935 let got_forkid = spec.fork_id(&Head { number: 0, timestamp: 0, ..Default::default() });
1936 let genesis_hash = spec.genesis_hash();
1941 let expected_forkid =
1942 ForkId { hash: ForkHash::from(genesis_hash), next: expected_timestamp };
1943 assert_eq!(got_forkid, expected_forkid);
1944 }
1945 }
1946
1947 #[test]
1949 fn check_terminal_ttd() {
1950 let chainspec = ChainSpecBuilder::mainnet().build();
1951
1952 let terminal_block_ttd = U256::from(58750003716598352816469_u128);
1954 let terminal_block_difficulty = U256::from(11055787484078698_u128);
1955 assert!(!chainspec
1956 .fork(EthereumHardfork::Paris)
1957 .active_at_ttd(terminal_block_ttd, terminal_block_difficulty));
1958
1959 let first_pos_block_ttd = U256::from(58750003716598352816469_u128);
1961 let first_pos_difficulty = U256::ZERO;
1962 assert!(chainspec
1963 .fork(EthereumHardfork::Paris)
1964 .active_at_ttd(first_pos_block_ttd, first_pos_difficulty));
1965 }
1966
1967 #[test]
1968 fn geth_genesis_with_shanghai() {
1969 let geth_genesis = r#"
1970 {
1971 "config": {
1972 "chainId": 1337,
1973 "homesteadBlock": 0,
1974 "eip150Block": 0,
1975 "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1976 "eip155Block": 0,
1977 "eip158Block": 0,
1978 "byzantiumBlock": 0,
1979 "constantinopleBlock": 0,
1980 "petersburgBlock": 0,
1981 "istanbulBlock": 0,
1982 "muirGlacierBlock": 0,
1983 "berlinBlock": 0,
1984 "londonBlock": 0,
1985 "arrowGlacierBlock": 0,
1986 "grayGlacierBlock": 0,
1987 "shanghaiTime": 0,
1988 "cancunTime": 1,
1989 "terminalTotalDifficulty": 0,
1990 "terminalTotalDifficultyPassed": true,
1991 "ethash": {}
1992 },
1993 "nonce": "0x0",
1994 "timestamp": "0x0",
1995 "extraData": "0x",
1996 "gasLimit": "0x4c4b40",
1997 "difficulty": "0x1",
1998 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
1999 "coinbase": "0x0000000000000000000000000000000000000000",
2000 "alloc": {
2001 "658bdf435d810c91414ec09147daa6db62406379": {
2002 "balance": "0x487a9a304539440000"
2003 },
2004 "aa00000000000000000000000000000000000000": {
2005 "code": "0x6042",
2006 "storage": {
2007 "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
2008 "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000",
2009 "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000",
2010 "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303"
2011 },
2012 "balance": "0x1",
2013 "nonce": "0x1"
2014 },
2015 "bb00000000000000000000000000000000000000": {
2016 "code": "0x600154600354",
2017 "storage": {
2018 "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
2019 "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000",
2020 "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000",
2021 "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303"
2022 },
2023 "balance": "0x2",
2024 "nonce": "0x1"
2025 }
2026 },
2027 "number": "0x0",
2028 "gasUsed": "0x0",
2029 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2030 "baseFeePerGas": "0x3b9aca00"
2031 }
2032 "#;
2033
2034 let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
2035 let chainspec = ChainSpec::from(genesis);
2036
2037 assert_eq!(
2039 chainspec.hardforks.get(EthereumHardfork::Homestead).unwrap(),
2040 ForkCondition::Block(0)
2041 );
2042 assert_eq!(
2043 chainspec.hardforks.get(EthereumHardfork::Tangerine).unwrap(),
2044 ForkCondition::Block(0)
2045 );
2046 assert_eq!(
2047 chainspec.hardforks.get(EthereumHardfork::SpuriousDragon).unwrap(),
2048 ForkCondition::Block(0)
2049 );
2050 assert_eq!(
2051 chainspec.hardforks.get(EthereumHardfork::Byzantium).unwrap(),
2052 ForkCondition::Block(0)
2053 );
2054 assert_eq!(
2055 chainspec.hardforks.get(EthereumHardfork::Constantinople).unwrap(),
2056 ForkCondition::Block(0)
2057 );
2058 assert_eq!(
2059 chainspec.hardforks.get(EthereumHardfork::Petersburg).unwrap(),
2060 ForkCondition::Block(0)
2061 );
2062 assert_eq!(
2063 chainspec.hardforks.get(EthereumHardfork::Istanbul).unwrap(),
2064 ForkCondition::Block(0)
2065 );
2066 assert_eq!(
2067 chainspec.hardforks.get(EthereumHardfork::MuirGlacier).unwrap(),
2068 ForkCondition::Block(0)
2069 );
2070 assert_eq!(
2071 chainspec.hardforks.get(EthereumHardfork::Berlin).unwrap(),
2072 ForkCondition::Block(0)
2073 );
2074 assert_eq!(
2075 chainspec.hardforks.get(EthereumHardfork::London).unwrap(),
2076 ForkCondition::Block(0)
2077 );
2078 assert_eq!(
2079 chainspec.hardforks.get(EthereumHardfork::ArrowGlacier).unwrap(),
2080 ForkCondition::Block(0)
2081 );
2082 assert_eq!(
2083 chainspec.hardforks.get(EthereumHardfork::GrayGlacier).unwrap(),
2084 ForkCondition::Block(0)
2085 );
2086
2087 assert_eq!(
2089 chainspec.hardforks.get(EthereumHardfork::Shanghai).unwrap(),
2090 ForkCondition::Timestamp(0)
2091 );
2092
2093 assert_eq!(
2095 chainspec.hardforks.get(EthereumHardfork::Cancun).unwrap(),
2096 ForkCondition::Timestamp(1)
2097 );
2098
2099 let key_rlp = vec![
2101 (
2102 hex!("0x658bdf435d810c91414ec09147daa6db62406379"),
2103 &hex!(
2104 "0xf84d8089487a9a304539440000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
2105 )[..],
2106 ),
2107 (
2108 hex!("0xaa00000000000000000000000000000000000000"),
2109 &hex!(
2110 "0xf8440101a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0ce92c756baff35fa740c3557c1a971fd24d2d35b7c8e067880d50cd86bb0bc99"
2111 )[..],
2112 ),
2113 (
2114 hex!("0xbb00000000000000000000000000000000000000"),
2115 &hex!(
2116 "0xf8440102a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0e25a53cbb501cec2976b393719c63d832423dd70a458731a0b64e4847bbca7d2"
2117 )[..],
2118 ),
2119 ];
2120
2121 for (key, expected_rlp) in key_rlp {
2122 let account = chainspec.genesis.alloc.get(&key).expect("account should exist");
2123 assert_eq!(&alloy_rlp::encode(TrieAccount::from(account.clone())), expected_rlp);
2124 }
2125
2126 let expected_state_root: B256 =
2127 hex!("0x078dc6061b1d8eaa8493384b59c9c65ceb917201221d08b80c4de6770b6ec7e7").into();
2128 assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
2129
2130 assert_eq!(chainspec.genesis_header().withdrawals_root, Some(EMPTY_ROOT_HASH));
2131
2132 let expected_hash: B256 =
2133 hex!("0x1fc027d65f820d3eef441ebeec139ebe09e471cf98516dce7b5643ccb27f418c").into();
2134 let hash = chainspec.genesis_hash();
2135 assert_eq!(hash, expected_hash);
2136 }
2137
2138 #[test]
2139 fn hive_geth_json() {
2140 let hive_json = r#"
2141 {
2142 "nonce": "0x0000000000000042",
2143 "difficulty": "0x2123456",
2144 "mixHash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
2145 "coinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
2146 "timestamp": "0x123456",
2147 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2148 "extraData": "0xfafbfcfd",
2149 "gasLimit": "0x2fefd8",
2150 "alloc": {
2151 "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {
2152 "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
2153 },
2154 "e6716f9544a56c530d868e4bfbacb172315bdead": {
2155 "balance": "0x11",
2156 "code": "0x12"
2157 },
2158 "b9c015918bdaba24b4ff057a92a3873d6eb201be": {
2159 "balance": "0x21",
2160 "storage": {
2161 "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22"
2162 }
2163 },
2164 "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {
2165 "balance": "0x31",
2166 "nonce": "0x32"
2167 },
2168 "0000000000000000000000000000000000000001": {
2169 "balance": "0x41"
2170 },
2171 "0000000000000000000000000000000000000002": {
2172 "balance": "0x51"
2173 },
2174 "0000000000000000000000000000000000000003": {
2175 "balance": "0x61"
2176 },
2177 "0000000000000000000000000000000000000004": {
2178 "balance": "0x71"
2179 }
2180 },
2181 "config": {
2182 "ethash": {},
2183 "chainId": 10,
2184 "homesteadBlock": 0,
2185 "eip150Block": 0,
2186 "eip155Block": 0,
2187 "eip158Block": 0,
2188 "byzantiumBlock": 0,
2189 "constantinopleBlock": 0,
2190 "petersburgBlock": 0,
2191 "istanbulBlock": 0
2192 }
2193 }
2194 "#;
2195
2196 let genesis = serde_json::from_str::<Genesis>(hive_json).unwrap();
2197 let chainspec: ChainSpec = genesis.into();
2198 assert_eq!(chainspec.chain, Chain::from_named(NamedChain::Optimism));
2199 let expected_state_root: B256 =
2200 hex!("0x9a6049ac535e3dc7436c189eaa81c73f35abd7f282ab67c32944ff0301d63360").into();
2201 assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
2202 let hard_forks = vec![
2203 EthereumHardfork::Byzantium,
2204 EthereumHardfork::Homestead,
2205 EthereumHardfork::Istanbul,
2206 EthereumHardfork::Petersburg,
2207 EthereumHardfork::Constantinople,
2208 ];
2209 for fork in hard_forks {
2210 assert_eq!(chainspec.hardforks.get(fork).unwrap(), ForkCondition::Block(0));
2211 }
2212
2213 let expected_hash: B256 =
2214 hex!("0x5ae31c6522bd5856129f66be3d582b842e4e9faaa87f21cce547128339a9db3c").into();
2215 let hash = chainspec.genesis_header().hash_slow();
2216 assert_eq!(hash, expected_hash);
2217 }
2218
2219 #[test]
2220 fn test_hive_paris_block_genesis_json() {
2221 let hive_paris = r#"
2224 {
2225 "config": {
2226 "ethash": {},
2227 "chainId": 3503995874084926,
2228 "homesteadBlock": 0,
2229 "eip150Block": 6,
2230 "eip155Block": 12,
2231 "eip158Block": 12,
2232 "byzantiumBlock": 18,
2233 "constantinopleBlock": 24,
2234 "petersburgBlock": 30,
2235 "istanbulBlock": 36,
2236 "muirGlacierBlock": 42,
2237 "berlinBlock": 48,
2238 "londonBlock": 54,
2239 "arrowGlacierBlock": 60,
2240 "grayGlacierBlock": 66,
2241 "mergeNetsplitBlock": 72,
2242 "terminalTotalDifficulty": 9454784,
2243 "shanghaiTime": 780,
2244 "cancunTime": 840
2245 },
2246 "nonce": "0x0",
2247 "timestamp": "0x0",
2248 "extraData": "0x68697665636861696e",
2249 "gasLimit": "0x23f3e20",
2250 "difficulty": "0x20000",
2251 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2252 "coinbase": "0x0000000000000000000000000000000000000000",
2253 "alloc": {
2254 "000f3df6d732807ef1319fb7b8bb8522d0beac02": {
2255 "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
2256 "balance": "0x2a"
2257 },
2258 "0c2c51a0990aee1d73c1228de158688341557508": {
2259 "balance": "0xc097ce7bc90715b34b9f1000000000"
2260 },
2261 "14e46043e63d0e3cdcf2530519f4cfaf35058cb2": {
2262 "balance": "0xc097ce7bc90715b34b9f1000000000"
2263 },
2264 "16c57edf7fa9d9525378b0b81bf8a3ced0620c1c": {
2265 "balance": "0xc097ce7bc90715b34b9f1000000000"
2266 },
2267 "1f4924b14f34e24159387c0a4cdbaa32f3ddb0cf": {
2268 "balance": "0xc097ce7bc90715b34b9f1000000000"
2269 },
2270 "1f5bde34b4afc686f136c7a3cb6ec376f7357759": {
2271 "balance": "0xc097ce7bc90715b34b9f1000000000"
2272 },
2273 "2d389075be5be9f2246ad654ce152cf05990b209": {
2274 "balance": "0xc097ce7bc90715b34b9f1000000000"
2275 },
2276 "3ae75c08b4c907eb63a8960c45b86e1e9ab6123c": {
2277 "balance": "0xc097ce7bc90715b34b9f1000000000"
2278 },
2279 "4340ee1b812acb40a1eb561c019c327b243b92df": {
2280 "balance": "0xc097ce7bc90715b34b9f1000000000"
2281 },
2282 "4a0f1452281bcec5bd90c3dce6162a5995bfe9df": {
2283 "balance": "0xc097ce7bc90715b34b9f1000000000"
2284 },
2285 "4dde844b71bcdf95512fb4dc94e84fb67b512ed8": {
2286 "balance": "0xc097ce7bc90715b34b9f1000000000"
2287 },
2288 "5f552da00dfb4d3749d9e62dcee3c918855a86a0": {
2289 "balance": "0xc097ce7bc90715b34b9f1000000000"
2290 },
2291 "654aa64f5fbefb84c270ec74211b81ca8c44a72e": {
2292 "balance": "0xc097ce7bc90715b34b9f1000000000"
2293 },
2294 "717f8aa2b982bee0e29f573d31df288663e1ce16": {
2295 "balance": "0xc097ce7bc90715b34b9f1000000000"
2296 },
2297 "7435ed30a8b4aeb0877cef0c6e8cffe834eb865f": {
2298 "balance": "0xc097ce7bc90715b34b9f1000000000"
2299 },
2300 "83c7e323d189f18725ac510004fdc2941f8c4a78": {
2301 "balance": "0xc097ce7bc90715b34b9f1000000000"
2302 },
2303 "84e75c28348fb86acea1a93a39426d7d60f4cc46": {
2304 "balance": "0xc097ce7bc90715b34b9f1000000000"
2305 },
2306 "8bebc8ba651aee624937e7d897853ac30c95a067": {
2307 "storage": {
2308 "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001",
2309 "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000002",
2310 "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000003"
2311 },
2312 "balance": "0x1",
2313 "nonce": "0x1"
2314 },
2315 "c7b99a164efd027a93f147376cc7da7c67c6bbe0": {
2316 "balance": "0xc097ce7bc90715b34b9f1000000000"
2317 },
2318 "d803681e487e6ac18053afc5a6cd813c86ec3e4d": {
2319 "balance": "0xc097ce7bc90715b34b9f1000000000"
2320 },
2321 "e7d13f7aa2a838d24c59b40186a0aca1e21cffcc": {
2322 "balance": "0xc097ce7bc90715b34b9f1000000000"
2323 },
2324 "eda8645ba6948855e3b3cd596bbb07596d59c603": {
2325 "balance": "0xc097ce7bc90715b34b9f1000000000"
2326 }
2327 },
2328 "number": "0x0",
2329 "gasUsed": "0x0",
2330 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2331 "baseFeePerGas": null,
2332 "excessBlobGas": null,
2333 "blobGasUsed": null
2334 }
2335 "#;
2336
2337 let genesis: Genesis = serde_json::from_str(hive_paris).unwrap();
2339 let chainspec = ChainSpec::from(genesis);
2340
2341 let expected_forkid = ForkId { hash: ForkHash([0xbc, 0x0c, 0x26, 0x05]), next: 0 };
2343 let got_forkid =
2344 chainspec.fork_id(&Head { number: 73, timestamp: 840, ..Default::default() });
2345
2346 assert_eq!(got_forkid, expected_forkid);
2348 assert_eq!(chainspec.paris_block_and_final_difficulty, Some((72, U256::from(9454784))));
2350 }
2351
2352 #[test]
2353 fn test_parse_genesis_json() {
2354 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"}"#;
2355 let genesis: Genesis = serde_json::from_str(s).unwrap();
2356 let acc = genesis
2357 .alloc
2358 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2359 .unwrap();
2360 assert_eq!(acc.balance, U256::from(1));
2361 assert_eq!(genesis.base_fee_per_gas, Some(0x1337));
2362 }
2363
2364 #[test]
2365 fn test_parse_cancun_genesis_json() {
2366 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"}"#;
2367 let genesis: Genesis = serde_json::from_str(s).unwrap();
2368 let acc = genesis
2369 .alloc
2370 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2371 .unwrap();
2372 assert_eq!(acc.balance, U256::from(1));
2373 assert_eq!(genesis.config.cancun_time, Some(4661));
2375 }
2376
2377 #[test]
2378 fn test_parse_prague_genesis_all_formats() {
2379 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"}"#;
2380 let genesis: Genesis = serde_json::from_str(s).unwrap();
2381
2382 let acc = genesis
2384 .alloc
2385 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2386 .unwrap();
2387 assert_eq!(acc.balance, U256::from(1));
2388 assert_eq!(genesis.config.cancun_time, Some(4661));
2390 assert_eq!(genesis.config.prague_time, Some(4662));
2392 }
2393
2394 #[test]
2395 fn test_parse_cancun_genesis_all_formats() {
2396 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"}"#;
2397 let genesis: Genesis = serde_json::from_str(s).unwrap();
2398
2399 let acc = genesis
2401 .alloc
2402 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2403 .unwrap();
2404 assert_eq!(acc.balance, U256::from(1));
2405 assert_eq!(genesis.config.cancun_time, Some(4661));
2407 }
2408
2409 #[test]
2410 fn test_paris_block_and_total_difficulty() {
2411 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2412 let paris_chainspec = ChainSpecBuilder::default()
2413 .chain(Chain::from_id(1337))
2414 .genesis(genesis)
2415 .paris_activated()
2416 .build();
2417 assert_eq!(paris_chainspec.paris_block_and_final_difficulty, Some((0, U256::ZERO)));
2418 }
2419
2420 #[test]
2421 fn test_default_cancun_header_forkhash() {
2422 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2424 let default_chainspec = ChainSpecBuilder::default()
2425 .chain(Chain::from_id(1337))
2426 .genesis(genesis)
2427 .cancun_activated()
2428 .build();
2429 let mut header = default_chainspec.genesis_header().clone();
2430
2431 header.state_root =
2433 B256::from_str("0x62e2595e017f0ca23e08d17221010721a71c3ae932f4ea3cb12117786bb392d4")
2434 .unwrap();
2435
2436 assert_eq!(header.withdrawals_root, Some(EMPTY_WITHDRAWALS));
2438
2439 assert_eq!(header.parent_beacon_block_root, Some(B256::ZERO));
2442 assert_eq!(header.blob_gas_used, Some(0));
2443 assert_eq!(header.excess_blob_gas, Some(0));
2444
2445 let genesis_hash = header.hash_slow();
2447 let expected_hash =
2448 b256!("0x16bb7c59613a5bad3f7c04a852fd056545ade2483968d9a25a1abb05af0c4d37");
2449 assert_eq!(genesis_hash, expected_hash);
2450
2451 let expected_forkhash = ForkHash(hex!("8062457a"));
2453 assert_eq!(ForkHash::from(genesis_hash), expected_forkhash);
2454 }
2455
2456 #[test]
2457 fn holesky_paris_activated_at_genesis() {
2458 assert!(HOLESKY
2459 .fork(EthereumHardfork::Paris)
2460 .active_at_ttd(HOLESKY.genesis.difficulty, HOLESKY.genesis.difficulty));
2461 }
2462
2463 #[test]
2464 fn test_genesis_format_deserialization() {
2465 let config = ChainConfig {
2467 chain_id: 2600,
2468 homestead_block: Some(0),
2469 eip150_block: Some(0),
2470 eip155_block: Some(0),
2471 eip158_block: Some(0),
2472 byzantium_block: Some(0),
2473 constantinople_block: Some(0),
2474 petersburg_block: Some(0),
2475 istanbul_block: Some(0),
2476 berlin_block: Some(0),
2477 london_block: Some(0),
2478 shanghai_time: Some(0),
2479 terminal_total_difficulty: Some(U256::ZERO),
2480 terminal_total_difficulty_passed: true,
2481 ..Default::default()
2482 };
2483 let genesis = Genesis {
2485 config,
2486 nonce: 0,
2487 timestamp: 1698688670,
2488 gas_limit: 5000,
2489 difficulty: U256::ZERO,
2490 mix_hash: B256::ZERO,
2491 coinbase: Address::ZERO,
2492 ..Default::default()
2493 };
2494
2495 let address = hex!("0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b").into();
2497 let account = GenesisAccount::default().with_balance(U256::from(33));
2498 let genesis = genesis.extend_accounts(HashMap::from([(address, account)]));
2499
2500 let serialized_genesis = serde_json::to_string(&genesis).unwrap();
2502 let deserialized_genesis: Genesis = serde_json::from_str(&serialized_genesis).unwrap();
2503
2504 assert_eq!(genesis, deserialized_genesis);
2505 }
2506
2507 #[test]
2508 fn check_fork_id_chainspec_with_fork_condition_never() {
2509 let spec: ChainSpec = ChainSpec {
2510 chain: Chain::mainnet(),
2511 genesis: Genesis::default(),
2512 hardforks: ChainHardforks::new(vec![(
2513 EthereumHardfork::Frontier.boxed(),
2514 ForkCondition::Never,
2515 )]),
2516 paris_block_and_final_difficulty: None,
2517 deposit_contract: None,
2518 ..Default::default()
2519 };
2520
2521 assert_eq!(spec.hardfork_fork_id(EthereumHardfork::Frontier), None);
2522 }
2523
2524 #[test]
2525 fn check_fork_filter_chainspec_with_fork_condition_never() {
2526 let spec: ChainSpec = ChainSpec {
2527 chain: Chain::mainnet(),
2528 genesis: Genesis::default(),
2529 hardforks: ChainHardforks::new(vec![(
2530 EthereumHardfork::Shanghai.boxed(),
2531 ForkCondition::Never,
2532 )]),
2533 paris_block_and_final_difficulty: None,
2534 deposit_contract: None,
2535 ..Default::default()
2536 };
2537
2538 assert_eq!(spec.hardfork_fork_filter(EthereumHardfork::Shanghai), None);
2539 }
2540
2541 #[test]
2542 fn latest_eth_mainnet_fork_id() {
2543 assert_eq!(
2544 ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 },
2545 MAINNET.latest_fork_id()
2546 )
2547 }
2548
2549 #[test]
2550 fn latest_hoodi_mainnet_fork_id() {
2551 assert_eq!(ForkId { hash: ForkHash(hex!("0x23aa1351")), next: 0 }, HOODI.latest_fork_id())
2553 }
2554
2555 #[test]
2556 fn latest_holesky_mainnet_fork_id() {
2557 assert_eq!(ForkId { hash: ForkHash(hex!("0x9bc6cb31")), next: 0 }, HOLESKY.latest_fork_id())
2559 }
2560
2561 #[test]
2562 fn latest_sepolia_mainnet_fork_id() {
2563 assert_eq!(ForkId { hash: ForkHash(hex!("0x268956b6")), next: 0 }, SEPOLIA.latest_fork_id())
2565 }
2566
2567 #[test]
2568 fn test_fork_order_ethereum_mainnet() {
2569 let genesis = Genesis {
2570 config: ChainConfig {
2571 chain_id: 0,
2572 homestead_block: Some(0),
2573 dao_fork_block: Some(0),
2574 dao_fork_support: false,
2575 eip150_block: Some(0),
2576 eip155_block: Some(0),
2577 eip158_block: Some(0),
2578 byzantium_block: Some(0),
2579 constantinople_block: Some(0),
2580 petersburg_block: Some(0),
2581 istanbul_block: Some(0),
2582 muir_glacier_block: Some(0),
2583 berlin_block: Some(0),
2584 london_block: Some(0),
2585 arrow_glacier_block: Some(0),
2586 gray_glacier_block: Some(0),
2587 merge_netsplit_block: Some(0),
2588 shanghai_time: Some(0),
2589 cancun_time: Some(0),
2590 terminal_total_difficulty: Some(U256::ZERO),
2591 ..Default::default()
2592 },
2593 ..Default::default()
2594 };
2595
2596 let chain_spec: ChainSpec = genesis.into();
2597
2598 let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect();
2599 let expected_hardforks = vec![
2600 EthereumHardfork::Frontier.boxed(),
2601 EthereumHardfork::Homestead.boxed(),
2602 EthereumHardfork::Dao.boxed(),
2603 EthereumHardfork::Tangerine.boxed(),
2604 EthereumHardfork::SpuriousDragon.boxed(),
2605 EthereumHardfork::Byzantium.boxed(),
2606 EthereumHardfork::Constantinople.boxed(),
2607 EthereumHardfork::Petersburg.boxed(),
2608 EthereumHardfork::Istanbul.boxed(),
2609 EthereumHardfork::MuirGlacier.boxed(),
2610 EthereumHardfork::Berlin.boxed(),
2611 EthereumHardfork::London.boxed(),
2612 EthereumHardfork::ArrowGlacier.boxed(),
2613 EthereumHardfork::GrayGlacier.boxed(),
2614 EthereumHardfork::Paris.boxed(),
2615 EthereumHardfork::Shanghai.boxed(),
2616 EthereumHardfork::Cancun.boxed(),
2617 ];
2618
2619 assert!(expected_hardforks
2620 .iter()
2621 .zip(hardforks.iter())
2622 .all(|(expected, actual)| &**expected == *actual));
2623 assert_eq!(expected_hardforks.len(), hardforks.len());
2624 }
2625
2626 #[test]
2627 fn test_calc_base_block_reward() {
2628 let cases = [
2630 ((0, U256::ZERO), Some(ETH_TO_WEI * 5)),
2632 ((4370000, U256::ZERO), Some(ETH_TO_WEI * 3)),
2634 ((7280000, U256::ZERO), Some(ETH_TO_WEI * 2)),
2636 ((15537394, U256::from(58_750_000_000_000_000_000_000_u128)), None),
2638 ];
2639
2640 for ((block_number, _td), expected_reward) in cases {
2641 assert_eq!(base_block_reward(&*MAINNET, block_number), expected_reward);
2642 }
2643 }
2644
2645 #[test]
2646 fn test_calc_full_block_reward() {
2647 let base_reward = ETH_TO_WEI;
2648 let one_thirty_twoth_reward = base_reward >> 5;
2649
2650 let cases = [
2652 (0, base_reward),
2653 (1, base_reward + one_thirty_twoth_reward),
2654 (2, base_reward + one_thirty_twoth_reward * 2),
2655 ];
2656
2657 for (num_ommers, expected_reward) in cases {
2658 assert_eq!(block_reward(base_reward, num_ommers), expected_reward);
2659 }
2660 }
2661
2662 #[test]
2663 fn blob_params_from_genesis() {
2664 let s = r#"{
2665 "blobSchedule": {
2666 "cancun":{
2667 "baseFeeUpdateFraction":3338477,
2668 "max":6,
2669 "target":3
2670 },
2671 "prague":{
2672 "baseFeeUpdateFraction":3338477,
2673 "max":6,
2674 "target":3
2675 }
2676 }
2677 }"#;
2678 let config: ChainConfig = serde_json::from_str(s).unwrap();
2679 let hardfork_params = config.blob_schedule_blob_params();
2680 let expected = BlobScheduleBlobParams {
2681 cancun: BlobParams {
2682 target_blob_count: 3,
2683 max_blob_count: 6,
2684 update_fraction: 3338477,
2685 min_blob_fee: BLOB_TX_MIN_BLOB_GASPRICE,
2686 max_blobs_per_tx: 6,
2687 blob_base_cost: 0,
2688 },
2689 prague: BlobParams {
2690 target_blob_count: 3,
2691 max_blob_count: 6,
2692 update_fraction: 3338477,
2693 min_blob_fee: BLOB_TX_MIN_BLOB_GASPRICE,
2694 max_blobs_per_tx: 6,
2695 blob_base_cost: 0,
2696 },
2697 ..Default::default()
2698 };
2699 assert_eq!(hardfork_params, expected);
2700 }
2701
2702 #[test]
2703 fn parse_perf_net_genesis() {
2704 let s = r#"{
2705 "config": {
2706 "chainId": 1,
2707 "homesteadBlock": 1150000,
2708 "daoForkBlock": 1920000,
2709 "daoForkSupport": true,
2710 "eip150Block": 2463000,
2711 "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
2712 "eip155Block": 2675000,
2713 "eip158Block": 2675000,
2714 "byzantiumBlock": 4370000,
2715 "constantinopleBlock": 7280000,
2716 "petersburgBlock": 7280000,
2717 "istanbulBlock": 9069000,
2718 "muirGlacierBlock": 9200000,
2719 "berlinBlock": 12244000,
2720 "londonBlock": 12965000,
2721 "arrowGlacierBlock": 13773000,
2722 "grayGlacierBlock": 15050000,
2723 "terminalTotalDifficulty": 58750000000000000000000,
2724 "terminalTotalDifficultyPassed": true,
2725 "shanghaiTime": 1681338455,
2726 "cancunTime": 1710338135,
2727 "pragueTime": 1746612311,
2728 "ethash": {},
2729 "depositContractAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa",
2730 "blobSchedule": {
2731 "cancun": {
2732 "target": 3,
2733 "max": 6,
2734 "baseFeeUpdateFraction": 3338477
2735 },
2736 "prague": {
2737 "target": 6,
2738 "max": 9,
2739 "baseFeeUpdateFraction": 5007716
2740 }
2741 }
2742 },
2743 "nonce": "0x42",
2744 "timestamp": "0x0",
2745 "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
2746 "gasLimit": "0x1388",
2747 "difficulty": "0x400000000",
2748 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2749 "coinbase": "0x0000000000000000000000000000000000000000",
2750 "number": "0x0",
2751 "gasUsed": "0x0",
2752 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2753 "baseFeePerGas": null
2754}"#;
2755
2756 let genesis = serde_json::from_str::<Genesis>(s).unwrap();
2757 let chainspec = ChainSpec::from_genesis(genesis);
2758 let activation = chainspec.hardforks.fork(EthereumHardfork::Paris);
2759 assert_eq!(
2760 activation,
2761 ForkCondition::TTD {
2762 activation_block_number: MAINNET_PARIS_BLOCK,
2763 total_difficulty: MAINNET_PARIS_TTD,
2764 fork_block: None,
2765 }
2766 )
2767 }
2768}