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