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