fix(chainspec): add ChainConfig to StatelessInput and add ChainConfig creator helpers (#20101)

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
This commit is contained in:
Ignacio Hagopian
2025-12-04 15:46:04 -03:00
committed by GitHub
parent 61f5b4e06f
commit e53990cf41
6 changed files with 159 additions and 10 deletions

5
Cargo.lock generated
View File

@@ -303,9 +303,9 @@ dependencies = [
[[package]]
name = "alloy-hardforks"
version = "0.4.4"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e29d7eacf42f89c21d7f089916d0bdb4f36139a31698790e8837d2dbbd4b2c3"
checksum = "2d9a33550fc21fd77a3f8b63e99969d17660eec8dcc50a95a80f7c9964f7680b"
dependencies = [
"alloy-chains",
"alloy-eip2124",
@@ -10566,6 +10566,7 @@ name = "reth-stateless"
version = "1.9.3"
dependencies = [
"alloy-consensus",
"alloy-genesis",
"alloy-primitives",
"alloy-rlp",
"alloy-rpc-types-debug",

View File

@@ -493,7 +493,7 @@ alloy-sol-macro = "1.4.1"
alloy-sol-types = { version = "1.4.1", default-features = false }
alloy-trie = { version = "0.9.1", default-features = false }
alloy-hardforks = "0.4.4"
alloy-hardforks = "0.4.5"
alloy-consensus = { version = "1.1.2", default-features = false }
alloy-contract = { version = "1.1.2", default-features = false }

View File

@@ -30,8 +30,9 @@ pub use info::ChainInfo;
#[cfg(any(test, feature = "test-utils"))]
pub use spec::test_fork_ids;
pub use spec::{
make_genesis_header, BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder,
ChainSpecProvider, DepositContract, ForkBaseFeeParams, DEV, HOLESKY, HOODI, MAINNET, SEPOLIA,
blob_params_to_schedule, create_chain_config, mainnet_chain_config, make_genesis_header,
BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder, ChainSpecProvider,
DepositContract, ForkBaseFeeParams, DEV, HOLESKY, HOODI, MAINNET, SEPOLIA,
};
use reth_primitives_traits::sync::OnceLock;

View File

@@ -10,7 +10,14 @@ use crate::{
sepolia::SEPOLIA_PARIS_BLOCK,
EthChainSpec,
};
use alloc::{boxed::Box, format, sync::Arc, vec::Vec};
use alloc::{
boxed::Box,
collections::BTreeMap,
format,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use alloy_chains::{Chain, NamedChain};
use alloy_consensus::{
constants::{
@@ -23,7 +30,7 @@ use alloy_eips::{
eip1559::INITIAL_BASE_FEE, eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams,
eip7892::BlobScheduleBlobParams,
};
use alloy_genesis::Genesis;
use alloy_genesis::{ChainConfig, Genesis};
use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256};
use alloy_trie::root::state_root_ref_unhashed;
use core::fmt::Debug;
@@ -240,6 +247,111 @@ pub static DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
.into()
});
/// Creates a [`ChainConfig`] from the given chain, hardforks, deposit contract address, and blob
/// schedule.
pub fn create_chain_config(
chain: Option<Chain>,
hardforks: &ChainHardforks,
deposit_contract_address: Option<Address>,
blob_schedule: BTreeMap<String, BlobParams>,
) -> ChainConfig {
// Helper to extract block number from a hardfork condition
let block_num = |fork: EthereumHardfork| hardforks.fork(fork).block_number();
// Helper to extract timestamp from a hardfork condition
let timestamp = |fork: EthereumHardfork| -> Option<u64> {
match hardforks.fork(fork) {
ForkCondition::Timestamp(t) => Some(t),
_ => None,
}
};
// Extract TTD from Paris fork
let (terminal_total_difficulty, terminal_total_difficulty_passed) =
match hardforks.fork(EthereumHardfork::Paris) {
ForkCondition::TTD { total_difficulty, .. } => (Some(total_difficulty), true),
_ => (None, false),
};
// Check if DAO fork is supported (it has an activation block)
let dao_fork_support = hardforks.fork(EthereumHardfork::Dao) != ForkCondition::Never;
ChainConfig {
chain_id: chain.map(|c| c.id()).unwrap_or(0),
homestead_block: block_num(EthereumHardfork::Homestead),
dao_fork_block: block_num(EthereumHardfork::Dao),
dao_fork_support,
eip150_block: block_num(EthereumHardfork::Tangerine),
eip155_block: block_num(EthereumHardfork::SpuriousDragon),
eip158_block: block_num(EthereumHardfork::SpuriousDragon),
byzantium_block: block_num(EthereumHardfork::Byzantium),
constantinople_block: block_num(EthereumHardfork::Constantinople),
petersburg_block: block_num(EthereumHardfork::Petersburg),
istanbul_block: block_num(EthereumHardfork::Istanbul),
muir_glacier_block: block_num(EthereumHardfork::MuirGlacier),
berlin_block: block_num(EthereumHardfork::Berlin),
london_block: block_num(EthereumHardfork::London),
arrow_glacier_block: block_num(EthereumHardfork::ArrowGlacier),
gray_glacier_block: block_num(EthereumHardfork::GrayGlacier),
merge_netsplit_block: None,
shanghai_time: timestamp(EthereumHardfork::Shanghai),
cancun_time: timestamp(EthereumHardfork::Cancun),
prague_time: timestamp(EthereumHardfork::Prague),
osaka_time: timestamp(EthereumHardfork::Osaka),
bpo1_time: timestamp(EthereumHardfork::Bpo1),
bpo2_time: timestamp(EthereumHardfork::Bpo2),
bpo3_time: timestamp(EthereumHardfork::Bpo3),
bpo4_time: timestamp(EthereumHardfork::Bpo4),
bpo5_time: timestamp(EthereumHardfork::Bpo5),
terminal_total_difficulty,
terminal_total_difficulty_passed,
ethash: None,
clique: None,
parlia: None,
extra_fields: Default::default(),
deposit_contract_address,
blob_schedule,
}
}
/// Returns a [`ChainConfig`] for the current Ethereum mainnet chain.
pub fn mainnet_chain_config() -> ChainConfig {
let hardforks: ChainHardforks = EthereumHardfork::mainnet().into();
let blob_schedule = blob_params_to_schedule(&MAINNET.blob_params, &hardforks);
create_chain_config(
Some(Chain::mainnet()),
&hardforks,
Some(MAINNET_DEPOSIT_CONTRACT.address),
blob_schedule,
)
}
/// Converts the given [`BlobScheduleBlobParams`] into blobs schedule.
pub fn blob_params_to_schedule(
params: &BlobScheduleBlobParams,
hardforks: &ChainHardforks,
) -> BTreeMap<String, BlobParams> {
let mut schedule = BTreeMap::new();
schedule.insert("cancun".to_string(), params.cancun);
schedule.insert("prague".to_string(), params.prague);
schedule.insert("osaka".to_string(), params.osaka);
// Map scheduled entries back to bpo fork names by matching timestamps
let bpo_forks = EthereumHardfork::bpo_variants();
for (timestamp, blob_params) in &params.scheduled {
for bpo_fork in bpo_forks {
if let ForkCondition::Timestamp(fork_ts) = hardforks.fork(bpo_fork) &&
fork_ts == *timestamp
{
schedule.insert(bpo_fork.name().to_lowercase(), *blob_params);
break;
}
}
}
schedule
}
/// A wrapper around [`BaseFeeParams`] that allows for specifying constant or dynamic EIP-1559
/// parameters based on the active [Hardfork].
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -936,9 +1048,16 @@ impl ChainSpecBuilder {
self
}
/// Enable Dao at genesis.
pub fn dao_activated(mut self) -> Self {
self = self.frontier_activated();
self.hardforks.insert(EthereumHardfork::Dao, ForkCondition::Block(0));
self
}
/// Enable Homestead at genesis.
pub fn homestead_activated(mut self) -> Self {
self = self.frontier_activated();
self = self.dao_activated();
self.hardforks.insert(EthereumHardfork::Homestead, ForkCondition::Block(0));
self
}
@@ -985,9 +1104,16 @@ impl ChainSpecBuilder {
self
}
/// Enable Muir Glacier at genesis.
pub fn muirglacier_activated(mut self) -> Self {
self = self.istanbul_activated();
self.hardforks.insert(EthereumHardfork::MuirGlacier, ForkCondition::Block(0));
self
}
/// Enable Berlin at genesis.
pub fn berlin_activated(mut self) -> Self {
self = self.istanbul_activated();
self = self.muirglacier_activated();
self.hardforks.insert(EthereumHardfork::Berlin, ForkCondition::Block(0));
self
}
@@ -999,9 +1125,23 @@ impl ChainSpecBuilder {
self
}
/// Enable Arrow Glacier at genesis.
pub fn arrowglacier_activated(mut self) -> Self {
self = self.london_activated();
self.hardforks.insert(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0));
self
}
/// Enable Gray Glacier at genesis.
pub fn grayglacier_activated(mut self) -> Self {
self = self.arrowglacier_activated();
self.hardforks.insert(EthereumHardfork::GrayGlacier, ForkCondition::Block(0));
self
}
/// Enable Paris at genesis.
pub fn paris_activated(mut self) -> Self {
self = self.london_activated();
self = self.grayglacier_activated();
self.hardforks.insert(
EthereumHardfork::Paris,
ForkCondition::TTD {

View File

@@ -18,6 +18,7 @@ alloy-rlp.workspace = true
alloy-trie.workspace = true
alloy-consensus.workspace = true
alloy-rpc-types-debug.workspace = true
alloy-genesis = { workspace = true, features = ["serde-bincode-compat"] }
# reth
reth-ethereum-consensus.workspace = true

View File

@@ -39,6 +39,7 @@ mod recover_block;
/// Sparse trie implementation for stateless validation
pub mod trie;
use alloy_genesis::ChainConfig;
#[doc(inline)]
pub use recover_block::UncompressedPublicKey;
#[doc(inline)]
@@ -55,6 +56,8 @@ pub(crate) mod witness_db;
#[doc(inline)]
pub use alloy_rpc_types_debug::ExecutionWitness;
pub use alloy_genesis::Genesis;
use reth_ethereum_primitives::Block;
/// `StatelessInput` is a convenience structure for serializing the input needed
@@ -69,4 +72,7 @@ pub struct StatelessInput {
pub block: Block,
/// `ExecutionWitness` for the stateless validation function
pub witness: ExecutionWitness,
/// Chain configuration for the stateless validation function
#[serde_as(as = "alloy_genesis::serde_bincode_compat::ChainConfig<'_>")]
pub chain_config: ChainConfig,
}