diff --git a/Cargo.lock b/Cargo.lock index 6cb1a9145af..25ea5286d89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9485,6 +9485,7 @@ dependencies = [ "reth-optimism-evm", "reth-optimism-node", "reth-optimism-primitives", + "reth-optimism-trie", "reth-primitives-traits", "reth-provider", "reth-prune", @@ -9569,7 +9570,6 @@ dependencies = [ "eyre", "futures", "futures-util", - "reth-chainspec", "reth-db", "reth-exex", "reth-node-api", diff --git a/crates/optimism/cli/Cargo.toml b/crates/optimism/cli/Cargo.toml index bdb680b2062..bfc5a81c813 100644 --- a/crates/optimism/cli/Cargo.toml +++ b/crates/optimism/cli/Cargo.toml @@ -37,6 +37,7 @@ reth-node-metrics.workspace = true reth-optimism-primitives.workspace = true reth-optimism-chainspec = { workspace = true, features = ["superchain-configs"] } reth-optimism-consensus.workspace = true +reth-optimism-trie.workspace = true reth-chainspec.workspace = true reth-node-events.workspace = true @@ -86,15 +87,9 @@ asm-keccak = [ ] # Jemalloc feature for vergen to generate correct env vars -jemalloc = [ - "reth-node-core/jemalloc", - "reth-node-metrics/jemalloc", -] +jemalloc = ["reth-node-core/jemalloc", "reth-node-metrics/jemalloc"] -dev = [ - "dep:proptest", - "reth-cli-commands/arbitrary", -] +dev = ["dep:proptest", "reth-cli-commands/arbitrary"] serde = [ "alloy-consensus/serde", diff --git a/crates/optimism/cli/src/app.rs b/crates/optimism/cli/src/app.rs index 34048b3d943..e0c31995955 100644 --- a/crates/optimism/cli/src/app.rs +++ b/crates/optimism/cli/src/app.rs @@ -110,6 +110,9 @@ where Commands::ReExecute(command) => { runner.run_until_ctrl_c(command.execute::(components)) } + Commands::InitializeOpProofs(command) => { + runner.run_blocking_until_ctrl_c(command.execute::()) + } } } diff --git a/crates/optimism/cli/src/commands/initialize_proofs.rs b/crates/optimism/cli/src/commands/initialize_proofs.rs new file mode 100644 index 00000000000..e81e84f9dcc --- /dev/null +++ b/crates/optimism/cli/src/commands/initialize_proofs.rs @@ -0,0 +1,100 @@ +//! Command that initializes the OP proofs storage with the current state of the chain. + +use clap::Parser; +use reth_chainspec::ChainInfo; +use reth_cli::chainspec::ChainSpecParser; +use reth_cli_commands::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs}; +use reth_node_core::version::version_metadata; +use reth_optimism_chainspec::OpChainSpec; +use reth_optimism_primitives::OpPrimitives; +use reth_optimism_trie::{db::MdbxProofsStorage, BackfillJob, OpProofsStorage, OpProofsStore}; +use reth_provider::{BlockNumReader, DBProvider, DatabaseProviderFactory}; +use std::{path::PathBuf, sync::Arc}; +use tracing::info; + +/// Initializes the proofs storage with the current state of the chain. +/// +/// This command must be run before starting the node with proofs history enabled. +/// It backfills the proofs storage with trie nodes from the current chain state. +#[derive(Debug, Parser)] +pub struct InitializeOpProofsCommand { + #[command(flatten)] + env: EnvironmentArgs, + + /// The path to the storage DB for proofs history. + /// + /// This should match the path used when starting the node with + /// `--proofs-history.storage-path`. + #[arg( + long = "proofs-history.storage-path", + value_name = "PROOFS_HISTORY_STORAGE_PATH", + required = true + )] + pub storage_path: PathBuf, +} + +impl> InitializeOpProofsCommand { + /// Execute `initialize-op-proofs` command + pub async fn execute>( + self, + ) -> eyre::Result<()> { + info!(target: "reth::cli", "reth {} starting", version_metadata().short_version); + info!(target: "reth::cli", "Initializing OP proofs storage at: {:?}", self.storage_path); + + // Initialize the environment with read-only access + let Environment { provider_factory, .. } = self.env.init::(AccessRights::RO)?; + + // Create the proofs storage + let storage: OpProofsStorage> = Arc::new( + MdbxProofsStorage::new(&self.storage_path) + .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorage: {e}"))?, + ) + .into(); + + // Check if already initialized + if let Some((block_number, block_hash)) = storage.get_earliest_block_number().await? { + info!( + target: "reth::cli", + block_number = block_number, + block_hash = ?block_hash, + "Proofs storage already initialized" + ); + return Ok(()); + } + + // Get the current chain state + let ChainInfo { best_number, best_hash, .. } = provider_factory.chain_info()?; + + info!( + target: "reth::cli", + best_number = best_number, + best_hash = ?best_hash, + "Starting backfill job for current chain state" + ); + + // Run the backfill job + { + let db_provider = + provider_factory.database_provider_ro()?.disable_long_read_transaction_safety(); + let db_tx = db_provider.into_tx(); + + BackfillJob::new(storage.clone(), &db_tx).run(best_number, best_hash).await?; + } + + info!( + target: "reth::cli", + best_number = best_number, + best_hash = ?best_hash, + "Proofs storage initialized successfully" + ); + + Ok(()) + } +} + +impl InitializeOpProofsCommand { + /// Returns the underlying chain being used to run this command + pub const fn chain_spec(&self) -> Option<&Arc> { + Some(&self.env.chain) + } +} diff --git a/crates/optimism/cli/src/commands/mod.rs b/crates/optimism/cli/src/commands/mod.rs index 5edd55b0ccb..c3958a18b19 100644 --- a/crates/optimism/cli/src/commands/mod.rs +++ b/crates/optimism/cli/src/commands/mod.rs @@ -14,6 +14,7 @@ use std::{fmt, sync::Arc}; pub mod import; pub mod import_receipts; pub mod init_state; +pub mod initialize_proofs; #[cfg(feature = "dev")] pub mod test_vectors; @@ -61,6 +62,9 @@ pub enum Commands), + /// Initializes the proofs storage with the current state of the chain. + #[command(name = "initialize-op-proofs")] + InitializeOpProofs(initialize_proofs::InitializeOpProofsCommand), } impl< @@ -85,6 +89,7 @@ impl< #[cfg(feature = "dev")] Self::TestVectors(_) => None, Self::ReExecute(cmd) => cmd.chain_spec(), + Self::InitializeOpProofs(cmd) => cmd.chain_spec(), } } } diff --git a/crates/optimism/exex/Cargo.toml b/crates/optimism/exex/Cargo.toml index deaaeb2f5a1..12a967f37a2 100644 --- a/crates/optimism/exex/Cargo.toml +++ b/crates/optimism/exex/Cargo.toml @@ -16,9 +16,8 @@ workspace = true reth-exex.workspace = true reth-node-types.workspace = true reth-node-api.workspace = true -reth-provider.workspace = true -reth-chainspec.workspace = true reth-primitives-traits.workspace = true +reth-provider.workspace = true # op-reth # proofs exex handles `TrieUpdates` in notifications @@ -44,12 +43,11 @@ tempfile.workspace = true [features] test-utils = [ - "reth-provider/test-utils", "reth-db/test-utils", - "reth-chainspec/test-utils", "reth-node-builder/test-utils", "reth-optimism-node/test-utils", "reth-primitives-traits/test-utils", + "reth-provider/test-utils", ] [package.metadata.cargo-udeps.ignore] diff --git a/crates/optimism/exex/src/lib.rs b/crates/optimism/exex/src/lib.rs index 123b5726d9c..164ac1a98e9 100644 --- a/crates/optimism/exex/src/lib.rs +++ b/crates/optimism/exex/src/lib.rs @@ -11,15 +11,12 @@ use alloy_consensus::BlockHeader; use derive_more::Constructor; use futures_util::TryStreamExt; -use reth_chainspec::ChainInfo; use reth_exex::{ExExContext, ExExEvent, ExExNotification}; use reth_node_api::{FullNodeComponents, NodePrimitives}; use reth_node_types::NodeTypes; -use reth_optimism_trie::{live::LiveTrieCollector, BackfillJob, OpProofsStorage, OpProofsStore}; +use reth_optimism_trie::{live::LiveTrieCollector, OpProofsStorage, OpProofsStore}; use reth_primitives_traits::{BlockTy, RecoveredBlock}; -use reth_provider::{ - BlockNumReader, BlockReader, DBProvider, DatabaseProviderFactory, TransactionVariant, -}; +use reth_provider::{BlockReader, TransactionVariant}; use tracing::{debug, error, info}; /// OP Proofs ExEx - processes blocks and tracks state changes within fault proof window. @@ -101,12 +98,11 @@ where { /// Main execution loop for the ExEx pub async fn run(mut self) -> eyre::Result<()> { - { - let db_provider = - self.ctx.provider().database_provider_ro()?.disable_long_read_transaction_safety(); - let db_tx = db_provider.into_tx(); - let ChainInfo { best_number, best_hash } = self.ctx.provider().chain_info()?; - BackfillJob::new(self.storage.clone(), &db_tx).run(best_number, best_hash).await?; + // Check if proofs storage is initialized + if self.storage.get_earliest_block_number().await?.is_none() { + return Err(eyre::eyre!( + "Proofs storage not initialized. Please run 'op-reth initialize-op-proofs --proofs-history.storage-path ' first." + )); } let collector = LiveTrieCollector::new(