From c23f0d11456023e83c97eb2bbd791ee6f7bdbace Mon Sep 17 00:00:00 2001 From: Rez Date: Wed, 16 Jul 2025 20:46:36 +1000 Subject: [PATCH 1/4] feat: make CLI compatible with custom headers Enable CLI to work with custom block header types by requiring From conversion. This allows chains to use custom headers while maintaining CLI compatibility through standard Ethereum header conversion. Changes: - Make init_state command generic over BlockHeader types - Add From
trait bound for custom headers - Update header factory to use generic conversion - Fix header field access to use trait methods --- crates/cli/commands/src/init_state/mod.rs | 15 +++++++++------ crates/cli/commands/src/init_state/without_evm.rs | 10 ++++++---- crates/ethereum/cli/src/interface.rs | 6 +++++- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/crates/cli/commands/src/init_state/mod.rs b/crates/cli/commands/src/init_state/mod.rs index 76e7791e1d4..8671df0fa61 100644 --- a/crates/cli/commands/src/init_state/mod.rs +++ b/crates/cli/commands/src/init_state/mod.rs @@ -1,14 +1,14 @@ //! Command that initializes the node from a genesis file. use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs}; -use alloy_consensus::Header; +use alloy_consensus::{BlockHeader as AlloyBlockHeader, Header}; use alloy_primitives::{B256, U256}; use clap::Parser; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_cli::chainspec::ChainSpecParser; use reth_db_common::init::init_from_state_dump; use reth_node_api::NodePrimitives; -use reth_primitives_traits::SealedHeader; +use reth_primitives_traits::{BlockHeader, SealedHeader}; use reth_provider::{ BlockNumReader, DatabaseProviderFactory, StaticFileProviderFactory, StaticFileWriter, }; @@ -72,7 +72,7 @@ impl> InitStateC where N: CliNodeTypes< ChainSpec = C::ChainSpec, - Primitives: NodePrimitives, + Primitives: NodePrimitives>, >, { info!(target: "reth::cli", "Reth init-state starting"); @@ -85,7 +85,7 @@ impl> InitStateC if self.without_evm { // ensure header, total difficulty and header hash are provided let header = self.header.ok_or_else(|| eyre::eyre!("Header file must be provided"))?; - let header = without_evm::read_header_from_file(header)?; + let header = without_evm::read_header_from_file::<::BlockHeader>(header)?; let header_hash = self.header_hash.ok_or_else(|| eyre::eyre!("Header hash must be provided"))?; @@ -103,7 +103,10 @@ impl> InitStateC &provider_rw, SealedHeader::new(header, header_hash), total_difficulty, - |number| Header { number, ..Default::default() }, + |number| { + let header = Header { number, ..Default::default() }; + <::BlockHeader>::from(header) + }, )?; // SAFETY: it's safe to commit static files, since in the event of a crash, they @@ -112,7 +115,7 @@ impl> InitStateC // Necessary to commit, so the header is accessible to provider_rw and // init_state_dump static_file_provider.commit()?; - } else if last_block_number > 0 && last_block_number < header.number { + } else if last_block_number > 0 && last_block_number < header.number() { return Err(eyre::eyre!( "Data directory should be empty when calling init-state with --without-evm-history." )); diff --git a/crates/cli/commands/src/init_state/without_evm.rs b/crates/cli/commands/src/init_state/without_evm.rs index c839aaf268e..31549dfd25b 100644 --- a/crates/cli/commands/src/init_state/without_evm.rs +++ b/crates/cli/commands/src/init_state/without_evm.rs @@ -1,4 +1,4 @@ -use alloy_consensus::{BlockHeader, Header}; +use alloy_consensus::BlockHeader; use alloy_primitives::{BlockNumber, B256, U256}; use alloy_rlp::Decodable; use reth_codecs::Compact; @@ -12,14 +12,16 @@ use reth_stages::{StageCheckpoint, StageId}; use reth_static_file_types::StaticFileSegment; use std::{fs::File, io::Read, path::PathBuf}; use tracing::info; - /// Reads the header RLP from a file and returns the Header. -pub(crate) fn read_header_from_file(path: PathBuf) -> Result { +pub(crate) fn read_header_from_file(path: PathBuf) -> Result +where + H: BlockHeader + Decodable +{ let mut file = File::open(path)?; let mut buf = Vec::new(); file.read_to_end(&mut buf)?; - let header = Header::decode(&mut &buf[..])?; + let header = H::decode(&mut &buf[..])?; Ok(header) } diff --git a/crates/ethereum/cli/src/interface.rs b/crates/ethereum/cli/src/interface.rs index f1bace672bd..2664d5b79ee 100644 --- a/crates/ethereum/cli/src/interface.rs +++ b/crates/ethereum/cli/src/interface.rs @@ -1,6 +1,8 @@ //! CLI definition and entrypoint to executable use crate::chainspec::EthereumChainSpecParser; +use alloy_consensus::Header; +use reth_node_api::NodeTypes; use clap::{Parser, Subcommand}; use reth_chainspec::{ChainSpec, EthChainSpec, Hardforks}; use reth_cli::chainspec::ChainSpecParser; @@ -182,10 +184,12 @@ impl Cli { ) -> eyre::Result<()> where N: CliNodeTypes< - Primitives: NodePrimitives, + Primitives: NodePrimitives, ChainSpec: Hardforks, >, C: ChainSpecParser, + // Custom headers must be convertible from standard Ethereum headers + <::Primitives as NodePrimitives>::BlockHeader: From
, { // Add network name if available to the logs dir if let Some(chain_spec) = self.command.chain_spec() { From 4f93672821d55d87c4fb16e23c619c7594e9180f Mon Sep 17 00:00:00 2001 From: Rez Date: Wed, 16 Jul 2025 20:51:58 +1000 Subject: [PATCH 2/4] chore: fix formatting and imports in CLI custom header support Clean up code formatting and import organization: - Fix multi-line type parameter formatting - Add missing trailing comma in where clause - Reorganize imports for better readability - Format constraint bounds consistently This addresses formatting issues from the previous commit. --- crates/cli/commands/src/init_state/mod.rs | 4 +++- crates/cli/commands/src/init_state/without_evm.rs | 2 +- crates/ethereum/cli/src/interface.rs | 8 ++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/cli/commands/src/init_state/mod.rs b/crates/cli/commands/src/init_state/mod.rs index 8671df0fa61..7a80997b976 100644 --- a/crates/cli/commands/src/init_state/mod.rs +++ b/crates/cli/commands/src/init_state/mod.rs @@ -85,7 +85,9 @@ impl> InitStateC if self.without_evm { // ensure header, total difficulty and header hash are provided let header = self.header.ok_or_else(|| eyre::eyre!("Header file must be provided"))?; - let header = without_evm::read_header_from_file::<::BlockHeader>(header)?; + let header = without_evm::read_header_from_file::< + ::BlockHeader, + >(header)?; let header_hash = self.header_hash.ok_or_else(|| eyre::eyre!("Header hash must be provided"))?; diff --git a/crates/cli/commands/src/init_state/without_evm.rs b/crates/cli/commands/src/init_state/without_evm.rs index 31549dfd25b..578b930e42c 100644 --- a/crates/cli/commands/src/init_state/without_evm.rs +++ b/crates/cli/commands/src/init_state/without_evm.rs @@ -15,7 +15,7 @@ use tracing::info; /// Reads the header RLP from a file and returns the Header. pub(crate) fn read_header_from_file(path: PathBuf) -> Result where - H: BlockHeader + Decodable + H: BlockHeader + Decodable, { let mut file = File::open(path)?; let mut buf = Vec::new(); diff --git a/crates/ethereum/cli/src/interface.rs b/crates/ethereum/cli/src/interface.rs index 2664d5b79ee..2624b82e35a 100644 --- a/crates/ethereum/cli/src/interface.rs +++ b/crates/ethereum/cli/src/interface.rs @@ -2,7 +2,6 @@ use crate::chainspec::EthereumChainSpecParser; use alloy_consensus::Header; -use reth_node_api::NodeTypes; use clap::{Parser, Subcommand}; use reth_chainspec::{ChainSpec, EthChainSpec, Hardforks}; use reth_cli::chainspec::ChainSpecParser; @@ -15,7 +14,7 @@ use reth_cli_commands::{ }; use reth_cli_runner::CliRunner; use reth_db::DatabaseEnv; -use reth_node_api::NodePrimitives; +use reth_node_api::{NodePrimitives, NodeTypes}; use reth_node_builder::{NodeBuilder, WithLaunchContext}; use reth_node_core::{ args::LogArgs, @@ -183,10 +182,7 @@ impl Cli { ) -> eyre::Result<()>, ) -> eyre::Result<()> where - N: CliNodeTypes< - Primitives: NodePrimitives, - ChainSpec: Hardforks, - >, + N: CliNodeTypes, C: ChainSpecParser, // Custom headers must be convertible from standard Ethereum headers <::Primitives as NodePrimitives>::BlockHeader: From
, From 315ebfad3e67d6b034048ff421812ebd9fb97578 Mon Sep 17 00:00:00 2001 From: Rez Date: Wed, 16 Jul 2025 20:52:59 +1000 Subject: [PATCH 3/4] remove extra comment --- crates/ethereum/cli/src/interface.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ethereum/cli/src/interface.rs b/crates/ethereum/cli/src/interface.rs index 2624b82e35a..e62dad13d09 100644 --- a/crates/ethereum/cli/src/interface.rs +++ b/crates/ethereum/cli/src/interface.rs @@ -184,7 +184,6 @@ impl Cli { where N: CliNodeTypes, C: ChainSpecParser, - // Custom headers must be convertible from standard Ethereum headers <::Primitives as NodePrimitives>::BlockHeader: From
, { // Add network name if available to the logs dir From 049ddda223c99219f12d8b60e07c71274c24cd2b Mon Sep 17 00:00:00 2001 From: Rez Date: Wed, 16 Jul 2025 21:21:48 +1000 Subject: [PATCH 4/4] fix: remove redundant BlockHeader constraint in read_header_from_file The function only needs Decodable trait to read RLP-encoded headers. BlockHeader constraint was redundant since the function doesn't use any BlockHeader methods - it only decodes the header from RLP bytes. --- crates/cli/commands/src/init_state/without_evm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/commands/src/init_state/without_evm.rs b/crates/cli/commands/src/init_state/without_evm.rs index 578b930e42c..3a85b175eb4 100644 --- a/crates/cli/commands/src/init_state/without_evm.rs +++ b/crates/cli/commands/src/init_state/without_evm.rs @@ -15,7 +15,7 @@ use tracing::info; /// Reads the header RLP from a file and returns the Header. pub(crate) fn read_header_from_file(path: PathBuf) -> Result where - H: BlockHeader + Decodable, + H: Decodable, { let mut file = File::open(path)?; let mut buf = Vec::new();