diff --git a/crates/chainspec/src/api.rs b/crates/chainspec/src/api.rs index b8d260b59..dbf3a00be 100644 --- a/crates/chainspec/src/api.rs +++ b/crates/chainspec/src/api.rs @@ -1,7 +1,6 @@ -use core::fmt::Debug; - use crate::ChainSpec; use alloy_chains::Chain; +use core::fmt::Debug; /// Trait representing type configuring a chain spec. pub trait EthChainSpec: Send + Sync + Unpin + Debug + 'static { diff --git a/crates/chainspec/src/lib.rs b/crates/chainspec/src/lib.rs index 62ea1aa9b..42e5f08fe 100644 --- a/crates/chainspec/src/lib.rs +++ b/crates/chainspec/src/lib.rs @@ -33,6 +33,13 @@ pub use spec::{ DepositContract, ForkBaseFeeParams, DEV, HOLESKY, MAINNET, SEPOLIA, }; +/// Simple utility to create a `OnceCell` with a value set. +pub fn once_cell_set(value: T) -> once_cell::sync::OnceCell { + let once = once_cell::sync::OnceCell::new(); + let _ = once.set(value); + once +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/chainspec/src/spec.rs b/crates/chainspec/src/spec.rs index eb25d0e5b..4215c50f2 100644 --- a/crates/chainspec/src/spec.rs +++ b/crates/chainspec/src/spec.rs @@ -1,12 +1,11 @@ +use crate::{constants::MAINNET_DEPOSIT_CONTRACT, once_cell_set, EthChainSpec}; use alloc::{boxed::Box, sync::Arc, vec::Vec}; -pub use alloy_eips::eip1559::BaseFeeParams; - use alloy_chains::{Chain, ChainKind, NamedChain}; use alloy_genesis::Genesis; use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256}; use alloy_trie::EMPTY_ROOT_HASH; use derive_more::From; -use once_cell::sync::Lazy; +use once_cell::sync::{Lazy, OnceCell}; #[cfg(feature = "optimism")] use reth_ethereum_forks::OptimismHardfork; use reth_ethereum_forks::{ @@ -26,7 +25,7 @@ use reth_primitives_traits::{ }; use reth_trie_common::root::state_root_ref_unhashed; -use crate::{constants::MAINNET_DEPOSIT_CONTRACT, EthChainSpec}; +pub use alloy_eips::eip1559::BaseFeeParams; /// The Ethereum mainnet spec pub static MAINNET: Lazy> = Lazy::new(|| { @@ -34,7 +33,8 @@ pub static MAINNET: Lazy> = Lazy::new(|| { chain: Chain::mainnet(), genesis: serde_json::from_str(include_str!("../res/genesis/mainnet.json")) .expect("Can't deserialize Mainnet genesis json"), - genesis_hash: Some(MAINNET_GENESIS_HASH), + genesis_hash: once_cell_set(MAINNET_GENESIS_HASH), + genesis_header: Default::default(), // paris_block_and_final_difficulty: Some(( 15537394, @@ -61,7 +61,8 @@ pub static SEPOLIA: Lazy> = Lazy::new(|| { chain: Chain::sepolia(), genesis: serde_json::from_str(include_str!("../res/genesis/sepolia.json")) .expect("Can't deserialize Sepolia genesis json"), - genesis_hash: Some(SEPOLIA_GENESIS_HASH), + genesis_hash: once_cell_set(SEPOLIA_GENESIS_HASH), + genesis_header: Default::default(), // paris_block_and_final_difficulty: Some((1450409, U256::from(17_000_018_015_853_232u128))), hardforks: EthereumHardfork::sepolia().into(), @@ -85,7 +86,8 @@ pub static HOLESKY: Lazy> = Lazy::new(|| { chain: Chain::holesky(), genesis: serde_json::from_str(include_str!("../res/genesis/holesky.json")) .expect("Can't deserialize Holesky genesis json"), - genesis_hash: Some(HOLESKY_GENESIS_HASH), + genesis_hash: once_cell_set(HOLESKY_GENESIS_HASH), + genesis_header: Default::default(), paris_block_and_final_difficulty: Some((0, U256::from(1))), hardforks: EthereumHardfork::holesky().into(), deposit_contract: Some(DepositContract::new( @@ -110,7 +112,7 @@ pub static DEV: Lazy> = Lazy::new(|| { chain: Chain::dev(), genesis: serde_json::from_str(include_str!("../res/genesis/dev.json")) .expect("Can't deserialize Dev testnet genesis json"), - genesis_hash: Some(DEV_GENESIS_HASH), + genesis_hash: once_cell_set(DEV_GENESIS_HASH), paris_block_and_final_difficulty: Some((0, U256::from(0))), hardforks: DEV_HARDFORKS.clone(), base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), @@ -174,14 +176,20 @@ pub struct ChainSpec { /// The chain ID pub chain: Chain, + /// The genesis block. + pub genesis: Genesis, + /// The hash of the genesis block. /// - /// This acts as a small cache for known chains. If the chain is known, then the genesis hash - /// is also known ahead of time, and this will be `Some`. - pub genesis_hash: Option, + /// This is either stored at construction time if it is known using [`once_cell_set`], or + /// computed once on the first access. + pub genesis_hash: OnceCell, - /// The genesis block - pub genesis: Genesis, + /// The header corresponding to the genesis block. + /// + /// This is either stored at construction time if it is known using [`once_cell_set`], or + /// computed once on the first access. + pub genesis_header: OnceCell
, /// The block at which [`EthereumHardfork::Paris`] was activated and the final difficulty at /// this block. @@ -209,6 +217,7 @@ impl Default for ChainSpec { chain: Default::default(), genesis_hash: Default::default(), genesis: Default::default(), + genesis_header: Default::default(), paris_block_and_final_difficulty: Default::default(), hardforks: Default::default(), deposit_contract: Default::default(), @@ -271,7 +280,11 @@ impl ChainSpec { } /// Get the header for the genesis block. - pub fn genesis_header(&self) -> Header { + pub fn genesis_header(&self) -> &Header { + self.genesis_header.get_or_init(|| self.make_genesis_header()) + } + + fn make_genesis_header(&self) -> Header { // If London is activated at genesis, we set the initial base fee as per EIP-1559. let base_fee_per_gas = self.initial_base_fee(); @@ -323,7 +336,7 @@ impl ChainSpec { /// Get the sealed header for the genesis block. pub fn sealed_genesis_header(&self) -> SealedHeader { - SealedHeader::new(self.genesis_header(), self.genesis_hash()) + SealedHeader::new(self.genesis_header().clone(), self.genesis_hash()) } /// Get the initial base fee of the genesis block. @@ -376,7 +389,7 @@ impl ChainSpec { /// Get the hash of the genesis block. pub fn genesis_hash(&self) -> B256 { - self.genesis_hash.unwrap_or_else(|| self.genesis_header().hash_slow()) + *self.genesis_hash.get_or_init(|| self.genesis_header().hash_slow()) } /// Get the timestamp of the genesis block. @@ -694,7 +707,7 @@ impl From for ChainSpec { Self { chain: genesis.config.chain_id.into(), genesis, - genesis_hash: None, + genesis_hash: OnceCell::new(), hardforks: ChainHardforks::new(ordered_hardforks), paris_block_and_final_difficulty, deposit_contract, @@ -939,7 +952,7 @@ impl ChainSpecBuilder { ChainSpec { chain: self.chain.expect("The chain is required"), genesis: self.genesis.expect("The genesis is required"), - genesis_hash: None, + genesis_hash: OnceCell::new(), hardforks: self.hardforks, paris_block_and_final_difficulty, deposit_contract: None, @@ -1921,7 +1934,7 @@ Post-merge hard forks (timestamp based): assert_eq!(&alloy_rlp::encode(TrieAccount::from(account.clone())), expected_rlp); } - assert_eq!(chainspec.genesis_hash, None); + assert_eq!(chainspec.genesis_hash.get(), None); let expected_state_root: B256 = hex!("078dc6061b1d8eaa8493384b59c9c65ceb917201221d08b80c4de6770b6ec7e7").into(); assert_eq!(chainspec.genesis_header().state_root, expected_state_root); @@ -1996,7 +2009,7 @@ Post-merge hard forks (timestamp based): let genesis = serde_json::from_str::(hive_json).unwrap(); let chainspec: ChainSpec = genesis.into(); - assert_eq!(chainspec.genesis_hash, None); + assert_eq!(chainspec.genesis_hash.get(), None); assert_eq!(chainspec.chain, Chain::from_named(NamedChain::Optimism)); let expected_state_root: B256 = hex!("9a6049ac535e3dc7436c189eaa81c73f35abd7f282ab67c32944ff0301d63360").into(); @@ -2228,7 +2241,7 @@ Post-merge hard forks (timestamp based): .genesis(genesis) .cancun_activated() .build(); - let mut header = default_chainspec.genesis_header(); + let mut header = default_chainspec.genesis_header().clone(); // set the state root to the same as in the hive test the hash was pulled from header.state_root = @@ -2311,7 +2324,7 @@ Post-merge hard forks (timestamp based): let spec = ChainSpec { chain: Chain::mainnet(), genesis: Genesis::default(), - genesis_hash: None, + genesis_hash: OnceCell::new(), hardforks: ChainHardforks::new(vec![( EthereumHardfork::Frontier.boxed(), ForkCondition::Never, @@ -2329,7 +2342,7 @@ Post-merge hard forks (timestamp based): let spec = ChainSpec { chain: Chain::mainnet(), genesis: Genesis::default(), - genesis_hash: None, + genesis_hash: OnceCell::new(), hardforks: ChainHardforks::new(vec![( EthereumHardfork::Shanghai.boxed(), ForkCondition::Never, diff --git a/crates/cli/commands/src/common.rs b/crates/cli/commands/src/common.rs index 2f24dbca2..3b4a00b2d 100644 --- a/crates/cli/commands/src/common.rs +++ b/crates/cli/commands/src/common.rs @@ -94,7 +94,7 @@ impl> EnvironmentArgs { let provider_factory = self.create_provider_factory(&config, db, sfp)?; if access.is_read_write() { debug!(target: "reth::cli", chain=%self.chain.chain, genesis=?self.chain.genesis_hash(), "Initializing genesis"); - init_genesis(provider_factory.clone())?; + init_genesis(&provider_factory)?; } Ok(Environment { config, provider_factory, data_dir }) diff --git a/crates/cli/commands/src/stage/drop.rs b/crates/cli/commands/src/stage/drop.rs index ce1791506..847611968 100644 --- a/crates/cli/commands/src/stage/drop.rs +++ b/crates/cli/commands/src/stage/drop.rs @@ -73,7 +73,7 @@ impl> Command { StageId::Headers.to_string(), Default::default(), )?; - insert_genesis_header(&provider_rw, &static_file_provider, self.env.chain)?; + insert_genesis_header(&provider_rw, &static_file_provider, &self.env.chain)?; } StageEnum::Bodies => { tx.clear::()?; @@ -86,7 +86,7 @@ impl> Command { StageId::Bodies.to_string(), Default::default(), )?; - insert_genesis_header(&provider_rw, &static_file_provider, self.env.chain)?; + insert_genesis_header(&provider_rw, &static_file_provider, &self.env.chain)?; } StageEnum::Senders => { tx.clear::()?; @@ -173,7 +173,7 @@ impl> Command { StageId::TransactionLookup.to_string(), Default::default(), )?; - insert_genesis_header(&provider_rw, &static_file_provider, self.env.chain)?; + insert_genesis_header(&provider_rw, &static_file_provider, &self.env.chain)?; } } diff --git a/crates/consensus/beacon/src/engine/test_utils.rs b/crates/consensus/beacon/src/engine/test_utils.rs index 98574c0e3..72e5d825f 100644 --- a/crates/consensus/beacon/src/engine/test_utils.rs +++ b/crates/consensus/beacon/src/engine/test_utils.rs @@ -392,7 +392,7 @@ where BlockchainTree::new(externals, BlockchainTreeConfig::new(1, 2, 3, 2)) .expect("failed to create tree"), )); - let genesis_block = self.base_config.chain_spec.genesis_header().seal_slow(); + let genesis_block = self.base_config.chain_spec.genesis_header().clone().seal_slow(); let blockchain_provider = BlockchainProvider::with_blocks(provider_factory.clone(), tree, genesis_block, None); diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index 11b2e5170..b20075505 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -2691,7 +2691,7 @@ mod tests { let (from_tree_tx, from_tree_rx) = unbounded_channel(); - let header = chain_spec.genesis_header().seal_slow(); + let header = chain_spec.genesis_header().clone().seal_slow(); let engine_api_tree_state = EngineApiTreeState::new(10, 10, header.num_hash()); let canonical_in_memory_state = CanonicalInMemoryState::with_head(header, None); diff --git a/crates/ethereum/engine-primitives/src/payload.rs b/crates/ethereum/engine-primitives/src/payload.rs index 0bb4da1c2..be8cf9314 100644 --- a/crates/ethereum/engine-primitives/src/payload.rs +++ b/crates/ethereum/engine-primitives/src/payload.rs @@ -417,7 +417,7 @@ mod tests { // use cfg_and_block_env let cfg_and_block_env = - payload_builder_attributes.cfg_and_block_env(&chainspec, &chainspec.genesis_header()); + payload_builder_attributes.cfg_and_block_env(&chainspec, chainspec.genesis_header()); // ensure the base fee is non zero assert_eq!(cfg_and_block_env.1.basefee, U256::from(EIP1559_INITIAL_BASE_FEE)); diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index 9231e7b89..67709ca2e 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -814,7 +814,7 @@ mod tests { .build(), ); - let mut header = chain_spec.genesis_header(); + let mut header = chain_spec.genesis_header().clone(); let provider = executor_provider(chain_spec); let mut executor = provider.batch_executor(StateProviderDatabase::new(&db)); @@ -1036,7 +1036,7 @@ mod tests { .build(), ); - let header = chain_spec.genesis_header(); + let header = chain_spec.genesis_header().clone(); let provider = executor_provider(chain_spec); let mut executor = provider.batch_executor(StateProviderDatabase::new(&db)); @@ -1207,7 +1207,7 @@ mod tests { .build(), ); - let mut header = chain_spec.genesis_header(); + let mut header = chain_spec.genesis_header().clone(); header.requests_root = Some(EMPTY_ROOT_HASH); let header_hash = header.hash_slow(); @@ -1365,7 +1365,7 @@ mod tests { let input: Bytes = [&validator_public_key[..], &withdrawal_amount[..]].concat().into(); assert_eq!(input.len(), 56); - let mut header = chain_spec.genesis_header(); + let mut header = chain_spec.genesis_header().clone(); header.gas_limit = 1_500_000; header.gas_used = 134_807; header.receipts_root = @@ -1454,7 +1454,7 @@ mod tests { assert_eq!(input.len(), 56); // Create a genesis block header with a specified gas limit and gas used - let mut header = chain_spec.genesis_header(); + let mut header = chain_spec.genesis_header().clone(); header.gas_limit = 1_500_000; header.gas_used = 134_807; header.receipts_root = diff --git a/crates/exex/exex/src/backfill/job.rs b/crates/exex/exex/src/backfill/job.rs index 385b04976..61b1db86c 100644 --- a/crates/exex/exex/src/backfill/job.rs +++ b/crates/exex/exex/src/backfill/job.rs @@ -252,7 +252,7 @@ mod tests { let executor = EthExecutorProvider::ethereum(chain_spec.clone()); let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone()); - init_genesis(provider_factory.clone())?; + init_genesis(&provider_factory)?; let blockchain_db = BlockchainProvider::new( provider_factory.clone(), Arc::new(NoopBlockchainTree::default()), @@ -291,7 +291,7 @@ mod tests { let executor = EthExecutorProvider::ethereum(chain_spec.clone()); let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone()); - init_genesis(provider_factory.clone())?; + init_genesis(&provider_factory)?; let blockchain_db = BlockchainProvider::new( provider_factory.clone(), Arc::new(NoopBlockchainTree::default()), diff --git a/crates/exex/exex/src/backfill/stream.rs b/crates/exex/exex/src/backfill/stream.rs index 5b72dad38..6b6b6f0f8 100644 --- a/crates/exex/exex/src/backfill/stream.rs +++ b/crates/exex/exex/src/backfill/stream.rs @@ -202,7 +202,7 @@ mod tests { let executor = EthExecutorProvider::ethereum(chain_spec.clone()); let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone()); - init_genesis(provider_factory.clone())?; + init_genesis(&provider_factory)?; let blockchain_db = BlockchainProvider::new( provider_factory.clone(), Arc::new(NoopBlockchainTree::default()), @@ -243,7 +243,7 @@ mod tests { let executor = EthExecutorProvider::ethereum(chain_spec.clone()); let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone()); - init_genesis(provider_factory.clone())?; + init_genesis(&provider_factory)?; let blockchain_db = BlockchainProvider::new( provider_factory.clone(), Arc::new(NoopBlockchainTree::default()), diff --git a/crates/exex/test-utils/src/lib.rs b/crates/exex/test-utils/src/lib.rs index b93c263a4..1d9d18039 100644 --- a/crates/exex/test-utils/src/lib.rs +++ b/crates/exex/test-utils/src/lib.rs @@ -250,7 +250,7 @@ pub async fn test_exex_context_with_chain_spec( StaticFileProvider::read_write(static_dir.into_path()).expect("static file provider"), ); - let genesis_hash = init_genesis(provider_factory.clone())?; + let genesis_hash = init_genesis(&provider_factory)?; let provider = BlockchainProvider::new(provider_factory.clone(), Arc::new(NoopBlockchainTree::default()))?; diff --git a/crates/node/builder/src/launch/common.rs b/crates/node/builder/src/launch/common.rs index 7f3fce644..f6c488a00 100644 --- a/crates/node/builder/src/launch/common.rs +++ b/crates/node/builder/src/launch/common.rs @@ -522,13 +522,13 @@ where /// Convenience function to [`Self::init_genesis`] pub fn with_genesis(self) -> Result { - init_genesis(self.provider_factory().clone())?; + init_genesis(self.provider_factory())?; Ok(self) } /// Write the genesis block and state if it has not already been written pub fn init_genesis(&self) -> Result { - init_genesis(self.provider_factory().clone()) + init_genesis(self.provider_factory()) } /// Creates a new `WithMeteredProvider` container and attaches it to the diff --git a/crates/optimism/chainspec/src/base.rs b/crates/optimism/chainspec/src/base.rs index 436fdaabe..6b13b2708 100644 --- a/crates/optimism/chainspec/src/base.rs +++ b/crates/optimism/chainspec/src/base.rs @@ -5,7 +5,7 @@ use alloc::sync::Arc; use alloy_chains::Chain; use alloy_primitives::{b256, U256}; use once_cell::sync::Lazy; -use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; +use reth_chainspec::{once_cell_set, BaseFeeParams, BaseFeeParamsKind, ChainSpec}; use reth_ethereum_forks::{EthereumHardfork, OptimismHardfork}; use crate::OpChainSpec; @@ -17,7 +17,7 @@ pub static BASE_MAINNET: Lazy> = Lazy::new(|| { chain: Chain::base_mainnet(), genesis: serde_json::from_str(include_str!("../res/genesis/base.json")) .expect("Can't deserialize Base genesis json"), - genesis_hash: Some(b256!( + genesis_hash: once_cell_set(b256!( "f712aa9241cc24369b143cf6dce85f0902a9731e70d66818a3a5845b296c73dd" )), paris_block_and_final_difficulty: Some((0, U256::from(0))), diff --git a/crates/optimism/chainspec/src/base_sepolia.rs b/crates/optimism/chainspec/src/base_sepolia.rs index 0a52c52fc..932cacc43 100644 --- a/crates/optimism/chainspec/src/base_sepolia.rs +++ b/crates/optimism/chainspec/src/base_sepolia.rs @@ -5,7 +5,7 @@ use alloc::sync::Arc; use alloy_chains::Chain; use alloy_primitives::{b256, U256}; use once_cell::sync::Lazy; -use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; +use reth_chainspec::{once_cell_set, BaseFeeParams, BaseFeeParamsKind, ChainSpec}; use reth_ethereum_forks::{EthereumHardfork, OptimismHardfork}; use crate::OpChainSpec; @@ -17,7 +17,7 @@ pub static BASE_SEPOLIA: Lazy> = Lazy::new(|| { chain: Chain::base_sepolia(), genesis: serde_json::from_str(include_str!("../res/genesis/sepolia_base.json")) .expect("Can't deserialize Base Sepolia genesis json"), - genesis_hash: Some(b256!( + genesis_hash: once_cell_set(b256!( "0dcc9e089e30b90ddfc55be9a37dd15bc551aeee999d2e2b51414c54eaf934e4" )), paris_block_and_final_difficulty: Some((0, U256::from(0))), diff --git a/crates/optimism/chainspec/src/dev.rs b/crates/optimism/chainspec/src/dev.rs index b1d42a94c..96ec0e42e 100644 --- a/crates/optimism/chainspec/src/dev.rs +++ b/crates/optimism/chainspec/src/dev.rs @@ -5,7 +5,7 @@ use alloc::sync::Arc; use alloy_chains::Chain; use alloy_primitives::U256; use once_cell::sync::Lazy; -use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; +use reth_chainspec::{once_cell_set, BaseFeeParams, BaseFeeParamsKind, ChainSpec}; use reth_ethereum_forks::DEV_HARDFORKS; use reth_primitives_traits::constants::DEV_GENESIS_HASH; @@ -16,20 +16,18 @@ use crate::OpChainSpec; /// Includes 20 prefunded accounts with `10_000` ETH each derived from mnemonic "test test test test /// test test test test test test test junk". pub static OP_DEV: Lazy> = Lazy::new(|| { - { - OpChainSpec { - inner: ChainSpec { - chain: Chain::dev(), - genesis: serde_json::from_str(include_str!("../res/genesis/dev.json")) - .expect("Can't deserialize Dev testnet genesis json"), - genesis_hash: Some(DEV_GENESIS_HASH), - paris_block_and_final_difficulty: Some((0, U256::from(0))), - hardforks: DEV_HARDFORKS.clone(), - base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), - deposit_contract: None, // TODO: do we even have? - ..Default::default() - }, - } + OpChainSpec { + inner: ChainSpec { + chain: Chain::dev(), + genesis: serde_json::from_str(include_str!("../res/genesis/dev.json")) + .expect("Can't deserialize Dev testnet genesis json"), + genesis_hash: once_cell_set(DEV_GENESIS_HASH), + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: DEV_HARDFORKS.clone(), + base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), + deposit_contract: None, // TODO: do we even have? + ..Default::default() + }, } .into() }); diff --git a/crates/optimism/chainspec/src/op.rs b/crates/optimism/chainspec/src/op.rs index 14a0b9770..cbd0a0d0b 100644 --- a/crates/optimism/chainspec/src/op.rs +++ b/crates/optimism/chainspec/src/op.rs @@ -5,7 +5,7 @@ use alloc::sync::Arc; use alloy_chains::Chain; use alloy_primitives::{b256, U256}; use once_cell::sync::Lazy; -use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; +use reth_chainspec::{once_cell_set, BaseFeeParams, BaseFeeParamsKind, ChainSpec}; use reth_ethereum_forks::{EthereumHardfork, OptimismHardfork}; use reth_primitives_traits::constants::ETHEREUM_BLOCK_GAS_LIMIT; @@ -20,7 +20,7 @@ pub static OP_MAINNET: Lazy> = Lazy::new(|| { // manually from trusted source genesis: serde_json::from_str(include_str!("../res/genesis/optimism.json")) .expect("Can't deserialize Optimism Mainnet genesis json"), - genesis_hash: Some(b256!( + genesis_hash: once_cell_set(b256!( "7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b" )), paris_block_and_final_difficulty: Some((0, U256::from(0))), diff --git a/crates/optimism/chainspec/src/op_sepolia.rs b/crates/optimism/chainspec/src/op_sepolia.rs index 8c0586360..3d2165f44 100644 --- a/crates/optimism/chainspec/src/op_sepolia.rs +++ b/crates/optimism/chainspec/src/op_sepolia.rs @@ -5,7 +5,7 @@ use alloc::sync::Arc; use alloy_chains::{Chain, NamedChain}; use alloy_primitives::{b256, U256}; use once_cell::sync::Lazy; -use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; +use reth_chainspec::{once_cell_set, BaseFeeParams, BaseFeeParamsKind, ChainSpec}; use reth_ethereum_forks::{EthereumHardfork, OptimismHardfork}; use reth_primitives_traits::constants::ETHEREUM_BLOCK_GAS_LIMIT; @@ -18,7 +18,7 @@ pub static OP_SEPOLIA: Lazy> = Lazy::new(|| { chain: Chain::from_named(NamedChain::OptimismSepolia), genesis: serde_json::from_str(include_str!("../res/genesis/sepolia_op.json")) .expect("Can't deserialize OP Sepolia genesis json"), - genesis_hash: Some(b256!( + genesis_hash: once_cell_set(b256!( "102de6ffb001480cc9b8b548fd05c34cd4f46ae4aa91759393db90ea0409887d" )), paris_block_and_final_difficulty: Some((0, U256::from(0))), diff --git a/crates/optimism/cli/src/commands/import_receipts.rs b/crates/optimism/cli/src/commands/import_receipts.rs index d84296d84..8074fa92f 100644 --- a/crates/optimism/cli/src/commands/import_receipts.rs +++ b/crates/optimism/cli/src/commands/import_receipts.rs @@ -285,7 +285,7 @@ mod test { ChunkedFileReader::from_file(f, DEFAULT_BYTE_LEN_CHUNK_CHAIN_FILE).await.unwrap(); let db = TestStageDB::default(); - init_genesis(db.factory.clone()).unwrap(); + init_genesis(&db.factory).unwrap(); // todo: where does import command init receipts ? probably somewhere in pipeline diff --git a/crates/storage/db-common/src/init.rs b/crates/storage/db-common/src/init.rs index 8ff530d4d..04b789944 100644 --- a/crates/storage/db-common/src/init.rs +++ b/crates/storage/db-common/src/init.rs @@ -23,11 +23,7 @@ use reth_stages_types::{StageCheckpoint, StageId}; use reth_trie::{IntermediateStateRootState, StateRoot as StateRootComputer, StateRootProgress}; use reth_trie_db::DatabaseStateRoot; use serde::{Deserialize, Serialize}; -use std::{ - collections::{BTreeMap, HashMap}, - io::BufRead, - sync::Arc, -}; +use std::{collections::HashMap, io::BufRead}; use tracing::{debug, error, info, trace}; /// Default soft limit for number of bytes to read from state dump file, before inserting into @@ -75,7 +71,7 @@ impl From for InitDatabaseError { /// Write the genesis block if it has not already been written pub fn init_genesis>( - factory: ProviderFactory, + factory: &ProviderFactory, ) -> Result { let chain = factory.chain_spec(); @@ -110,7 +106,7 @@ pub fn init_genesis>( // Insert header let static_file_provider = factory.static_file_provider(); - insert_genesis_header(&provider_rw, &static_file_provider, chain.clone())?; + insert_genesis_header(&provider_rw, &static_file_provider, &chain)?; insert_genesis_state(&provider_rw, alloc.len(), alloc.iter())?; @@ -235,13 +231,7 @@ pub fn insert_genesis_hashes<'a, 'b, DB: Database>( let alloc_storage = alloc.filter_map(|(addr, account)| { // only return Some if there is storage account.storage.as_ref().map(|storage| { - ( - *addr, - storage - .clone() - .into_iter() - .map(|(key, value)| StorageEntry { key, value: value.into() }), - ) + (*addr, storage.iter().map(|(&key, &value)| StorageEntry { key, value: value.into() })) }) }); provider.insert_storage_for_hashing(alloc_storage)?; @@ -265,16 +255,14 @@ pub fn insert_history<'a, 'b, DB: Database>( alloc: impl Iterator + Clone, block: u64, ) -> ProviderResult<()> { - let account_transitions = - alloc.clone().map(|(addr, _)| (*addr, vec![block])).collect::>(); + let account_transitions = alloc.clone().map(|(addr, _)| (*addr, [block])); provider.insert_account_history_index(account_transitions)?; trace!(target: "reth::cli", "Inserted account history"); let storage_transitions = alloc .filter_map(|(addr, account)| account.storage.as_ref().map(|storage| (addr, storage))) - .flat_map(|(addr, storage)| storage.iter().map(|(key, _)| ((*addr, *key), vec![block]))) - .collect::>(); + .flat_map(|(addr, storage)| storage.iter().map(|(key, _)| ((*addr, *key), [block]))); provider.insert_storage_history_index(storage_transitions)?; trace!(target: "reth::cli", "Inserted storage history"); @@ -286,15 +274,15 @@ pub fn insert_history<'a, 'b, DB: Database>( pub fn insert_genesis_header( provider: &DatabaseProviderRW, static_file_provider: &StaticFileProvider, - chain: Arc, + chain: &ChainSpec, ) -> ProviderResult<()> { - let (header, block_hash) = chain.sealed_genesis_header().split(); + let (header, block_hash) = (chain.genesis_header(), chain.genesis_hash()); match static_file_provider.block_hash(0) { Ok(None) | Err(ProviderError::MissingStaticFileBlock(StaticFileSegment::Headers, 0)) => { let (difficulty, hash) = (header.difficulty, block_hash); let mut writer = static_file_provider.latest_writer(StaticFileSegment::Headers)?; - writer.append_header(&header, difficulty, &hash)?; + writer.append_header(header, difficulty, &hash)?; } Ok(Some(_)) => {} Err(e) => return Err(e), @@ -563,6 +551,7 @@ mod tests { use reth_provider::test_utils::{ create_test_provider_factory_with_chain_spec, MockNodeTypesWithDB, }; + use std::{collections::BTreeMap, sync::Arc}; fn collect_table_entries( tx: &::TX, @@ -577,7 +566,7 @@ mod tests { #[test] fn success_init_genesis_mainnet() { let genesis_hash = - init_genesis(create_test_provider_factory_with_chain_spec(MAINNET.clone())).unwrap(); + init_genesis(&create_test_provider_factory_with_chain_spec(MAINNET.clone())).unwrap(); // actual, expected assert_eq!(genesis_hash, MAINNET_GENESIS_HASH); @@ -586,7 +575,7 @@ mod tests { #[test] fn success_init_genesis_sepolia() { let genesis_hash = - init_genesis(create_test_provider_factory_with_chain_spec(SEPOLIA.clone())).unwrap(); + init_genesis(&create_test_provider_factory_with_chain_spec(SEPOLIA.clone())).unwrap(); // actual, expected assert_eq!(genesis_hash, SEPOLIA_GENESIS_HASH); @@ -595,7 +584,7 @@ mod tests { #[test] fn success_init_genesis_holesky() { let genesis_hash = - init_genesis(create_test_provider_factory_with_chain_spec(HOLESKY.clone())).unwrap(); + init_genesis(&create_test_provider_factory_with_chain_spec(HOLESKY.clone())).unwrap(); // actual, expected assert_eq!(genesis_hash, HOLESKY_GENESIS_HASH); @@ -605,10 +594,10 @@ mod tests { fn fail_init_inconsistent_db() { let factory = create_test_provider_factory_with_chain_spec(SEPOLIA.clone()); let static_file_provider = factory.static_file_provider(); - init_genesis(factory.clone()).unwrap(); + init_genesis(&factory).unwrap(); // Try to init db with a different genesis block - let genesis_hash = init_genesis(ProviderFactory::::new( + let genesis_hash = init_genesis(&ProviderFactory::::new( factory.into_db(), MAINNET.clone(), static_file_provider, @@ -647,14 +636,14 @@ mod tests { ..Default::default() }, hardforks: Default::default(), - genesis_hash: None, + genesis_hash: Default::default(), paris_block_and_final_difficulty: None, deposit_contract: None, ..Default::default() }); let factory = create_test_provider_factory_with_chain_spec(chain_spec); - init_genesis(factory.clone()).unwrap(); + init_genesis(&factory).unwrap(); let provider = factory.provider().unwrap(); diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index c2bf4cdf7..0077081b3 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -1658,7 +1658,7 @@ impl DatabaseProvider { /// This function is used by history indexing stages. fn append_history_index( &self, - index_updates: BTreeMap>, + index_updates: impl IntoIterator)>, mut sharded_key_factory: impl FnMut(P, BlockNumber) -> T::Key, ) -> ProviderResult<()> where @@ -1666,21 +1666,17 @@ impl DatabaseProvider { T: Table, { for (partial_key, indices) in index_updates { - let last_shard = self.take_shard::(sharded_key_factory(partial_key, u64::MAX))?; - // chunk indices and insert them in shards of N size. - let indices = last_shard.iter().chain(indices.iter()); - let chunks = indices - .chunks(sharded_key::NUM_OF_INDICES_IN_SHARD) - .into_iter() - .map(|chunks| chunks.copied().collect()) - .collect::>>(); - - let mut chunks = chunks.into_iter().peekable(); + let mut last_shard = + self.take_shard::(sharded_key_factory(partial_key, u64::MAX))?; + last_shard.extend(indices); + // Chunk indices and insert them in shards of N size. + let indices = last_shard; + let mut chunks = indices.chunks(sharded_key::NUM_OF_INDICES_IN_SHARD).peekable(); while let Some(list) = chunks.next() { let highest_block_number = if chunks.peek().is_some() { *list.last().expect("`chunks` does not return empty list") } else { - // Insert last list with u64::MAX + // Insert last list with `u64::MAX`. u64::MAX }; self.tx.put::( @@ -3139,7 +3135,7 @@ impl HistoryWriter for DatabaseProvider { fn insert_account_history_index( &self, - account_transitions: BTreeMap>, + account_transitions: impl IntoIterator)>, ) -> ProviderResult<()> { self.append_history_index::<_, tables::AccountsHistory>( account_transitions, @@ -3189,7 +3185,7 @@ impl HistoryWriter for DatabaseProvider { fn insert_storage_history_index( &self, - storage_transitions: BTreeMap<(Address, B256), Vec>, + storage_transitions: impl IntoIterator)>, ) -> ProviderResult<()> { self.append_history_index::<_, tables::StoragesHistory>( storage_transitions, diff --git a/crates/storage/provider/src/traits/history.rs b/crates/storage/provider/src/traits/history.rs index 726fe0cb7..b6f9d72ac 100644 --- a/crates/storage/provider/src/traits/history.rs +++ b/crates/storage/provider/src/traits/history.rs @@ -2,10 +2,7 @@ use auto_impl::auto_impl; use reth_db_api::models::BlockNumberAddress; use reth_primitives::{Address, BlockNumber, B256}; use reth_storage_errors::provider::ProviderResult; -use std::{ - collections::BTreeMap, - ops::{Range, RangeInclusive}, -}; +use std::ops::{Range, RangeInclusive}; /// History Writer #[auto_impl(&, Arc, Box)] @@ -21,7 +18,7 @@ pub trait HistoryWriter: Send + Sync { /// Insert account change index to database. Used inside AccountHistoryIndex stage fn insert_account_history_index( &self, - account_transitions: BTreeMap>, + index_updates: impl IntoIterator)>, ) -> ProviderResult<()>; /// Unwind and clear storage history indices. @@ -35,7 +32,7 @@ pub trait HistoryWriter: Send + Sync { /// Insert storage change index to database. Used inside StorageHistoryIndex stage fn insert_storage_history_index( &self, - storage_transitions: BTreeMap<(Address, B256), Vec>, + storage_transitions: impl IntoIterator)>, ) -> ProviderResult<()>; /// Read account/storage changesets and update account/storage history indices. diff --git a/examples/bsc-p2p/src/chainspec.rs b/examples/bsc-p2p/src/chainspec.rs index 92bf2f10a..11c570233 100644 --- a/examples/bsc-p2p/src/chainspec.rs +++ b/examples/bsc-p2p/src/chainspec.rs @@ -1,5 +1,5 @@ use reth_chainspec::{ - BaseFeeParams, Chain, ChainHardforks, ChainSpec, EthereumHardfork, ForkCondition, + once_cell_set, BaseFeeParams, Chain, ChainHardforks, ChainSpec, EthereumHardfork, ForkCondition, }; use reth_network_peers::NodeRecord; use reth_primitives::{b256, B256}; @@ -14,7 +14,8 @@ pub(crate) fn bsc_chain_spec() -> Arc { ChainSpec { chain: Chain::from_id(56), genesis: serde_json::from_str(include_str!("./genesis.json")).expect("deserialize genesis"), - genesis_hash: Some(GENESIS), + genesis_hash: once_cell_set(GENESIS), + genesis_header: Default::default(), paris_block_and_final_difficulty: None, hardforks: ChainHardforks::new(vec![( EthereumHardfork::Shanghai.boxed(), diff --git a/examples/polygon-p2p/src/chain_cfg.rs b/examples/polygon-p2p/src/chain_cfg.rs index 90bfaab35..eabcfb2e7 100644 --- a/examples/polygon-p2p/src/chain_cfg.rs +++ b/examples/polygon-p2p/src/chain_cfg.rs @@ -1,5 +1,5 @@ use reth_chainspec::{ - BaseFeeParams, Chain, ChainHardforks, ChainSpec, EthereumHardfork, ForkCondition, + once_cell_set, BaseFeeParams, Chain, ChainHardforks, ChainSpec, EthereumHardfork, ForkCondition, }; use reth_discv4::NodeRecord; use reth_primitives::{b256, Head, B256}; @@ -15,7 +15,8 @@ pub(crate) fn polygon_chain_spec() -> Arc { chain: Chain::from_id(137), // genesis: serde_json::from_str(include_str!("./genesis.json")).expect("deserialize genesis"), - genesis_hash: Some(GENESIS), + genesis_hash: once_cell_set(GENESIS), + genesis_header: Default::default(), paris_block_and_final_difficulty: None, hardforks: ChainHardforks::new(vec![ (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)),