diff --git a/Cargo.lock b/Cargo.lock index 352dcf9815e..9adeae1b05e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9844,7 +9844,6 @@ version = "1.9.3" dependencies = [ "alloy-consensus", "alloy-eips", - "derive_more", "eyre", "futures", "futures-util", diff --git a/crates/optimism/bin/src/main.rs b/crates/optimism/bin/src/main.rs index 80048eae334..ad574c96964 100644 --- a/crates/optimism/bin/src/main.rs +++ b/crates/optimism/bin/src/main.rs @@ -77,6 +77,21 @@ struct Args { value_parser = humantime::parse_duration )] pub proofs_history_prune_interval: Duration, + /// Verification interval: perform full block execution every N blocks for data integrity. + /// - 0: Disabled (Default) (always use fast path with pre-computed data from notifications) + /// - 1: Always verify (always execute blocks, slowest) + /// - N: Verify every Nth block (e.g., 100 = every 100 blocks) + /// + /// Periodic verification helps catch data corruption or consensus bugs while maintaining + /// good performance. + /// + /// CLI: `--proofs-history.verification-interval 100` + #[arg( + long = "proofs-history.verification-interval", + value_name = "PROOFS_HISTORY_VERIFICATION_INTERVAL", + default_value_t = 0 + )] + pub proofs_history_verification_interval: u64, } /// Single entry that handles: @@ -91,6 +106,7 @@ async fn launch_node( let rollup_args = args.rollup_args.clone(); let proofs_history_window = args.proofs_history_window; let proofs_history_prune_interval = args.proofs_history_prune_interval; + let proofs_history_verification_interval = args.proofs_history_verification_interval; // Start from a plain OpNode builder let mut node_builder = builder.node(OpNode::new(rollup_args)); @@ -125,6 +141,7 @@ async fn launch_node( storage_exec, proofs_history_window, proofs_history_prune_interval, + proofs_history_verification_interval, ) .run() .boxed()) diff --git a/crates/optimism/exex/Cargo.toml b/crates/optimism/exex/Cargo.toml index a182431e27e..cd5f8b2a760 100644 --- a/crates/optimism/exex/Cargo.toml +++ b/crates/optimism/exex/Cargo.toml @@ -31,7 +31,6 @@ alloy-eips.workspace = true # misc eyre.workspace = true futures-util.workspace = true -derive_more.workspace = true tracing.workspace = true [dev-dependencies] diff --git a/crates/optimism/exex/src/lib.rs b/crates/optimism/exex/src/lib.rs index 1b6c6f61760..a651a98d225 100644 --- a/crates/optimism/exex/src/lib.rs +++ b/crates/optimism/exex/src/lib.rs @@ -10,7 +10,6 @@ use alloy_consensus::BlockHeader; use alloy_eips::eip1898::BlockWithParent; -use derive_more::Constructor; use futures_util::TryStreamExt; use reth_execution_types::Chain; use reth_exex::{ExExContext, ExExEvent, ExExNotification}; @@ -72,6 +71,10 @@ const MAX_PRUNE_BLOCKS_STARTUP: u64 = 1000; /// let storage_exec = storage.clone(); /// let proofs_history_window = 1_296_000u64; /// let proofs_history_prune_interval = Duration::from_secs(3600); +/// +/// // Verification interval: perform full execution every N blocks +/// let verification_interval = 0; // 0 = disabled, 100 = verify every 100 blocks +/// /// // Can also use install_exex_if along with a boolean flag /// // Set this based on your configuration or CLI args /// let _builder = NodeBuilder::new(config) @@ -84,6 +87,7 @@ const MAX_PRUNE_BLOCKS_STARTUP: u64 = 1000; /// storage_exec, /// proofs_history_window, /// proofs_history_prune_interval, +/// verification_interval, // 0 = no verification, 100 = every 100 blocks /// ) /// .run() /// .boxed()) @@ -91,7 +95,7 @@ const MAX_PRUNE_BLOCKS_STARTUP: u64 = 1000; /// .on_node_started(|_full_node| Ok(())) /// .check_launch(); /// ``` -#[derive(Debug, Constructor)] +#[derive(Debug)] pub struct OpProofsExEx where Node: FullNodeComponents, @@ -106,6 +110,32 @@ where proofs_history_window: u64, /// Interval between proof-storage prune runs proofs_history_prune_interval: Duration, + /// Verification interval: perform full block execution every N blocks for data integrity. + /// If 0, verification is disabled (always use fast path when available). + /// If 1, verification is always enabled (always execute blocks). + verification_interval: u64, +} + +impl OpProofsExEx +where + Node: FullNodeComponents, +{ + /// Create a new `OpProofsExEx` instance. + pub const fn new( + ctx: ExExContext, + storage: OpProofsStorage, + proofs_history_window: u64, + proofs_history_prune_interval: Duration, + verification_interval: u64, + ) -> Self { + Self { + ctx, + storage, + proofs_history_window, + proofs_history_prune_interval, + verification_interval, + } + } } impl OpProofsExEx @@ -266,6 +296,10 @@ where chain: &Chain, collector: &LiveTrieCollector<'_, Node::Evm, Node::Provider, Storage>, ) -> eyre::Result<()> { + // Check if this block should be verified via full execution + let should_verify = self.verification_interval > 0 && + block_number.is_multiple_of(self.verification_interval); + // Try to get block data from the chain first // 1. Fast Path: Try to use pre-computed state from the notification if let Some(block) = chain.blocks().get(&block_number) { @@ -274,21 +308,31 @@ where if let (Some(trie_updates), Some(hashed_state)) = (chain.trie_updates_at(block_number), chain.hashed_state_at(block_number)) { - debug!( + // Use fast path only if we're not scheduled to verify this block + if !should_verify { + debug!( + target: "optimism::exex", + block_number, + "Using pre-computed state updates from notification" + ); + + collector + .store_block_updates( + block.block_with_parent(), + (**trie_updates).clone(), + (**hashed_state).clone(), + ) + .await?; + + return Ok(()); + } + + info!( target: "optimism::exex", block_number, - "Using pre-computed state updates from notification" + verification_interval = self.verification_interval, + "Periodic verification: performing full block execution" ); - - collector - .store_block_updates( - block.block_with_parent(), - (**trie_updates).clone(), - (**hashed_state).clone(), - ) - .await?; - - return Ok(()); } debug!( diff --git a/docs/vocs/docs/pages/cli/op-reth/node.mdx b/docs/vocs/docs/pages/cli/op-reth/node.mdx index 2d3a09f4cb2..28fdcea71ee 100644 --- a/docs/vocs/docs/pages/cli/op-reth/node.mdx +++ b/docs/vocs/docs/pages/cli/op-reth/node.mdx @@ -1087,6 +1087,15 @@ Rollup: [default: 15s] + --proofs-history.verification-interval + Verification interval: perform full block execution every N blocks for data integrity. - 0: Disabled (Default) (always use fast path with pre-computed data from notifications) - 1: Always verify (always execute blocks, slowest) - N: Verify every Nth block (e.g., 100 = every 100 blocks) + + Periodic verification helps catch data corruption or consensus bugs while maintaining good performance. + + CLI: `--proofs-history.verification-interval 100` + + [default: 0] + Logging: --log.stdout.format The format to use for logs written to stdout