1pub use alloy_eips::eip1559::BaseFeeParams;
2use alloy_evm::eth::spec::EthExecutorSpec;
3
4use crate::{
5 constants::{MAINNET_DEPOSIT_CONTRACT, MAINNET_PRUNE_DELETE_LIMIT},
6 ethereum::SEPOLIA_PARIS_TTD,
7 holesky, hoodi, mainnet,
8 mainnet::{MAINNET_PARIS_BLOCK, MAINNET_PARIS_TTD},
9 sepolia,
10 sepolia::SEPOLIA_PARIS_BLOCK,
11 EthChainSpec,
12};
13use alloc::{
14 boxed::Box,
15 collections::BTreeMap,
16 format,
17 string::{String, ToString},
18 sync::Arc,
19 vec::Vec,
20};
21use alloy_chains::{Chain, NamedChain};
22use alloy_consensus::{
23 constants::{
24 EMPTY_WITHDRAWALS, HOLESKY_GENESIS_HASH, HOODI_GENESIS_HASH, MAINNET_GENESIS_HASH,
25 SEPOLIA_GENESIS_HASH,
26 },
27 Header,
28};
29use alloy_eips::{
30 eip1559::INITIAL_BASE_FEE, eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams,
31 eip7892::BlobScheduleBlobParams,
32};
33use alloy_genesis::{ChainConfig, Genesis};
34use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256};
35use alloy_trie::root::state_root_ref_unhashed;
36use core::fmt::Debug;
37use derive_more::From;
38use reth_ethereum_forks::{
39 ChainHardforks, DisplayHardforks, EthereumHardfork, EthereumHardforks, ForkCondition,
40 ForkFilter, ForkFilterKey, ForkHash, ForkId, Hardfork, Hardforks, Head, DEV_HARDFORKS,
41};
42use reth_network_peers::{
43 holesky_nodes, hoodi_nodes, mainnet_nodes, op_nodes, op_testnet_nodes, sepolia_nodes,
44 NodeRecord,
45};
46use reth_primitives_traits::{sync::LazyLock, BlockHeader, SealedHeader};
47
48pub fn make_genesis_header(genesis: &Genesis, hardforks: &ChainHardforks) -> Header {
50 let base_fee_per_gas = hardforks
52 .fork(EthereumHardfork::London)
53 .active_at_block(0)
54 .then(|| genesis.base_fee_per_gas.map(|fee| fee as u64).unwrap_or(INITIAL_BASE_FEE));
55
56 let withdrawals_root = hardforks
59 .fork(EthereumHardfork::Shanghai)
60 .active_at_timestamp(genesis.timestamp)
61 .then_some(EMPTY_WITHDRAWALS);
62
63 let (parent_beacon_block_root, blob_gas_used, excess_blob_gas) =
68 if hardforks.fork(EthereumHardfork::Cancun).active_at_timestamp(genesis.timestamp) {
69 let blob_gas_used = genesis.blob_gas_used.unwrap_or(0);
70 let excess_blob_gas = genesis.excess_blob_gas.unwrap_or(0);
71 (Some(B256::ZERO), Some(blob_gas_used), Some(excess_blob_gas))
72 } else {
73 (None, None, None)
74 };
75
76 let requests_hash = hardforks
78 .fork(EthereumHardfork::Prague)
79 .active_at_timestamp(genesis.timestamp)
80 .then_some(EMPTY_REQUESTS_HASH);
81
82 Header {
83 gas_limit: genesis.gas_limit,
84 difficulty: genesis.difficulty,
85 nonce: genesis.nonce.into(),
86 extra_data: genesis.extra_data.clone(),
87 state_root: state_root_ref_unhashed(&genesis.alloc),
88 timestamp: genesis.timestamp,
89 mix_hash: genesis.mix_hash,
90 beneficiary: genesis.coinbase,
91 base_fee_per_gas,
92 withdrawals_root,
93 parent_beacon_block_root,
94 blob_gas_used,
95 excess_blob_gas,
96 requests_hash,
97 ..Default::default()
98 }
99}
100
101pub static MAINNET: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
103 let genesis = serde_json::from_str(include_str!("../res/genesis/mainnet.json"))
104 .expect("Can't deserialize Mainnet genesis json");
105 let hardforks = EthereumHardfork::mainnet().into();
106 let mut spec = ChainSpec {
107 chain: Chain::mainnet(),
108 genesis_header: SealedHeader::new(
109 make_genesis_header(&genesis, &hardforks),
110 MAINNET_GENESIS_HASH,
111 ),
112 genesis,
113 paris_block_and_final_difficulty: Some((
115 MAINNET_PARIS_BLOCK,
116 U256::from(58_750_003_716_598_352_816_469u128),
117 )),
118 hardforks,
119 deposit_contract: Some(MAINNET_DEPOSIT_CONTRACT),
121 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
122 prune_delete_limit: MAINNET_PRUNE_DELETE_LIMIT,
123 blob_params: BlobScheduleBlobParams::default().with_scheduled([
124 (mainnet::MAINNET_BPO1_TIMESTAMP, BlobParams::bpo1()),
125 (mainnet::MAINNET_BPO2_TIMESTAMP, BlobParams::bpo2()),
126 ]),
127 };
128 spec.genesis.config.dao_fork_support = true;
129 spec.into()
130});
131
132pub static SEPOLIA: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
134 let genesis = serde_json::from_str(include_str!("../res/genesis/sepolia.json"))
135 .expect("Can't deserialize Sepolia genesis json");
136 let hardforks = EthereumHardfork::sepolia().into();
137 let mut spec = ChainSpec {
138 chain: Chain::sepolia(),
139 genesis_header: SealedHeader::new(
140 make_genesis_header(&genesis, &hardforks),
141 SEPOLIA_GENESIS_HASH,
142 ),
143 genesis,
144 paris_block_and_final_difficulty: Some((
146 SEPOLIA_PARIS_BLOCK,
147 U256::from(17_000_018_015_853_232u128),
148 )),
149 hardforks,
150 deposit_contract: Some(DepositContract::new(
152 address!("0x7f02c3e3c98b133055b8b348b2ac625669ed295d"),
153 1273020,
154 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
155 )),
156 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
157 prune_delete_limit: 10000,
158 blob_params: BlobScheduleBlobParams::default().with_scheduled([
159 (sepolia::SEPOLIA_BPO1_TIMESTAMP, BlobParams::bpo1()),
160 (sepolia::SEPOLIA_BPO2_TIMESTAMP, BlobParams::bpo2()),
161 ]),
162 };
163 spec.genesis.config.dao_fork_support = true;
164 spec.into()
165});
166
167pub static HOLESKY: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
169 let genesis = serde_json::from_str(include_str!("../res/genesis/holesky.json"))
170 .expect("Can't deserialize Holesky genesis json");
171 let hardforks = EthereumHardfork::holesky().into();
172 let mut spec = ChainSpec {
173 chain: Chain::holesky(),
174 genesis_header: SealedHeader::new(
175 make_genesis_header(&genesis, &hardforks),
176 HOLESKY_GENESIS_HASH,
177 ),
178 genesis,
179 paris_block_and_final_difficulty: Some((0, U256::from(1))),
180 hardforks,
181 deposit_contract: Some(DepositContract::new(
182 address!("0x4242424242424242424242424242424242424242"),
183 0,
184 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
185 )),
186 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
187 prune_delete_limit: 10000,
188 blob_params: BlobScheduleBlobParams::default().with_scheduled([
189 (holesky::HOLESKY_BPO1_TIMESTAMP, BlobParams::bpo1()),
190 (holesky::HOLESKY_BPO2_TIMESTAMP, BlobParams::bpo2()),
191 ]),
192 };
193 spec.genesis.config.dao_fork_support = true;
194 spec.into()
195});
196
197pub static HOODI: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
201 let genesis = serde_json::from_str(include_str!("../res/genesis/hoodi.json"))
202 .expect("Can't deserialize Hoodi genesis json");
203 let hardforks = EthereumHardfork::hoodi().into();
204 let mut spec = ChainSpec {
205 chain: Chain::hoodi(),
206 genesis_header: SealedHeader::new(
207 make_genesis_header(&genesis, &hardforks),
208 HOODI_GENESIS_HASH,
209 ),
210 genesis,
211 paris_block_and_final_difficulty: Some((0, U256::from(0))),
212 hardforks,
213 deposit_contract: Some(DepositContract::new(
214 address!("0x00000000219ab540356cBB839Cbe05303d7705Fa"),
215 0,
216 b256!("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
217 )),
218 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
219 prune_delete_limit: 10000,
220 blob_params: BlobScheduleBlobParams::default().with_scheduled([
221 (hoodi::HOODI_BPO1_TIMESTAMP, BlobParams::bpo1()),
222 (hoodi::HOODI_BPO2_TIMESTAMP, BlobParams::bpo2()),
223 ]),
224 };
225 spec.genesis.config.dao_fork_support = true;
226 spec.into()
227});
228
229pub static DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
234 let genesis = serde_json::from_str(include_str!("../res/genesis/dev.json"))
235 .expect("Can't deserialize Dev testnet genesis json");
236 let hardforks = DEV_HARDFORKS.clone();
237 ChainSpec {
238 chain: Chain::dev(),
239 genesis_header: SealedHeader::seal_slow(make_genesis_header(&genesis, &hardforks)),
240 genesis,
241 paris_block_and_final_difficulty: Some((0, U256::from(0))),
242 hardforks,
243 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
244 deposit_contract: None, ..Default::default()
246 }
247 .into()
248});
249
250pub fn create_chain_config(
253 chain: Option<Chain>,
254 hardforks: &ChainHardforks,
255 deposit_contract_address: Option<Address>,
256 blob_schedule: BTreeMap<String, BlobParams>,
257) -> ChainConfig {
258 let block_num = |fork: EthereumHardfork| hardforks.fork(fork).block_number();
260
261 let timestamp = |fork: EthereumHardfork| -> Option<u64> {
263 match hardforks.fork(fork) {
264 ForkCondition::Timestamp(t) => Some(t),
265 _ => None,
266 }
267 };
268
269 let (terminal_total_difficulty, terminal_total_difficulty_passed) =
271 match hardforks.fork(EthereumHardfork::Paris) {
272 ForkCondition::TTD { total_difficulty, .. } => (Some(total_difficulty), true),
273 _ => (None, false),
274 };
275
276 let dao_fork_support = hardforks.fork(EthereumHardfork::Dao) != ForkCondition::Never;
278
279 ChainConfig {
280 chain_id: chain.map(|c| c.id()).unwrap_or(0),
281 homestead_block: block_num(EthereumHardfork::Homestead),
282 dao_fork_block: block_num(EthereumHardfork::Dao),
283 dao_fork_support,
284 eip150_block: block_num(EthereumHardfork::Tangerine),
285 eip155_block: block_num(EthereumHardfork::SpuriousDragon),
286 eip158_block: block_num(EthereumHardfork::SpuriousDragon),
287 byzantium_block: block_num(EthereumHardfork::Byzantium),
288 constantinople_block: block_num(EthereumHardfork::Constantinople),
289 petersburg_block: block_num(EthereumHardfork::Petersburg),
290 istanbul_block: block_num(EthereumHardfork::Istanbul),
291 muir_glacier_block: block_num(EthereumHardfork::MuirGlacier),
292 berlin_block: block_num(EthereumHardfork::Berlin),
293 london_block: block_num(EthereumHardfork::London),
294 arrow_glacier_block: block_num(EthereumHardfork::ArrowGlacier),
295 gray_glacier_block: block_num(EthereumHardfork::GrayGlacier),
296 merge_netsplit_block: None,
297 shanghai_time: timestamp(EthereumHardfork::Shanghai),
298 cancun_time: timestamp(EthereumHardfork::Cancun),
299 prague_time: timestamp(EthereumHardfork::Prague),
300 osaka_time: timestamp(EthereumHardfork::Osaka),
301 bpo1_time: timestamp(EthereumHardfork::Bpo1),
302 bpo2_time: timestamp(EthereumHardfork::Bpo2),
303 bpo3_time: timestamp(EthereumHardfork::Bpo3),
304 bpo4_time: timestamp(EthereumHardfork::Bpo4),
305 bpo5_time: timestamp(EthereumHardfork::Bpo5),
306 terminal_total_difficulty,
307 terminal_total_difficulty_passed,
308 ethash: None,
309 clique: None,
310 parlia: None,
311 extra_fields: Default::default(),
312 deposit_contract_address,
313 blob_schedule,
314 }
315}
316
317pub fn mainnet_chain_config() -> ChainConfig {
319 let hardforks: ChainHardforks = EthereumHardfork::mainnet().into();
320 let blob_schedule = blob_params_to_schedule(&MAINNET.blob_params, &hardforks);
321 create_chain_config(
322 Some(Chain::mainnet()),
323 &hardforks,
324 Some(MAINNET_DEPOSIT_CONTRACT.address),
325 blob_schedule,
326 )
327}
328
329pub fn blob_params_to_schedule(
331 params: &BlobScheduleBlobParams,
332 hardforks: &ChainHardforks,
333) -> BTreeMap<String, BlobParams> {
334 let mut schedule = BTreeMap::new();
335 schedule.insert("cancun".to_string(), params.cancun);
336 schedule.insert("prague".to_string(), params.prague);
337 schedule.insert("osaka".to_string(), params.osaka);
338
339 let bpo_forks = EthereumHardfork::bpo_variants();
341 for (timestamp, blob_params) in ¶ms.scheduled {
342 for bpo_fork in bpo_forks {
343 if let ForkCondition::Timestamp(fork_ts) = hardforks.fork(bpo_fork) &&
344 fork_ts == *timestamp
345 {
346 schedule.insert(bpo_fork.name().to_lowercase(), *blob_params);
347 break;
348 }
349 }
350 }
351
352 schedule
353}
354
355#[derive(Clone, Debug, PartialEq, Eq)]
358pub enum BaseFeeParamsKind {
359 Constant(BaseFeeParams),
361 Variable(ForkBaseFeeParams),
364}
365
366impl Default for BaseFeeParamsKind {
367 fn default() -> Self {
368 BaseFeeParams::ethereum().into()
369 }
370}
371
372impl From<BaseFeeParams> for BaseFeeParamsKind {
373 fn from(params: BaseFeeParams) -> Self {
374 Self::Constant(params)
375 }
376}
377
378impl From<ForkBaseFeeParams> for BaseFeeParamsKind {
379 fn from(params: ForkBaseFeeParams) -> Self {
380 Self::Variable(params)
381 }
382}
383
384#[derive(Clone, Debug, PartialEq, Eq, From)]
387pub struct ForkBaseFeeParams(Vec<(Box<dyn Hardfork>, BaseFeeParams)>);
388
389impl<H: BlockHeader> core::ops::Deref for ChainSpec<H> {
390 type Target = ChainHardforks;
391
392 fn deref(&self) -> &Self::Target {
393 &self.hardforks
394 }
395}
396
397#[derive(Debug, Clone, PartialEq, Eq)]
405pub struct ChainSpec<H: BlockHeader = Header> {
406 pub chain: Chain,
408
409 pub genesis: Genesis,
411
412 pub genesis_header: SealedHeader<H>,
414
415 pub paris_block_and_final_difficulty: Option<(u64, U256)>,
418
419 pub hardforks: ChainHardforks,
421
422 pub deposit_contract: Option<DepositContract>,
424
425 pub base_fee_params: BaseFeeParamsKind,
427
428 pub prune_delete_limit: usize,
430
431 pub blob_params: BlobScheduleBlobParams,
433}
434
435impl<H: BlockHeader> Default for ChainSpec<H> {
436 fn default() -> Self {
437 Self {
438 chain: Default::default(),
439 genesis: Default::default(),
440 genesis_header: Default::default(),
441 paris_block_and_final_difficulty: Default::default(),
442 hardforks: Default::default(),
443 deposit_contract: Default::default(),
444 base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()),
445 prune_delete_limit: MAINNET_PRUNE_DELETE_LIMIT,
446 blob_params: Default::default(),
447 }
448 }
449}
450
451impl ChainSpec {
452 pub fn from_genesis(genesis: Genesis) -> Self {
454 genesis.into()
455 }
456
457 pub fn builder() -> ChainSpecBuilder {
459 ChainSpecBuilder::default()
460 }
461}
462
463impl<H: BlockHeader> ChainSpec<H> {
464 pub const fn chain(&self) -> Chain {
466 self.chain
467 }
468
469 #[inline]
471 pub const fn is_ethereum(&self) -> bool {
472 self.chain.is_ethereum()
473 }
474
475 #[inline]
477 pub fn is_optimism_mainnet(&self) -> bool {
478 self.chain == Chain::optimism_mainnet()
479 }
480
481 #[inline]
483 pub fn paris_block(&self) -> Option<u64> {
484 self.paris_block_and_final_difficulty.map(|(block, _)| block)
485 }
486
487 pub const fn genesis(&self) -> &Genesis {
491 &self.genesis
492 }
493
494 pub fn genesis_header(&self) -> &H {
496 &self.genesis_header
497 }
498
499 pub fn sealed_genesis_header(&self) -> SealedHeader<H> {
501 SealedHeader::new(self.genesis_header().clone(), self.genesis_hash())
502 }
503
504 pub fn initial_base_fee(&self) -> Option<u64> {
506 let genesis_base_fee =
508 self.genesis.base_fee_per_gas.map(|fee| fee as u64).unwrap_or(INITIAL_BASE_FEE);
509
510 self.hardforks.fork(EthereumHardfork::London).active_at_block(0).then_some(genesis_base_fee)
512 }
513
514 pub fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams {
516 match self.base_fee_params {
517 BaseFeeParamsKind::Constant(bf_params) => bf_params,
518 BaseFeeParamsKind::Variable(ForkBaseFeeParams(ref bf_params)) => {
519 for (fork, params) in bf_params.iter().rev() {
523 if self.hardforks.is_fork_active_at_timestamp(fork.clone(), timestamp) {
524 return *params
525 }
526 }
527
528 bf_params.first().map(|(_, params)| *params).unwrap_or(BaseFeeParams::ethereum())
529 }
530 }
531 }
532
533 pub fn genesis_hash(&self) -> B256 {
535 self.genesis_header.hash()
536 }
537
538 pub const fn genesis_timestamp(&self) -> u64 {
540 self.genesis.timestamp
541 }
542
543 pub fn get_final_paris_total_difficulty(&self) -> Option<U256> {
545 self.paris_block_and_final_difficulty.map(|(_, final_difficulty)| final_difficulty)
546 }
547
548 pub fn hardfork_fork_filter<HF: Hardfork + Clone>(&self, fork: HF) -> Option<ForkFilter> {
550 match self.hardforks.fork(fork.clone()) {
551 ForkCondition::Never => None,
552 _ => Some(self.fork_filter(self.satisfy(self.hardforks.fork(fork)))),
553 }
554 }
555
556 pub fn display_hardforks(&self) -> DisplayHardforks {
558 let hardforks_with_meta = self.hardforks.forks_iter().map(|(fork, condition)| {
560 let metadata = match condition {
562 ForkCondition::Timestamp(timestamp) => {
563 EthChainSpec::blob_params_at_timestamp(self, timestamp).map(|params| {
566 format!(
567 "blob: (target: {}, max: {}, fraction: {})",
568 params.target_blob_count, params.max_blob_count, params.update_fraction
569 )
570 })
571 }
572 _ => None,
573 };
574 (fork, condition, metadata)
575 });
576
577 DisplayHardforks::with_meta(hardforks_with_meta)
578 }
579
580 #[inline]
582 pub fn hardfork_fork_id<HF: Hardfork + Clone>(&self, fork: HF) -> Option<ForkId> {
583 let condition = self.hardforks.fork(fork);
584 match condition {
585 ForkCondition::Never => None,
586 _ => Some(self.fork_id(&self.satisfy(condition))),
587 }
588 }
589
590 #[inline]
593 pub fn shanghai_fork_id(&self) -> Option<ForkId> {
594 self.hardfork_fork_id(EthereumHardfork::Shanghai)
595 }
596
597 #[inline]
600 pub fn cancun_fork_id(&self) -> Option<ForkId> {
601 self.hardfork_fork_id(EthereumHardfork::Cancun)
602 }
603
604 #[inline]
607 pub fn latest_fork_id(&self) -> ForkId {
608 self.hardfork_fork_id(self.hardforks.last().unwrap().0).unwrap()
609 }
610
611 pub fn fork_filter(&self, head: Head) -> ForkFilter {
613 let forks = self.hardforks.forks_iter().filter_map(|(_, condition)| {
614 Some(match condition {
617 ForkCondition::Block(block) |
618 ForkCondition::TTD { fork_block: Some(block), .. } => ForkFilterKey::Block(block),
619 ForkCondition::Timestamp(time) => ForkFilterKey::Time(time),
620 _ => return None,
621 })
622 });
623
624 ForkFilter::new(head, self.genesis_hash(), self.genesis_timestamp(), forks)
625 }
626
627 pub fn fork_id(&self, head: &Head) -> ForkId {
639 let mut forkhash = ForkHash::from(self.genesis_hash());
640
641 let mut current_applied = 0;
647
648 for (_, cond) in self.hardforks.forks_iter() {
650 if let ForkCondition::Block(block) |
653 ForkCondition::TTD { fork_block: Some(block), .. } = cond
654 {
655 if head.number >= block {
656 if block != current_applied {
658 forkhash += block;
659 current_applied = block;
660 }
661 } else {
662 return ForkId { hash: forkhash, next: block }
665 }
666 }
667 }
668
669 for timestamp in self.hardforks.forks_iter().filter_map(|(_, cond)| {
673 cond.as_timestamp().filter(|time| time > &self.genesis.timestamp)
675 }) {
676 if head.timestamp >= timestamp {
677 if timestamp != current_applied {
679 forkhash += timestamp;
680 current_applied = timestamp;
681 }
682 } else {
683 return ForkId { hash: forkhash, next: timestamp }
687 }
688 }
689
690 ForkId { hash: forkhash, next: 0 }
691 }
692
693 pub(crate) fn satisfy(&self, cond: ForkCondition) -> Head {
699 match cond {
700 ForkCondition::Block(number) => Head { number, ..Default::default() },
701 ForkCondition::Timestamp(timestamp) => {
702 Head {
705 timestamp,
706 number: self.last_block_fork_before_merge_or_timestamp().unwrap_or_default(),
707 ..Default::default()
708 }
709 }
710 ForkCondition::TTD { total_difficulty, fork_block, .. } => Head {
711 total_difficulty,
712 number: fork_block.unwrap_or_default(),
713 ..Default::default()
714 },
715 ForkCondition::Never => unreachable!(),
716 }
717 }
718
719 pub(crate) fn last_block_fork_before_merge_or_timestamp(&self) -> Option<u64> {
731 let mut hardforks_iter = self.hardforks.forks_iter().peekable();
732 while let Some((_, curr_cond)) = hardforks_iter.next() {
733 if let Some((_, next_cond)) = hardforks_iter.peek() {
734 match next_cond {
738 ForkCondition::TTD { fork_block: Some(block), .. } => return Some(*block),
741
742 ForkCondition::TTD { .. } | ForkCondition::Timestamp(_) => {
745 if let ForkCondition::Block(block_num) = curr_cond {
748 return Some(block_num);
749 }
750 }
751 ForkCondition::Block(_) | ForkCondition::Never => {}
752 }
753 }
754 }
755 None
756 }
757
758 pub fn bootnodes(&self) -> Option<Vec<NodeRecord>> {
760 use NamedChain as C;
761
762 match self.chain.try_into().ok()? {
763 C::Mainnet => Some(mainnet_nodes()),
764 C::Sepolia => Some(sepolia_nodes()),
765 C::Holesky => Some(holesky_nodes()),
766 C::Hoodi => Some(hoodi_nodes()),
767 C::Base | C::Optimism | C::Unichain | C::World => Some(op_nodes()),
769 C::OptimismSepolia | C::BaseSepolia | C::UnichainSepolia | C::WorldSepolia => {
770 Some(op_testnet_nodes())
771 }
772
773 chain if chain.is_optimism() && chain.is_testnet() => Some(op_testnet_nodes()),
775 chain if chain.is_optimism() => Some(op_nodes()),
776 _ => None,
777 }
778 }
779
780 pub fn map_header<NewH: BlockHeader>(self, f: impl FnOnce(H) -> NewH) -> ChainSpec<NewH> {
782 let Self {
783 chain,
784 genesis,
785 genesis_header,
786 paris_block_and_final_difficulty,
787 hardforks,
788 deposit_contract,
789 base_fee_params,
790 prune_delete_limit,
791 blob_params,
792 } = self;
793 ChainSpec {
794 chain,
795 genesis,
796 genesis_header: SealedHeader::new_unhashed(f(genesis_header.into_header())),
797 paris_block_and_final_difficulty,
798 hardforks,
799 deposit_contract,
800 base_fee_params,
801 prune_delete_limit,
802 blob_params,
803 }
804 }
805}
806
807impl From<Genesis> for ChainSpec {
808 fn from(genesis: Genesis) -> Self {
809 let hardfork_opts = [
811 (EthereumHardfork::Frontier.boxed(), Some(0)),
812 (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block),
813 (EthereumHardfork::Dao.boxed(), genesis.config.dao_fork_block),
814 (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block),
815 (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block),
816 (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block),
817 (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block),
818 (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block),
819 (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block),
820 (EthereumHardfork::MuirGlacier.boxed(), genesis.config.muir_glacier_block),
821 (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block),
822 (EthereumHardfork::London.boxed(), genesis.config.london_block),
823 (EthereumHardfork::ArrowGlacier.boxed(), genesis.config.arrow_glacier_block),
824 (EthereumHardfork::GrayGlacier.boxed(), genesis.config.gray_glacier_block),
825 ];
826 let mut hardforks = hardfork_opts
827 .into_iter()
828 .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block))))
829 .collect::<Vec<_>>();
830
831 let paris_block_and_final_difficulty = if let Some(ttd) =
835 genesis.config.terminal_total_difficulty
836 {
837 hardforks.push((
838 EthereumHardfork::Paris.boxed(),
839 ForkCondition::TTD {
840 activation_block_number: genesis
843 .config
844 .merge_netsplit_block
845 .or_else(|| {
846 match genesis.config.chain_id {
854 1 => {
855 if ttd == MAINNET_PARIS_TTD {
856 return Some(MAINNET_PARIS_BLOCK)
857 }
858 }
859 11155111 => {
860 if ttd == SEPOLIA_PARIS_TTD {
861 return Some(SEPOLIA_PARIS_BLOCK)
862 }
863 }
864 _ => {}
865 };
866 None
867 })
868 .unwrap_or_default(),
869 total_difficulty: ttd,
870 fork_block: genesis.config.merge_netsplit_block,
871 },
872 ));
873
874 genesis.config.merge_netsplit_block.map(|block| (block, ttd))
875 } else {
876 None
877 };
878
879 let time_hardfork_opts = [
881 (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time),
882 (EthereumHardfork::Cancun.boxed(), genesis.config.cancun_time),
883 (EthereumHardfork::Prague.boxed(), genesis.config.prague_time),
884 (EthereumHardfork::Osaka.boxed(), genesis.config.osaka_time),
885 (EthereumHardfork::Bpo1.boxed(), genesis.config.bpo1_time),
886 (EthereumHardfork::Bpo2.boxed(), genesis.config.bpo2_time),
887 (EthereumHardfork::Bpo3.boxed(), genesis.config.bpo3_time),
888 (EthereumHardfork::Bpo4.boxed(), genesis.config.bpo4_time),
889 (EthereumHardfork::Bpo5.boxed(), genesis.config.bpo5_time),
890 ];
891
892 let mut time_hardforks = time_hardfork_opts
893 .into_iter()
894 .filter_map(|(hardfork, opt)| {
895 opt.map(|time| (hardfork, ForkCondition::Timestamp(time)))
896 })
897 .collect::<Vec<_>>();
898
899 hardforks.append(&mut time_hardforks);
900
901 let mainnet_hardforks: ChainHardforks = EthereumHardfork::mainnet().into();
903 let mainnet_order = mainnet_hardforks.forks_iter();
904
905 let mut ordered_hardforks = Vec::with_capacity(hardforks.len());
906 for (hardfork, _) in mainnet_order {
907 if let Some(pos) = hardforks.iter().position(|(e, _)| **e == *hardfork) {
908 ordered_hardforks.push(hardforks.remove(pos));
909 }
910 }
911
912 ordered_hardforks.append(&mut hardforks);
914
915 let blob_params = genesis.config.blob_schedule_blob_params();
917
918 let deposit_contract = genesis.config.deposit_contract_address.map(|address| {
923 DepositContract { address, block: 0, topic: MAINNET_DEPOSIT_CONTRACT.topic }
924 });
925
926 let hardforks = ChainHardforks::new(ordered_hardforks);
927
928 Self {
929 chain: genesis.config.chain_id.into(),
930 genesis_header: SealedHeader::new_unhashed(make_genesis_header(&genesis, &hardforks)),
931 genesis,
932 hardforks,
933 paris_block_and_final_difficulty,
934 deposit_contract,
935 blob_params,
936 ..Default::default()
937 }
938 }
939}
940
941impl<H: BlockHeader> Hardforks for ChainSpec<H> {
942 fn fork<HF: Hardfork>(&self, fork: HF) -> ForkCondition {
943 self.hardforks.fork(fork)
944 }
945
946 fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
947 self.hardforks.forks_iter()
948 }
949
950 fn fork_id(&self, head: &Head) -> ForkId {
951 self.fork_id(head)
952 }
953
954 fn latest_fork_id(&self) -> ForkId {
955 self.latest_fork_id()
956 }
957
958 fn fork_filter(&self, head: Head) -> ForkFilter {
959 self.fork_filter(head)
960 }
961}
962
963impl<H: BlockHeader> EthereumHardforks for ChainSpec<H> {
964 fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition {
965 self.fork(fork)
966 }
967}
968
969#[auto_impl::auto_impl(&, Arc)]
971pub trait ChainSpecProvider: Debug + Send + Sync {
972 type ChainSpec: EthChainSpec + 'static;
974
975 fn chain_spec(&self) -> Arc<Self::ChainSpec>;
977}
978
979#[derive(Debug, Default, Clone)]
981pub struct ChainSpecBuilder {
982 chain: Option<Chain>,
983 genesis: Option<Genesis>,
984 hardforks: ChainHardforks,
985}
986
987impl ChainSpecBuilder {
988 pub fn mainnet() -> Self {
990 Self {
991 chain: Some(MAINNET.chain),
992 genesis: Some(MAINNET.genesis.clone()),
993 hardforks: MAINNET.hardforks.clone(),
994 }
995 }
996}
997
998impl ChainSpecBuilder {
999 pub const fn chain(mut self, chain: Chain) -> Self {
1001 self.chain = Some(chain);
1002 self
1003 }
1004
1005 pub fn reset(mut self) -> Self {
1007 self.hardforks = ChainHardforks::default();
1008 self
1009 }
1010
1011 pub fn genesis(mut self, genesis: Genesis) -> Self {
1013 self.genesis = Some(genesis);
1014 self
1015 }
1016
1017 pub fn with_fork<H: Hardfork>(mut self, fork: H, condition: ForkCondition) -> Self {
1019 self.hardforks.insert(fork, condition);
1020 self
1021 }
1022
1023 pub fn with_forks(mut self, forks: ChainHardforks) -> Self {
1025 self.hardforks = forks;
1026 self
1027 }
1028
1029 pub fn without_fork<H: Hardfork>(mut self, fork: H) -> Self {
1031 self.hardforks.remove(&fork);
1032 self
1033 }
1034
1035 pub fn paris_at_ttd(self, ttd: U256, activation_block_number: BlockNumber) -> Self {
1039 self.with_fork(
1040 EthereumHardfork::Paris,
1041 ForkCondition::TTD { activation_block_number, total_difficulty: ttd, fork_block: None },
1042 )
1043 }
1044
1045 pub fn frontier_activated(mut self) -> Self {
1047 self.hardforks.insert(EthereumHardfork::Frontier, ForkCondition::Block(0));
1048 self
1049 }
1050
1051 pub fn dao_activated(mut self) -> Self {
1053 self = self.frontier_activated();
1054 self.hardforks.insert(EthereumHardfork::Dao, ForkCondition::Block(0));
1055 self
1056 }
1057
1058 pub fn homestead_activated(mut self) -> Self {
1060 self = self.dao_activated();
1061 self.hardforks.insert(EthereumHardfork::Homestead, ForkCondition::Block(0));
1062 self
1063 }
1064
1065 pub fn tangerine_whistle_activated(mut self) -> Self {
1067 self = self.homestead_activated();
1068 self.hardforks.insert(EthereumHardfork::Tangerine, ForkCondition::Block(0));
1069 self
1070 }
1071
1072 pub fn spurious_dragon_activated(mut self) -> Self {
1074 self = self.tangerine_whistle_activated();
1075 self.hardforks.insert(EthereumHardfork::SpuriousDragon, ForkCondition::Block(0));
1076 self
1077 }
1078
1079 pub fn byzantium_activated(mut self) -> Self {
1081 self = self.spurious_dragon_activated();
1082 self.hardforks.insert(EthereumHardfork::Byzantium, ForkCondition::Block(0));
1083 self
1084 }
1085
1086 pub fn constantinople_activated(mut self) -> Self {
1088 self = self.byzantium_activated();
1089 self.hardforks.insert(EthereumHardfork::Constantinople, ForkCondition::Block(0));
1090 self
1091 }
1092
1093 pub fn petersburg_activated(mut self) -> Self {
1095 self = self.constantinople_activated();
1096 self.hardforks.insert(EthereumHardfork::Petersburg, ForkCondition::Block(0));
1097 self
1098 }
1099
1100 pub fn istanbul_activated(mut self) -> Self {
1102 self = self.petersburg_activated();
1103 self.hardforks.insert(EthereumHardfork::Istanbul, ForkCondition::Block(0));
1104 self
1105 }
1106
1107 pub fn muirglacier_activated(mut self) -> Self {
1109 self = self.istanbul_activated();
1110 self.hardforks.insert(EthereumHardfork::MuirGlacier, ForkCondition::Block(0));
1111 self
1112 }
1113
1114 pub fn berlin_activated(mut self) -> Self {
1116 self = self.muirglacier_activated();
1117 self.hardforks.insert(EthereumHardfork::Berlin, ForkCondition::Block(0));
1118 self
1119 }
1120
1121 pub fn london_activated(mut self) -> Self {
1123 self = self.berlin_activated();
1124 self.hardforks.insert(EthereumHardfork::London, ForkCondition::Block(0));
1125 self
1126 }
1127
1128 pub fn arrowglacier_activated(mut self) -> Self {
1130 self = self.london_activated();
1131 self.hardforks.insert(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0));
1132 self
1133 }
1134
1135 pub fn grayglacier_activated(mut self) -> Self {
1137 self = self.arrowglacier_activated();
1138 self.hardforks.insert(EthereumHardfork::GrayGlacier, ForkCondition::Block(0));
1139 self
1140 }
1141
1142 pub fn paris_activated(mut self) -> Self {
1144 self = self.grayglacier_activated();
1145 self.hardforks.insert(
1146 EthereumHardfork::Paris,
1147 ForkCondition::TTD {
1148 activation_block_number: 0,
1149 total_difficulty: U256::ZERO,
1150 fork_block: None,
1151 },
1152 );
1153 self
1154 }
1155
1156 pub fn shanghai_activated(mut self) -> Self {
1158 self = self.paris_activated();
1159 self.hardforks.insert(EthereumHardfork::Shanghai, ForkCondition::Timestamp(0));
1160 self
1161 }
1162
1163 pub fn cancun_activated(mut self) -> Self {
1165 self = self.shanghai_activated();
1166 self.hardforks.insert(EthereumHardfork::Cancun, ForkCondition::Timestamp(0));
1167 self
1168 }
1169
1170 pub fn prague_activated(mut self) -> Self {
1172 self = self.cancun_activated();
1173 self.hardforks.insert(EthereumHardfork::Prague, ForkCondition::Timestamp(0));
1174 self
1175 }
1176
1177 pub fn with_prague_at(mut self, timestamp: u64) -> Self {
1179 self.hardforks.insert(EthereumHardfork::Prague, ForkCondition::Timestamp(timestamp));
1180 self
1181 }
1182
1183 pub fn osaka_activated(mut self) -> Self {
1185 self = self.prague_activated();
1186 self.hardforks.insert(EthereumHardfork::Osaka, ForkCondition::Timestamp(0));
1187 self
1188 }
1189
1190 pub fn with_osaka_at(mut self, timestamp: u64) -> Self {
1192 self.hardforks.insert(EthereumHardfork::Osaka, ForkCondition::Timestamp(timestamp));
1193 self
1194 }
1195
1196 pub fn build(self) -> ChainSpec {
1203 let paris_block_and_final_difficulty = {
1204 self.hardforks.get(EthereumHardfork::Paris).and_then(|cond| {
1205 if let ForkCondition::TTD { total_difficulty, activation_block_number, .. } = cond {
1206 Some((activation_block_number, total_difficulty))
1207 } else {
1208 None
1209 }
1210 })
1211 };
1212 let genesis = self.genesis.expect("The genesis is required");
1213 ChainSpec {
1214 chain: self.chain.expect("The chain is required"),
1215 genesis_header: SealedHeader::new_unhashed(make_genesis_header(
1216 &genesis,
1217 &self.hardforks,
1218 )),
1219 genesis,
1220 hardforks: self.hardforks,
1221 paris_block_and_final_difficulty,
1222 deposit_contract: None,
1223 ..Default::default()
1224 }
1225 }
1226}
1227
1228impl From<&Arc<ChainSpec>> for ChainSpecBuilder {
1229 fn from(value: &Arc<ChainSpec>) -> Self {
1230 Self {
1231 chain: Some(value.chain),
1232 genesis: Some(value.genesis.clone()),
1233 hardforks: value.hardforks.clone(),
1234 }
1235 }
1236}
1237
1238impl<H: BlockHeader> EthExecutorSpec for ChainSpec<H> {
1239 fn deposit_contract_address(&self) -> Option<Address> {
1240 self.deposit_contract.map(|deposit_contract| deposit_contract.address)
1241 }
1242}
1243
1244#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1246pub struct DepositContract {
1247 pub address: Address,
1249 pub block: BlockNumber,
1251 pub topic: B256,
1253}
1254
1255impl DepositContract {
1256 pub const fn new(address: Address, block: BlockNumber, topic: B256) -> Self {
1258 Self { address, block, topic }
1259 }
1260}
1261
1262#[cfg(any(test, feature = "test-utils"))]
1264pub fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) {
1265 for (block, expected_id) in cases {
1266 let computed_id = spec.fork_id(block);
1267 assert_eq!(
1268 expected_id, &computed_id,
1269 "Expected fork ID {:?}, computed fork ID {:?} at block {}",
1270 expected_id, computed_id, block.number
1271 );
1272 }
1273}
1274
1275#[cfg(test)]
1276mod tests {
1277 use super::*;
1278 use alloy_chains::Chain;
1279 use alloy_consensus::constants::ETH_TO_WEI;
1280 use alloy_eips::{eip4844::BLOB_TX_MIN_BLOB_GASPRICE, eip7840::BlobParams};
1281 use alloy_evm::block::calc::{base_block_reward, block_reward};
1282 use alloy_genesis::{ChainConfig, GenesisAccount};
1283 use alloy_primitives::{b256, hex};
1284 use alloy_trie::{TrieAccount, EMPTY_ROOT_HASH};
1285 use core::ops::Deref;
1286 use reth_ethereum_forks::{ForkCondition, ForkHash, ForkId, Head};
1287 use std::{collections::HashMap, str::FromStr};
1288
1289 fn test_hardfork_fork_ids(spec: &ChainSpec, cases: &[(EthereumHardfork, ForkId)]) {
1290 for (hardfork, expected_id) in cases {
1291 if let Some(computed_id) = spec.hardfork_fork_id(*hardfork) {
1292 assert_eq!(
1293 expected_id, &computed_id,
1294 "Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for hardfork {hardfork}"
1295 );
1296 if matches!(hardfork, EthereumHardfork::Shanghai) {
1297 if let Some(shanghai_id) = spec.shanghai_fork_id() {
1298 assert_eq!(
1299 expected_id, &shanghai_id,
1300 "Expected fork ID {expected_id:?}, computed fork ID {computed_id:?} for Shanghai hardfork"
1301 );
1302 } else {
1303 panic!("Expected ForkCondition to return Some for Hardfork::Shanghai");
1304 }
1305 }
1306 }
1307 }
1308 }
1309
1310 #[test]
1311 fn test_hardfork_list_display_mainnet() {
1312 assert_eq!(
1313 MAINNET.display_hardforks().to_string(),
1314 "Pre-merge hard forks (block based):
1315- Frontier @0
1316- Homestead @1150000
1317- Dao @1920000
1318- Tangerine @2463000
1319- SpuriousDragon @2675000
1320- Byzantium @4370000
1321- Constantinople @7280000
1322- Petersburg @7280000
1323- Istanbul @9069000
1324- MuirGlacier @9200000
1325- Berlin @12244000
1326- London @12965000
1327- ArrowGlacier @13773000
1328- GrayGlacier @15050000
1329Merge hard forks:
1330- Paris @58750000000000000000000 (network is known to be merged)
1331Post-merge hard forks (timestamp based):
1332- Shanghai @1681338455
1333- Cancun @1710338135 blob: (target: 3, max: 6, fraction: 3338477)
1334- Prague @1746612311 blob: (target: 6, max: 9, fraction: 5007716)
1335- Osaka @1764798551 blob: (target: 6, max: 9, fraction: 5007716)
1336- Bpo1 @1765290071 blob: (target: 10, max: 15, fraction: 8346193)
1337- Bpo2 @1767747671 blob: (target: 14, max: 21, fraction: 11684671)"
1338 );
1339 }
1340
1341 #[test]
1342 fn test_hardfork_list_ignores_disabled_forks() {
1343 let spec = ChainSpec::builder()
1344 .chain(Chain::mainnet())
1345 .genesis(Genesis::default())
1346 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1347 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Never)
1348 .build();
1349 assert_eq!(
1350 spec.display_hardforks().to_string(),
1351 "Pre-merge hard forks (block based):
1352- Frontier @0"
1353 );
1354 }
1355
1356 #[test]
1358 fn ignores_genesis_fork_blocks() {
1359 let spec = ChainSpec::builder()
1360 .chain(Chain::mainnet())
1361 .genesis(Genesis::default())
1362 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1363 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(0))
1364 .with_fork(EthereumHardfork::Tangerine, ForkCondition::Block(0))
1365 .with_fork(EthereumHardfork::SpuriousDragon, ForkCondition::Block(0))
1366 .with_fork(EthereumHardfork::Byzantium, ForkCondition::Block(0))
1367 .with_fork(EthereumHardfork::Constantinople, ForkCondition::Block(0))
1368 .with_fork(EthereumHardfork::Istanbul, ForkCondition::Block(0))
1369 .with_fork(EthereumHardfork::MuirGlacier, ForkCondition::Block(0))
1370 .with_fork(EthereumHardfork::Berlin, ForkCondition::Block(0))
1371 .with_fork(EthereumHardfork::London, ForkCondition::Block(0))
1372 .with_fork(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0))
1373 .with_fork(EthereumHardfork::GrayGlacier, ForkCondition::Block(0))
1374 .build();
1375
1376 assert_eq!(spec.deref().len(), 12, "12 forks should be active.");
1377 assert_eq!(
1378 spec.fork_id(&Head { number: 1, ..Default::default() }),
1379 ForkId { hash: ForkHash::from(spec.genesis_hash()), next: 0 },
1380 "the fork ID should be the genesis hash; forks at genesis are ignored for fork filters"
1381 );
1382 }
1383
1384 #[test]
1385 fn ignores_duplicate_fork_blocks() {
1386 let empty_genesis = Genesis::default();
1387 let unique_spec = ChainSpec::builder()
1388 .chain(Chain::mainnet())
1389 .genesis(empty_genesis.clone())
1390 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1391 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(1))
1392 .build();
1393
1394 let duplicate_spec = ChainSpec::builder()
1395 .chain(Chain::mainnet())
1396 .genesis(empty_genesis)
1397 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1398 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(1))
1399 .with_fork(EthereumHardfork::Tangerine, ForkCondition::Block(1))
1400 .build();
1401
1402 assert_eq!(
1403 unique_spec.fork_id(&Head { number: 2, ..Default::default() }),
1404 duplicate_spec.fork_id(&Head { number: 2, ..Default::default() }),
1405 "duplicate fork blocks should be deduplicated for fork filters"
1406 );
1407 }
1408
1409 #[test]
1410 fn test_chainspec_satisfy() {
1411 let empty_genesis = Genesis::default();
1412 let happy_path_case = ChainSpec::builder()
1414 .chain(Chain::mainnet())
1415 .genesis(empty_genesis.clone())
1416 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1417 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1418 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1419 .build();
1420 let happy_path_head = happy_path_case.satisfy(ForkCondition::Timestamp(11313123));
1421 let happy_path_expected = Head { number: 73, timestamp: 11313123, ..Default::default() };
1422 assert_eq!(
1423 happy_path_head, happy_path_expected,
1424 "expected satisfy() to return {happy_path_expected:#?}, but got {happy_path_head:#?} "
1425 );
1426 let multiple_timestamp_fork_case = ChainSpec::builder()
1428 .chain(Chain::mainnet())
1429 .genesis(empty_genesis.clone())
1430 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1431 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1432 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1433 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(11313398))
1434 .build();
1435 let multi_timestamp_head =
1436 multiple_timestamp_fork_case.satisfy(ForkCondition::Timestamp(11313398));
1437 let mult_timestamp_expected =
1438 Head { number: 73, timestamp: 11313398, ..Default::default() };
1439 assert_eq!(
1440 multi_timestamp_head, mult_timestamp_expected,
1441 "expected satisfy() to return {mult_timestamp_expected:#?}, but got {multi_timestamp_head:#?} "
1442 );
1443 let no_block_fork_case = ChainSpec::builder()
1445 .chain(Chain::mainnet())
1446 .genesis(empty_genesis.clone())
1447 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1448 .build();
1449 let no_block_fork_head = no_block_fork_case.satisfy(ForkCondition::Timestamp(11313123));
1450 let no_block_fork_expected = Head { number: 0, timestamp: 11313123, ..Default::default() };
1451 assert_eq!(
1452 no_block_fork_head, no_block_fork_expected,
1453 "expected satisfy() to return {no_block_fork_expected:#?}, but got {no_block_fork_head:#?} ",
1454 );
1455 let fork_cond_ttd_blocknum_case = ChainSpec::builder()
1457 .chain(Chain::mainnet())
1458 .genesis(empty_genesis.clone())
1459 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1460 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1461 .with_fork(
1462 EthereumHardfork::Paris,
1463 ForkCondition::TTD {
1464 activation_block_number: 101,
1465 fork_block: Some(101),
1466 total_difficulty: U256::from(10_790_000),
1467 },
1468 )
1469 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(11313123))
1470 .build();
1471 let fork_cond_ttd_blocknum_head =
1472 fork_cond_ttd_blocknum_case.satisfy(ForkCondition::Timestamp(11313123));
1473 let fork_cond_ttd_blocknum_expected =
1474 Head { number: 101, timestamp: 11313123, ..Default::default() };
1475 assert_eq!(
1476 fork_cond_ttd_blocknum_head, fork_cond_ttd_blocknum_expected,
1477 "expected satisfy() to return {fork_cond_ttd_blocknum_expected:#?}, but got {fork_cond_ttd_blocknum_head:#?} ",
1478 );
1479
1480 let fork_cond_block_only_case = ChainSpec::builder()
1484 .chain(Chain::mainnet())
1485 .genesis(empty_genesis)
1486 .with_fork(EthereumHardfork::Frontier, ForkCondition::Block(0))
1487 .with_fork(EthereumHardfork::Homestead, ForkCondition::Block(73))
1488 .build();
1489 let fork_cond_block_only_head = fork_cond_block_only_case.satisfy(ForkCondition::Block(73));
1490 let fork_cond_block_only_expected = Head { number: 73, ..Default::default() };
1491 assert_eq!(
1492 fork_cond_block_only_head, fork_cond_block_only_expected,
1493 "expected satisfy() to return {fork_cond_block_only_expected:#?}, but got {fork_cond_block_only_head:#?} ",
1494 );
1495 let fork_cond_ttd_no_new_spec = fork_cond_block_only_case.satisfy(ForkCondition::TTD {
1498 activation_block_number: 101,
1499 fork_block: None,
1500 total_difficulty: U256::from(10_790_000),
1501 });
1502 let fork_cond_ttd_no_new_spec_expected =
1503 Head { total_difficulty: U256::from(10_790_000), ..Default::default() };
1504 assert_eq!(
1505 fork_cond_ttd_no_new_spec, fork_cond_ttd_no_new_spec_expected,
1506 "expected satisfy() to return {fork_cond_ttd_no_new_spec_expected:#?}, but got {fork_cond_ttd_no_new_spec:#?} ",
1507 );
1508 }
1509
1510 #[test]
1511 fn mainnet_hardfork_fork_ids() {
1512 test_hardfork_fork_ids(
1513 &MAINNET,
1514 &[
1515 (
1516 EthereumHardfork::Frontier,
1517 ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
1518 ),
1519 (
1520 EthereumHardfork::Homestead,
1521 ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
1522 ),
1523 (
1524 EthereumHardfork::Dao,
1525 ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
1526 ),
1527 (
1528 EthereumHardfork::Tangerine,
1529 ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
1530 ),
1531 (
1532 EthereumHardfork::SpuriousDragon,
1533 ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
1534 ),
1535 (
1536 EthereumHardfork::Byzantium,
1537 ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
1538 ),
1539 (
1540 EthereumHardfork::Constantinople,
1541 ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
1542 ),
1543 (
1544 EthereumHardfork::Petersburg,
1545 ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
1546 ),
1547 (
1548 EthereumHardfork::Istanbul,
1549 ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
1550 ),
1551 (
1552 EthereumHardfork::MuirGlacier,
1553 ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
1554 ),
1555 (
1556 EthereumHardfork::Berlin,
1557 ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
1558 ),
1559 (
1560 EthereumHardfork::London,
1561 ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
1562 ),
1563 (
1564 EthereumHardfork::ArrowGlacier,
1565 ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
1566 ),
1567 (
1568 EthereumHardfork::GrayGlacier,
1569 ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
1570 ),
1571 (
1572 EthereumHardfork::Shanghai,
1573 ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
1574 ),
1575 (
1576 EthereumHardfork::Cancun,
1577 ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
1578 ),
1579 (
1580 EthereumHardfork::Prague,
1581 ForkId {
1582 hash: ForkHash(hex!("0xc376cf8b")),
1583 next: mainnet::MAINNET_OSAKA_TIMESTAMP,
1584 },
1585 ),
1586 ],
1587 );
1588 }
1589
1590 #[test]
1591 fn sepolia_hardfork_fork_ids() {
1592 test_hardfork_fork_ids(
1593 &SEPOLIA,
1594 &[
1595 (
1596 EthereumHardfork::Frontier,
1597 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1598 ),
1599 (
1600 EthereumHardfork::Homestead,
1601 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1602 ),
1603 (
1604 EthereumHardfork::Tangerine,
1605 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1606 ),
1607 (
1608 EthereumHardfork::SpuriousDragon,
1609 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1610 ),
1611 (
1612 EthereumHardfork::Byzantium,
1613 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1614 ),
1615 (
1616 EthereumHardfork::Constantinople,
1617 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1618 ),
1619 (
1620 EthereumHardfork::Petersburg,
1621 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1622 ),
1623 (
1624 EthereumHardfork::Istanbul,
1625 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1626 ),
1627 (
1628 EthereumHardfork::Berlin,
1629 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1630 ),
1631 (
1632 EthereumHardfork::London,
1633 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1634 ),
1635 (
1636 EthereumHardfork::Paris,
1637 ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
1638 ),
1639 (
1640 EthereumHardfork::Shanghai,
1641 ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
1642 ),
1643 (
1644 EthereumHardfork::Cancun,
1645 ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
1646 ),
1647 (
1648 EthereumHardfork::Prague,
1649 ForkId {
1650 hash: ForkHash(hex!("0xed88b5fd")),
1651 next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
1652 },
1653 ),
1654 ],
1655 );
1656 }
1657
1658 #[test]
1659 fn mainnet_fork_ids() {
1660 test_fork_ids(
1661 &MAINNET,
1662 &[
1663 (
1664 Head { number: 0, ..Default::default() },
1665 ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
1666 ),
1667 (
1668 Head { number: 1150000, ..Default::default() },
1669 ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
1670 ),
1671 (
1672 Head { number: 1920000, ..Default::default() },
1673 ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
1674 ),
1675 (
1676 Head { number: 2463000, ..Default::default() },
1677 ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
1678 ),
1679 (
1680 Head { number: 2675000, ..Default::default() },
1681 ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
1682 ),
1683 (
1684 Head { number: 4370000, ..Default::default() },
1685 ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
1686 ),
1687 (
1688 Head { number: 7280000, ..Default::default() },
1689 ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
1690 ),
1691 (
1692 Head { number: 9069000, ..Default::default() },
1693 ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
1694 ),
1695 (
1696 Head { number: 9200000, ..Default::default() },
1697 ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
1698 ),
1699 (
1700 Head { number: 12244000, ..Default::default() },
1701 ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
1702 ),
1703 (
1704 Head { number: 12965000, ..Default::default() },
1705 ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
1706 ),
1707 (
1708 Head { number: 13773000, ..Default::default() },
1709 ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
1710 ),
1711 (
1712 Head { number: 15050000, ..Default::default() },
1713 ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
1714 ),
1715 (
1717 Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
1718 ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
1719 ),
1720 (
1722 Head { number: 20000001, timestamp: 1710338135, ..Default::default() },
1723 ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
1724 ),
1725 (
1727 Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
1728 ForkId {
1729 hash: ForkHash(hex!("0xc376cf8b")),
1730 next: mainnet::MAINNET_OSAKA_TIMESTAMP,
1731 },
1732 ),
1733 (
1735 Head {
1736 number: 20000004,
1737 timestamp: mainnet::MAINNET_OSAKA_TIMESTAMP,
1738 ..Default::default()
1739 },
1740 ForkId {
1741 hash: ForkHash(hex!("0x5167e2a6")),
1742 next: mainnet::MAINNET_BPO1_TIMESTAMP,
1743 },
1744 ),
1745 ],
1746 );
1747 }
1748
1749 #[test]
1750 fn hoodi_fork_ids() {
1751 test_fork_ids(
1752 &HOODI,
1753 &[
1754 (
1755 Head { number: 0, ..Default::default() },
1756 ForkId { hash: ForkHash(hex!("0xbef71d30")), next: 1742999832 },
1757 ),
1758 (
1760 Head { number: 0, timestamp: 1742999833, ..Default::default() },
1761 ForkId {
1762 hash: ForkHash(hex!("0x0929e24e")),
1763 next: hoodi::HOODI_OSAKA_TIMESTAMP,
1764 },
1765 ),
1766 (
1768 Head {
1769 number: 0,
1770 timestamp: hoodi::HOODI_OSAKA_TIMESTAMP,
1771 ..Default::default()
1772 },
1773 ForkId {
1774 hash: ForkHash(hex!("0xe7e0e7ff")),
1775 next: hoodi::HOODI_BPO1_TIMESTAMP,
1776 },
1777 ),
1778 ],
1779 )
1780 }
1781
1782 #[test]
1783 fn holesky_fork_ids() {
1784 test_fork_ids(
1785 &HOLESKY,
1786 &[
1787 (
1788 Head { number: 0, ..Default::default() },
1789 ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
1790 ),
1791 (
1793 Head { number: 123, ..Default::default() },
1794 ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
1795 ),
1796 (
1798 Head { number: 123, timestamp: 1696000703, ..Default::default() },
1799 ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
1800 ),
1801 (
1803 Head { number: 123, timestamp: 1696000704, ..Default::default() },
1804 ForkId { hash: ForkHash(hex!("0xfd4f016b")), next: 1707305664 },
1805 ),
1806 (
1808 Head { number: 123, timestamp: 1707305663, ..Default::default() },
1809 ForkId { hash: ForkHash(hex!("0xfd4f016b")), next: 1707305664 },
1810 ),
1811 (
1813 Head { number: 123, timestamp: 1707305664, ..Default::default() },
1814 ForkId { hash: ForkHash(hex!("0x9b192ad0")), next: 1740434112 },
1815 ),
1816 (
1818 Head { number: 123, timestamp: 1740434111, ..Default::default() },
1819 ForkId { hash: ForkHash(hex!("0x9b192ad0")), next: 1740434112 },
1820 ),
1821 (
1823 Head { number: 123, timestamp: 1740434112, ..Default::default() },
1824 ForkId {
1825 hash: ForkHash(hex!("0xdfbd9bed")),
1826 next: holesky::HOLESKY_OSAKA_TIMESTAMP,
1827 },
1828 ),
1829 (
1831 Head {
1832 number: 123,
1833 timestamp: holesky::HOLESKY_OSAKA_TIMESTAMP,
1834 ..Default::default()
1835 },
1836 ForkId {
1837 hash: ForkHash(hex!("0x783def52")),
1838 next: holesky::HOLESKY_BPO1_TIMESTAMP,
1839 },
1840 ),
1841 ],
1842 )
1843 }
1844
1845 #[test]
1846 fn sepolia_fork_ids() {
1847 test_fork_ids(
1848 &SEPOLIA,
1849 &[
1850 (
1851 Head { number: 0, ..Default::default() },
1852 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1853 ),
1854 (
1855 Head { number: 1735370, ..Default::default() },
1856 ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
1857 ),
1858 (
1859 Head { number: 1735371, ..Default::default() },
1860 ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
1861 ),
1862 (
1863 Head { number: 1735372, timestamp: 1677557087, ..Default::default() },
1864 ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
1865 ),
1866 (
1868 Head { number: 1735373, timestamp: 1677557088, ..Default::default() },
1869 ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
1870 ),
1871 (
1873 Head { number: 1735374, timestamp: 1706655071, ..Default::default() },
1874 ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
1875 ),
1876 (
1878 Head { number: 1735375, timestamp: 1706655072, ..Default::default() },
1879 ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
1880 ),
1881 (
1883 Head { number: 1735376, timestamp: 1741159775, ..Default::default() },
1884 ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
1885 ),
1886 (
1888 Head { number: 1735377, timestamp: 1741159776, ..Default::default() },
1889 ForkId {
1890 hash: ForkHash(hex!("0xed88b5fd")),
1891 next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
1892 },
1893 ),
1894 (
1896 Head {
1897 number: 1735377,
1898 timestamp: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
1899 ..Default::default()
1900 },
1901 ForkId {
1902 hash: ForkHash(hex!("0xe2ae4999")),
1903 next: sepolia::SEPOLIA_BPO1_TIMESTAMP,
1904 },
1905 ),
1906 ],
1907 );
1908 }
1909
1910 #[test]
1911 fn dev_fork_ids() {
1912 test_fork_ids(
1913 &DEV,
1914 &[(
1915 Head { number: 0, ..Default::default() },
1916 ForkId { hash: ForkHash(hex!("0x0b1a4ef7")), next: 0 },
1917 )],
1918 )
1919 }
1920
1921 #[test]
1925 fn timestamped_forks() {
1926 let mainnet_with_timestamps = ChainSpecBuilder::mainnet().build();
1927 test_fork_ids(
1928 &mainnet_with_timestamps,
1929 &[
1930 (
1931 Head { number: 0, timestamp: 0, ..Default::default() },
1932 ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
1933 ), (
1935 Head { number: 1149999, timestamp: 0, ..Default::default() },
1936 ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
1937 ), (
1939 Head { number: 1150000, timestamp: 0, ..Default::default() },
1940 ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
1941 ), (
1943 Head { number: 1919999, timestamp: 0, ..Default::default() },
1944 ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
1945 ), (
1947 Head { number: 1920000, timestamp: 0, ..Default::default() },
1948 ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
1949 ), (
1951 Head { number: 2462999, timestamp: 0, ..Default::default() },
1952 ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
1953 ), (
1955 Head { number: 2463000, timestamp: 0, ..Default::default() },
1956 ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
1957 ), (
1959 Head { number: 2674999, timestamp: 0, ..Default::default() },
1960 ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
1961 ), (
1963 Head { number: 2675000, timestamp: 0, ..Default::default() },
1964 ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
1965 ), (
1967 Head { number: 4369999, timestamp: 0, ..Default::default() },
1968 ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
1969 ), (
1971 Head { number: 4370000, timestamp: 0, ..Default::default() },
1972 ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
1973 ), (
1975 Head { number: 7279999, timestamp: 0, ..Default::default() },
1976 ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
1977 ), (
1979 Head { number: 7280000, timestamp: 0, ..Default::default() },
1980 ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
1981 ), (
1983 Head { number: 9068999, timestamp: 0, ..Default::default() },
1984 ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
1985 ), (
1987 Head { number: 9069000, timestamp: 0, ..Default::default() },
1988 ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
1989 ), (
1991 Head { number: 9199999, timestamp: 0, ..Default::default() },
1992 ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
1993 ), (
1995 Head { number: 9200000, timestamp: 0, ..Default::default() },
1996 ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
1997 ), (
1999 Head { number: 12243999, timestamp: 0, ..Default::default() },
2000 ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
2001 ), (
2003 Head { number: 12244000, timestamp: 0, ..Default::default() },
2004 ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
2005 ), (
2007 Head { number: 12964999, timestamp: 0, ..Default::default() },
2008 ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
2009 ), (
2011 Head { number: 12965000, timestamp: 0, ..Default::default() },
2012 ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
2013 ), (
2015 Head { number: 13772999, timestamp: 0, ..Default::default() },
2016 ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
2017 ), (
2019 Head { number: 13773000, timestamp: 0, ..Default::default() },
2020 ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
2021 ), (
2023 Head { number: 15049999, timestamp: 0, ..Default::default() },
2024 ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
2025 ), (
2027 Head { number: 15050000, timestamp: 0, ..Default::default() },
2028 ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
2029 ), (
2031 Head { number: 19999999, timestamp: 1667999999, ..Default::default() },
2032 ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
2033 ), (
2035 Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
2036 ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
2037 ), (
2039 Head { number: 20000001, timestamp: 1710338134, ..Default::default() },
2040 ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
2041 ), (
2043 Head { number: 20000002, timestamp: 1710338135, ..Default::default() },
2044 ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
2045 ), (
2047 Head { number: 20000003, timestamp: 1746612310, ..Default::default() },
2048 ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
2049 ), (
2051 Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
2052 ForkId {
2053 hash: ForkHash(hex!("0xc376cf8b")),
2054 next: mainnet::MAINNET_OSAKA_TIMESTAMP,
2055 },
2056 ),
2057 (
2059 Head {
2060 number: 20000004,
2061 timestamp: mainnet::MAINNET_OSAKA_TIMESTAMP,
2062 ..Default::default()
2063 },
2064 ForkId {
2065 hash: ForkHash(hex!("0x5167e2a6")),
2066 next: mainnet::MAINNET_BPO1_TIMESTAMP,
2067 },
2068 ),
2069 ],
2070 );
2071 }
2072
2073 fn construct_chainspec(
2076 builder: ChainSpecBuilder,
2077 shanghai_time: u64,
2078 cancun_time: u64,
2079 ) -> ChainSpec {
2080 builder
2081 .with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(shanghai_time))
2082 .with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(cancun_time))
2083 .build()
2084 }
2085
2086 #[test]
2091 fn test_timestamp_fork_in_genesis() {
2092 let timestamp = 1690475657u64;
2093 let default_spec_builder = ChainSpecBuilder::default()
2094 .chain(Chain::from_id(1337))
2095 .genesis(Genesis::default().with_timestamp(timestamp))
2096 .paris_activated();
2097
2098 let tests = [
2101 (
2102 construct_chainspec(default_spec_builder.clone(), timestamp - 1, timestamp + 1),
2103 timestamp + 1,
2104 ),
2105 (
2106 construct_chainspec(default_spec_builder.clone(), timestamp, timestamp + 1),
2107 timestamp + 1,
2108 ),
2109 (
2110 construct_chainspec(default_spec_builder, timestamp + 1, timestamp + 2),
2111 timestamp + 1,
2112 ),
2113 ];
2114
2115 for (spec, expected_timestamp) in tests {
2116 let got_forkid = spec.fork_id(&Head { number: 0, timestamp: 0, ..Default::default() });
2117 let genesis_hash = spec.genesis_hash();
2122 let expected_forkid =
2123 ForkId { hash: ForkHash::from(genesis_hash), next: expected_timestamp };
2124 assert_eq!(got_forkid, expected_forkid);
2125 }
2126 }
2127
2128 #[test]
2130 fn check_terminal_ttd() {
2131 let chainspec = ChainSpecBuilder::mainnet().build();
2132
2133 let terminal_block_ttd = U256::from(58750003716598352816469_u128);
2135 let terminal_block_difficulty = U256::from(11055787484078698_u128);
2136 assert!(!chainspec
2137 .fork(EthereumHardfork::Paris)
2138 .active_at_ttd(terminal_block_ttd, terminal_block_difficulty));
2139
2140 let first_pos_block_ttd = U256::from(58750003716598352816469_u128);
2142 let first_pos_difficulty = U256::ZERO;
2143 assert!(chainspec
2144 .fork(EthereumHardfork::Paris)
2145 .active_at_ttd(first_pos_block_ttd, first_pos_difficulty));
2146 }
2147
2148 #[test]
2149 fn geth_genesis_with_shanghai() {
2150 let geth_genesis = r#"
2151 {
2152 "config": {
2153 "chainId": 1337,
2154 "homesteadBlock": 0,
2155 "eip150Block": 0,
2156 "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2157 "eip155Block": 0,
2158 "eip158Block": 0,
2159 "byzantiumBlock": 0,
2160 "constantinopleBlock": 0,
2161 "petersburgBlock": 0,
2162 "istanbulBlock": 0,
2163 "muirGlacierBlock": 0,
2164 "berlinBlock": 0,
2165 "londonBlock": 0,
2166 "arrowGlacierBlock": 0,
2167 "grayGlacierBlock": 0,
2168 "shanghaiTime": 0,
2169 "cancunTime": 1,
2170 "terminalTotalDifficulty": 0,
2171 "terminalTotalDifficultyPassed": true,
2172 "ethash": {}
2173 },
2174 "nonce": "0x0",
2175 "timestamp": "0x0",
2176 "extraData": "0x",
2177 "gasLimit": "0x4c4b40",
2178 "difficulty": "0x1",
2179 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2180 "coinbase": "0x0000000000000000000000000000000000000000",
2181 "alloc": {
2182 "658bdf435d810c91414ec09147daa6db62406379": {
2183 "balance": "0x487a9a304539440000"
2184 },
2185 "aa00000000000000000000000000000000000000": {
2186 "code": "0x6042",
2187 "storage": {
2188 "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
2189 "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000",
2190 "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000",
2191 "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303"
2192 },
2193 "balance": "0x1",
2194 "nonce": "0x1"
2195 },
2196 "bb00000000000000000000000000000000000000": {
2197 "code": "0x600154600354",
2198 "storage": {
2199 "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
2200 "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000",
2201 "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000",
2202 "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303"
2203 },
2204 "balance": "0x2",
2205 "nonce": "0x1"
2206 }
2207 },
2208 "number": "0x0",
2209 "gasUsed": "0x0",
2210 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2211 "baseFeePerGas": "0x3b9aca00"
2212 }
2213 "#;
2214
2215 let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap();
2216 let chainspec = ChainSpec::from(genesis);
2217
2218 assert_eq!(
2220 chainspec.hardforks.get(EthereumHardfork::Homestead).unwrap(),
2221 ForkCondition::Block(0)
2222 );
2223 assert_eq!(
2224 chainspec.hardforks.get(EthereumHardfork::Tangerine).unwrap(),
2225 ForkCondition::Block(0)
2226 );
2227 assert_eq!(
2228 chainspec.hardforks.get(EthereumHardfork::SpuriousDragon).unwrap(),
2229 ForkCondition::Block(0)
2230 );
2231 assert_eq!(
2232 chainspec.hardforks.get(EthereumHardfork::Byzantium).unwrap(),
2233 ForkCondition::Block(0)
2234 );
2235 assert_eq!(
2236 chainspec.hardforks.get(EthereumHardfork::Constantinople).unwrap(),
2237 ForkCondition::Block(0)
2238 );
2239 assert_eq!(
2240 chainspec.hardforks.get(EthereumHardfork::Petersburg).unwrap(),
2241 ForkCondition::Block(0)
2242 );
2243 assert_eq!(
2244 chainspec.hardforks.get(EthereumHardfork::Istanbul).unwrap(),
2245 ForkCondition::Block(0)
2246 );
2247 assert_eq!(
2248 chainspec.hardforks.get(EthereumHardfork::MuirGlacier).unwrap(),
2249 ForkCondition::Block(0)
2250 );
2251 assert_eq!(
2252 chainspec.hardforks.get(EthereumHardfork::Berlin).unwrap(),
2253 ForkCondition::Block(0)
2254 );
2255 assert_eq!(
2256 chainspec.hardforks.get(EthereumHardfork::London).unwrap(),
2257 ForkCondition::Block(0)
2258 );
2259 assert_eq!(
2260 chainspec.hardforks.get(EthereumHardfork::ArrowGlacier).unwrap(),
2261 ForkCondition::Block(0)
2262 );
2263 assert_eq!(
2264 chainspec.hardforks.get(EthereumHardfork::GrayGlacier).unwrap(),
2265 ForkCondition::Block(0)
2266 );
2267
2268 assert_eq!(
2270 chainspec.hardforks.get(EthereumHardfork::Shanghai).unwrap(),
2271 ForkCondition::Timestamp(0)
2272 );
2273
2274 assert_eq!(
2276 chainspec.hardforks.get(EthereumHardfork::Cancun).unwrap(),
2277 ForkCondition::Timestamp(1)
2278 );
2279
2280 let key_rlp = vec![
2282 (
2283 hex!("0x658bdf435d810c91414ec09147daa6db62406379"),
2284 &hex!(
2285 "0xf84d8089487a9a304539440000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
2286 )[..],
2287 ),
2288 (
2289 hex!("0xaa00000000000000000000000000000000000000"),
2290 &hex!(
2291 "0xf8440101a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0ce92c756baff35fa740c3557c1a971fd24d2d35b7c8e067880d50cd86bb0bc99"
2292 )[..],
2293 ),
2294 (
2295 hex!("0xbb00000000000000000000000000000000000000"),
2296 &hex!(
2297 "0xf8440102a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0e25a53cbb501cec2976b393719c63d832423dd70a458731a0b64e4847bbca7d2"
2298 )[..],
2299 ),
2300 ];
2301
2302 for (key, expected_rlp) in key_rlp {
2303 let account = chainspec.genesis.alloc.get(&key).expect("account should exist");
2304 assert_eq!(&alloy_rlp::encode(TrieAccount::from(account.clone())), expected_rlp);
2305 }
2306
2307 let expected_state_root: B256 =
2308 hex!("0x078dc6061b1d8eaa8493384b59c9c65ceb917201221d08b80c4de6770b6ec7e7").into();
2309 assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
2310
2311 assert_eq!(chainspec.genesis_header().withdrawals_root, Some(EMPTY_ROOT_HASH));
2312
2313 let expected_hash: B256 =
2314 hex!("0x1fc027d65f820d3eef441ebeec139ebe09e471cf98516dce7b5643ccb27f418c").into();
2315 let hash = chainspec.genesis_hash();
2316 assert_eq!(hash, expected_hash);
2317 }
2318
2319 #[test]
2320 fn hive_geth_json() {
2321 let hive_json = r#"
2322 {
2323 "nonce": "0x0000000000000042",
2324 "difficulty": "0x2123456",
2325 "mixHash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
2326 "coinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
2327 "timestamp": "0x123456",
2328 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2329 "extraData": "0xfafbfcfd",
2330 "gasLimit": "0x2fefd8",
2331 "alloc": {
2332 "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {
2333 "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
2334 },
2335 "e6716f9544a56c530d868e4bfbacb172315bdead": {
2336 "balance": "0x11",
2337 "code": "0x12"
2338 },
2339 "b9c015918bdaba24b4ff057a92a3873d6eb201be": {
2340 "balance": "0x21",
2341 "storage": {
2342 "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22"
2343 }
2344 },
2345 "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {
2346 "balance": "0x31",
2347 "nonce": "0x32"
2348 },
2349 "0000000000000000000000000000000000000001": {
2350 "balance": "0x41"
2351 },
2352 "0000000000000000000000000000000000000002": {
2353 "balance": "0x51"
2354 },
2355 "0000000000000000000000000000000000000003": {
2356 "balance": "0x61"
2357 },
2358 "0000000000000000000000000000000000000004": {
2359 "balance": "0x71"
2360 }
2361 },
2362 "config": {
2363 "ethash": {},
2364 "chainId": 10,
2365 "homesteadBlock": 0,
2366 "eip150Block": 0,
2367 "eip155Block": 0,
2368 "eip158Block": 0,
2369 "byzantiumBlock": 0,
2370 "constantinopleBlock": 0,
2371 "petersburgBlock": 0,
2372 "istanbulBlock": 0
2373 }
2374 }
2375 "#;
2376
2377 let genesis = serde_json::from_str::<Genesis>(hive_json).unwrap();
2378 let chainspec: ChainSpec = genesis.into();
2379 assert_eq!(chainspec.chain, Chain::from_named(NamedChain::Optimism));
2380 let expected_state_root: B256 =
2381 hex!("0x9a6049ac535e3dc7436c189eaa81c73f35abd7f282ab67c32944ff0301d63360").into();
2382 assert_eq!(chainspec.genesis_header().state_root, expected_state_root);
2383 let hard_forks = vec![
2384 EthereumHardfork::Byzantium,
2385 EthereumHardfork::Homestead,
2386 EthereumHardfork::Istanbul,
2387 EthereumHardfork::Petersburg,
2388 EthereumHardfork::Constantinople,
2389 ];
2390 for fork in hard_forks {
2391 assert_eq!(chainspec.hardforks.get(fork).unwrap(), ForkCondition::Block(0));
2392 }
2393
2394 let expected_hash: B256 =
2395 hex!("0x5ae31c6522bd5856129f66be3d582b842e4e9faaa87f21cce547128339a9db3c").into();
2396 let hash = chainspec.genesis_header().hash_slow();
2397 assert_eq!(hash, expected_hash);
2398 }
2399
2400 #[test]
2401 fn test_hive_paris_block_genesis_json() {
2402 let hive_paris = r#"
2405 {
2406 "config": {
2407 "ethash": {},
2408 "chainId": 3503995874084926,
2409 "homesteadBlock": 0,
2410 "eip150Block": 6,
2411 "eip155Block": 12,
2412 "eip158Block": 12,
2413 "byzantiumBlock": 18,
2414 "constantinopleBlock": 24,
2415 "petersburgBlock": 30,
2416 "istanbulBlock": 36,
2417 "muirGlacierBlock": 42,
2418 "berlinBlock": 48,
2419 "londonBlock": 54,
2420 "arrowGlacierBlock": 60,
2421 "grayGlacierBlock": 66,
2422 "mergeNetsplitBlock": 72,
2423 "terminalTotalDifficulty": 9454784,
2424 "shanghaiTime": 780,
2425 "cancunTime": 840
2426 },
2427 "nonce": "0x0",
2428 "timestamp": "0x0",
2429 "extraData": "0x68697665636861696e",
2430 "gasLimit": "0x23f3e20",
2431 "difficulty": "0x20000",
2432 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2433 "coinbase": "0x0000000000000000000000000000000000000000",
2434 "alloc": {
2435 "000f3df6d732807ef1319fb7b8bb8522d0beac02": {
2436 "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
2437 "balance": "0x2a"
2438 },
2439 "0c2c51a0990aee1d73c1228de158688341557508": {
2440 "balance": "0xc097ce7bc90715b34b9f1000000000"
2441 },
2442 "14e46043e63d0e3cdcf2530519f4cfaf35058cb2": {
2443 "balance": "0xc097ce7bc90715b34b9f1000000000"
2444 },
2445 "16c57edf7fa9d9525378b0b81bf8a3ced0620c1c": {
2446 "balance": "0xc097ce7bc90715b34b9f1000000000"
2447 },
2448 "1f4924b14f34e24159387c0a4cdbaa32f3ddb0cf": {
2449 "balance": "0xc097ce7bc90715b34b9f1000000000"
2450 },
2451 "1f5bde34b4afc686f136c7a3cb6ec376f7357759": {
2452 "balance": "0xc097ce7bc90715b34b9f1000000000"
2453 },
2454 "2d389075be5be9f2246ad654ce152cf05990b209": {
2455 "balance": "0xc097ce7bc90715b34b9f1000000000"
2456 },
2457 "3ae75c08b4c907eb63a8960c45b86e1e9ab6123c": {
2458 "balance": "0xc097ce7bc90715b34b9f1000000000"
2459 },
2460 "4340ee1b812acb40a1eb561c019c327b243b92df": {
2461 "balance": "0xc097ce7bc90715b34b9f1000000000"
2462 },
2463 "4a0f1452281bcec5bd90c3dce6162a5995bfe9df": {
2464 "balance": "0xc097ce7bc90715b34b9f1000000000"
2465 },
2466 "4dde844b71bcdf95512fb4dc94e84fb67b512ed8": {
2467 "balance": "0xc097ce7bc90715b34b9f1000000000"
2468 },
2469 "5f552da00dfb4d3749d9e62dcee3c918855a86a0": {
2470 "balance": "0xc097ce7bc90715b34b9f1000000000"
2471 },
2472 "654aa64f5fbefb84c270ec74211b81ca8c44a72e": {
2473 "balance": "0xc097ce7bc90715b34b9f1000000000"
2474 },
2475 "717f8aa2b982bee0e29f573d31df288663e1ce16": {
2476 "balance": "0xc097ce7bc90715b34b9f1000000000"
2477 },
2478 "7435ed30a8b4aeb0877cef0c6e8cffe834eb865f": {
2479 "balance": "0xc097ce7bc90715b34b9f1000000000"
2480 },
2481 "83c7e323d189f18725ac510004fdc2941f8c4a78": {
2482 "balance": "0xc097ce7bc90715b34b9f1000000000"
2483 },
2484 "84e75c28348fb86acea1a93a39426d7d60f4cc46": {
2485 "balance": "0xc097ce7bc90715b34b9f1000000000"
2486 },
2487 "8bebc8ba651aee624937e7d897853ac30c95a067": {
2488 "storage": {
2489 "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001",
2490 "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000002",
2491 "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000003"
2492 },
2493 "balance": "0x1",
2494 "nonce": "0x1"
2495 },
2496 "c7b99a164efd027a93f147376cc7da7c67c6bbe0": {
2497 "balance": "0xc097ce7bc90715b34b9f1000000000"
2498 },
2499 "d803681e487e6ac18053afc5a6cd813c86ec3e4d": {
2500 "balance": "0xc097ce7bc90715b34b9f1000000000"
2501 },
2502 "e7d13f7aa2a838d24c59b40186a0aca1e21cffcc": {
2503 "balance": "0xc097ce7bc90715b34b9f1000000000"
2504 },
2505 "eda8645ba6948855e3b3cd596bbb07596d59c603": {
2506 "balance": "0xc097ce7bc90715b34b9f1000000000"
2507 }
2508 },
2509 "number": "0x0",
2510 "gasUsed": "0x0",
2511 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2512 "baseFeePerGas": null,
2513 "excessBlobGas": null,
2514 "blobGasUsed": null
2515 }
2516 "#;
2517
2518 let genesis: Genesis = serde_json::from_str(hive_paris).unwrap();
2520 let chainspec = ChainSpec::from(genesis);
2521
2522 let expected_forkid = ForkId { hash: ForkHash(hex!("0xbc0c2605")), next: 0 };
2524 let got_forkid =
2525 chainspec.fork_id(&Head { number: 73, timestamp: 840, ..Default::default() });
2526
2527 assert_eq!(got_forkid, expected_forkid);
2529 assert_eq!(chainspec.paris_block_and_final_difficulty, Some((72, U256::from(9454784))));
2531 }
2532
2533 #[test]
2534 fn test_parse_genesis_json() {
2535 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"}"#;
2536 let genesis: Genesis = serde_json::from_str(s).unwrap();
2537 let acc = genesis
2538 .alloc
2539 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2540 .unwrap();
2541 assert_eq!(acc.balance, U256::from(1));
2542 assert_eq!(genesis.base_fee_per_gas, Some(0x1337));
2543 }
2544
2545 #[test]
2546 fn test_parse_cancun_genesis_json() {
2547 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"}"#;
2548 let genesis: Genesis = serde_json::from_str(s).unwrap();
2549 let acc = genesis
2550 .alloc
2551 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2552 .unwrap();
2553 assert_eq!(acc.balance, U256::from(1));
2554 assert_eq!(genesis.config.cancun_time, Some(4661));
2556 }
2557
2558 #[test]
2559 fn test_parse_prague_genesis_all_formats() {
2560 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"}"#;
2561 let genesis: Genesis = serde_json::from_str(s).unwrap();
2562
2563 let acc = genesis
2565 .alloc
2566 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2567 .unwrap();
2568 assert_eq!(acc.balance, U256::from(1));
2569 assert_eq!(genesis.config.cancun_time, Some(4661));
2571 assert_eq!(genesis.config.prague_time, Some(4662));
2573 }
2574
2575 #[test]
2576 fn test_parse_cancun_genesis_all_formats() {
2577 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"}"#;
2578 let genesis: Genesis = serde_json::from_str(s).unwrap();
2579
2580 let acc = genesis
2582 .alloc
2583 .get(&"0xaa00000000000000000000000000000000000000".parse::<Address>().unwrap())
2584 .unwrap();
2585 assert_eq!(acc.balance, U256::from(1));
2586 assert_eq!(genesis.config.cancun_time, Some(4661));
2588 }
2589
2590 #[test]
2591 fn test_paris_block_and_total_difficulty() {
2592 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2593 let paris_chainspec = ChainSpecBuilder::default()
2594 .chain(Chain::from_id(1337))
2595 .genesis(genesis)
2596 .paris_activated()
2597 .build();
2598 assert_eq!(paris_chainspec.paris_block_and_final_difficulty, Some((0, U256::ZERO)));
2599 }
2600
2601 #[test]
2602 fn test_default_cancun_header_forkhash() {
2603 let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() };
2605 let default_chainspec = ChainSpecBuilder::default()
2606 .chain(Chain::from_id(1337))
2607 .genesis(genesis)
2608 .cancun_activated()
2609 .build();
2610 let mut header = default_chainspec.genesis_header().clone();
2611
2612 header.state_root =
2614 B256::from_str("0x62e2595e017f0ca23e08d17221010721a71c3ae932f4ea3cb12117786bb392d4")
2615 .unwrap();
2616
2617 assert_eq!(header.withdrawals_root, Some(EMPTY_WITHDRAWALS));
2619
2620 assert_eq!(header.parent_beacon_block_root, Some(B256::ZERO));
2623 assert_eq!(header.blob_gas_used, Some(0));
2624 assert_eq!(header.excess_blob_gas, Some(0));
2625
2626 let genesis_hash = header.hash_slow();
2628 let expected_hash =
2629 b256!("0x16bb7c59613a5bad3f7c04a852fd056545ade2483968d9a25a1abb05af0c4d37");
2630 assert_eq!(genesis_hash, expected_hash);
2631
2632 let expected_forkhash = ForkHash(hex!("0x8062457a"));
2634 assert_eq!(ForkHash::from(genesis_hash), expected_forkhash);
2635 }
2636
2637 #[test]
2638 fn holesky_paris_activated_at_genesis() {
2639 assert!(HOLESKY
2640 .fork(EthereumHardfork::Paris)
2641 .active_at_ttd(HOLESKY.genesis.difficulty, HOLESKY.genesis.difficulty));
2642 }
2643
2644 #[test]
2645 fn test_genesis_format_deserialization() {
2646 let config = ChainConfig {
2648 chain_id: 2600,
2649 homestead_block: Some(0),
2650 eip150_block: Some(0),
2651 eip155_block: Some(0),
2652 eip158_block: Some(0),
2653 byzantium_block: Some(0),
2654 constantinople_block: Some(0),
2655 petersburg_block: Some(0),
2656 istanbul_block: Some(0),
2657 berlin_block: Some(0),
2658 london_block: Some(0),
2659 shanghai_time: Some(0),
2660 terminal_total_difficulty: Some(U256::ZERO),
2661 terminal_total_difficulty_passed: true,
2662 ..Default::default()
2663 };
2664 let genesis = Genesis {
2666 config,
2667 nonce: 0,
2668 timestamp: 1698688670,
2669 gas_limit: 5000,
2670 difficulty: U256::ZERO,
2671 mix_hash: B256::ZERO,
2672 coinbase: Address::ZERO,
2673 ..Default::default()
2674 };
2675
2676 let address = hex!("0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b").into();
2678 let account = GenesisAccount::default().with_balance(U256::from(33));
2679 let genesis = genesis.extend_accounts(HashMap::from([(address, account)]));
2680
2681 let serialized_genesis = serde_json::to_string(&genesis).unwrap();
2683 let deserialized_genesis: Genesis = serde_json::from_str(&serialized_genesis).unwrap();
2684
2685 assert_eq!(genesis, deserialized_genesis);
2686 }
2687
2688 #[test]
2689 fn check_fork_id_chainspec_with_fork_condition_never() {
2690 let spec: ChainSpec = ChainSpec {
2691 chain: Chain::mainnet(),
2692 genesis: Genesis::default(),
2693 hardforks: ChainHardforks::new(vec![(
2694 EthereumHardfork::Frontier.boxed(),
2695 ForkCondition::Never,
2696 )]),
2697 paris_block_and_final_difficulty: None,
2698 deposit_contract: None,
2699 ..Default::default()
2700 };
2701
2702 assert_eq!(spec.hardfork_fork_id(EthereumHardfork::Frontier), None);
2703 }
2704
2705 #[test]
2706 fn check_fork_filter_chainspec_with_fork_condition_never() {
2707 let spec: ChainSpec = ChainSpec {
2708 chain: Chain::mainnet(),
2709 genesis: Genesis::default(),
2710 hardforks: ChainHardforks::new(vec![(
2711 EthereumHardfork::Shanghai.boxed(),
2712 ForkCondition::Never,
2713 )]),
2714 paris_block_and_final_difficulty: None,
2715 deposit_contract: None,
2716 ..Default::default()
2717 };
2718
2719 assert_eq!(spec.hardfork_fork_filter(EthereumHardfork::Shanghai), None);
2720 }
2721
2722 #[test]
2723 fn latest_eth_mainnet_fork_id() {
2724 assert_eq!(ForkId { hash: ForkHash(hex!("0x07c9462e")), next: 0 }, MAINNET.latest_fork_id())
2726 }
2727
2728 #[test]
2729 fn latest_hoodi_mainnet_fork_id() {
2730 assert_eq!(ForkId { hash: ForkHash(hex!("0x23aa1351")), next: 0 }, HOODI.latest_fork_id())
2732 }
2733
2734 #[test]
2735 fn latest_holesky_mainnet_fork_id() {
2736 assert_eq!(ForkId { hash: ForkHash(hex!("0x9bc6cb31")), next: 0 }, HOLESKY.latest_fork_id())
2738 }
2739
2740 #[test]
2741 fn latest_sepolia_mainnet_fork_id() {
2742 assert_eq!(ForkId { hash: ForkHash(hex!("0x268956b6")), next: 0 }, SEPOLIA.latest_fork_id())
2744 }
2745
2746 #[test]
2747 fn test_fork_order_ethereum_mainnet() {
2748 let genesis = Genesis {
2749 config: ChainConfig {
2750 chain_id: 0,
2751 homestead_block: Some(0),
2752 dao_fork_block: Some(0),
2753 dao_fork_support: false,
2754 eip150_block: Some(0),
2755 eip155_block: Some(0),
2756 eip158_block: Some(0),
2757 byzantium_block: Some(0),
2758 constantinople_block: Some(0),
2759 petersburg_block: Some(0),
2760 istanbul_block: Some(0),
2761 muir_glacier_block: Some(0),
2762 berlin_block: Some(0),
2763 london_block: Some(0),
2764 arrow_glacier_block: Some(0),
2765 gray_glacier_block: Some(0),
2766 merge_netsplit_block: Some(0),
2767 shanghai_time: Some(0),
2768 cancun_time: Some(0),
2769 terminal_total_difficulty: Some(U256::ZERO),
2770 ..Default::default()
2771 },
2772 ..Default::default()
2773 };
2774
2775 let chain_spec: ChainSpec = genesis.into();
2776
2777 let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect();
2778 let expected_hardforks = vec![
2779 EthereumHardfork::Frontier.boxed(),
2780 EthereumHardfork::Homestead.boxed(),
2781 EthereumHardfork::Dao.boxed(),
2782 EthereumHardfork::Tangerine.boxed(),
2783 EthereumHardfork::SpuriousDragon.boxed(),
2784 EthereumHardfork::Byzantium.boxed(),
2785 EthereumHardfork::Constantinople.boxed(),
2786 EthereumHardfork::Petersburg.boxed(),
2787 EthereumHardfork::Istanbul.boxed(),
2788 EthereumHardfork::MuirGlacier.boxed(),
2789 EthereumHardfork::Berlin.boxed(),
2790 EthereumHardfork::London.boxed(),
2791 EthereumHardfork::ArrowGlacier.boxed(),
2792 EthereumHardfork::GrayGlacier.boxed(),
2793 EthereumHardfork::Paris.boxed(),
2794 EthereumHardfork::Shanghai.boxed(),
2795 EthereumHardfork::Cancun.boxed(),
2796 ];
2797
2798 assert!(expected_hardforks
2799 .iter()
2800 .zip(hardforks.iter())
2801 .all(|(expected, actual)| &**expected == *actual));
2802 assert_eq!(expected_hardforks.len(), hardforks.len());
2803 }
2804
2805 #[test]
2806 fn test_calc_base_block_reward() {
2807 let cases = [
2809 ((0, U256::ZERO), Some(ETH_TO_WEI * 5)),
2811 ((4370000, U256::ZERO), Some(ETH_TO_WEI * 3)),
2813 ((7280000, U256::ZERO), Some(ETH_TO_WEI * 2)),
2815 ((15537394, U256::from(58_750_000_000_000_000_000_000_u128)), None),
2817 ];
2818
2819 for ((block_number, _td), expected_reward) in cases {
2820 assert_eq!(base_block_reward(&*MAINNET, block_number), expected_reward);
2821 }
2822 }
2823
2824 #[test]
2825 fn test_calc_full_block_reward() {
2826 let base_reward = ETH_TO_WEI;
2827 let one_thirty_twoth_reward = base_reward >> 5;
2828
2829 let cases = [
2831 (0, base_reward),
2832 (1, base_reward + one_thirty_twoth_reward),
2833 (2, base_reward + one_thirty_twoth_reward * 2),
2834 ];
2835
2836 for (num_ommers, expected_reward) in cases {
2837 assert_eq!(block_reward(base_reward, num_ommers), expected_reward);
2838 }
2839 }
2840
2841 #[test]
2842 fn blob_params_from_genesis() {
2843 let s = r#"{
2844 "blobSchedule": {
2845 "cancun":{
2846 "baseFeeUpdateFraction":3338477,
2847 "max":6,
2848 "target":3
2849 },
2850 "prague":{
2851 "baseFeeUpdateFraction":3338477,
2852 "max":6,
2853 "target":3
2854 }
2855 }
2856 }"#;
2857 let config: ChainConfig = serde_json::from_str(s).unwrap();
2858 let hardfork_params = config.blob_schedule_blob_params();
2859 let expected = BlobScheduleBlobParams {
2860 cancun: BlobParams {
2861 target_blob_count: 3,
2862 max_blob_count: 6,
2863 update_fraction: 3338477,
2864 min_blob_fee: BLOB_TX_MIN_BLOB_GASPRICE,
2865 max_blobs_per_tx: 6,
2866 blob_base_cost: 0,
2867 },
2868 prague: BlobParams {
2869 target_blob_count: 3,
2870 max_blob_count: 6,
2871 update_fraction: 3338477,
2872 min_blob_fee: BLOB_TX_MIN_BLOB_GASPRICE,
2873 max_blobs_per_tx: 6,
2874 blob_base_cost: 0,
2875 },
2876 ..Default::default()
2877 };
2878 assert_eq!(hardfork_params, expected);
2879 }
2880
2881 #[test]
2882 fn parse_perf_net_genesis() {
2883 let s = r#"{
2884 "config": {
2885 "chainId": 1,
2886 "homesteadBlock": 1150000,
2887 "daoForkBlock": 1920000,
2888 "daoForkSupport": true,
2889 "eip150Block": 2463000,
2890 "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
2891 "eip155Block": 2675000,
2892 "eip158Block": 2675000,
2893 "byzantiumBlock": 4370000,
2894 "constantinopleBlock": 7280000,
2895 "petersburgBlock": 7280000,
2896 "istanbulBlock": 9069000,
2897 "muirGlacierBlock": 9200000,
2898 "berlinBlock": 12244000,
2899 "londonBlock": 12965000,
2900 "arrowGlacierBlock": 13773000,
2901 "grayGlacierBlock": 15050000,
2902 "terminalTotalDifficulty": 58750000000000000000000,
2903 "terminalTotalDifficultyPassed": true,
2904 "shanghaiTime": 1681338455,
2905 "cancunTime": 1710338135,
2906 "pragueTime": 1746612311,
2907 "ethash": {},
2908 "depositContractAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa",
2909 "blobSchedule": {
2910 "cancun": {
2911 "target": 3,
2912 "max": 6,
2913 "baseFeeUpdateFraction": 3338477
2914 },
2915 "prague": {
2916 "target": 6,
2917 "max": 9,
2918 "baseFeeUpdateFraction": 5007716
2919 }
2920 }
2921 },
2922 "nonce": "0x42",
2923 "timestamp": "0x0",
2924 "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
2925 "gasLimit": "0x1388",
2926 "difficulty": "0x400000000",
2927 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2928 "coinbase": "0x0000000000000000000000000000000000000000",
2929 "number": "0x0",
2930 "gasUsed": "0x0",
2931 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
2932 "baseFeePerGas": null
2933}"#;
2934
2935 let genesis = serde_json::from_str::<Genesis>(s).unwrap();
2936 let chainspec = ChainSpec::from_genesis(genesis);
2937 let activation = chainspec.hardforks.fork(EthereumHardfork::Paris);
2938 assert_eq!(
2939 activation,
2940 ForkCondition::TTD {
2941 activation_block_number: MAINNET_PARIS_BLOCK,
2942 total_difficulty: MAINNET_PARIS_TTD,
2943 fork_block: None,
2944 }
2945 )
2946 }
2947}