From df17419daba40fc1882c784aa4a7aabce7c98afd Mon Sep 17 00:00:00 2001 From: Niven Date: Thu, 4 Dec 2025 18:34:42 +0800 Subject: [PATCH 1/3] Fix payload validation for extra data and eip 1559 params with forkheights (#37) --- .../builders/flashblocks/payload_handler.rs | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs b/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs index 96b6f683..b33eed48 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs @@ -15,6 +15,7 @@ use reth_evm::FromRecoveredTx; use reth_node_builder::Events; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; +use reth_optimism_forks::OpHardforks; use reth_optimism_node::{OpEngineTypes, OpPayloadBuilderAttributes}; use reth_optimism_payload_builder::OpBuiltPayload; use reth_optimism_primitives::{OpReceipt, OpTransactionSigned}; @@ -192,17 +193,30 @@ where let mut info = ExecutionInfo::with_capacity(payload.block().body().transactions.len()); let extra_data = payload.block().sealed_header().extra_data.clone(); - if extra_data.len() != 9 { - tracing::error!(len = extra_data.len(), data = ?extra_data, "invalid extra data length in flashblock"); - bail!("extra data length should be 9 bytes"); - } + let eip_1559_parameters: Option = if chain_spec.is_jovian_active_at_timestamp(timestamp) { + if extra_data.len() != 17 { + tracing::debug!(len = extra_data.len(), data = ?extra_data, "invalid extra data length in flashblock for jovian fork"); + bail!("extra data length should be 17 bytes"); + } + extra_data[1..9].try_into().ok() + } else if chain_spec.is_holocene_active_at_timestamp(timestamp) { + if extra_data.len() != 9 { + tracing::debug!(len = extra_data.len(), data = ?extra_data, "invalid extra data length in flashblock for holocene fork"); + bail!("extra data length should be 9 bytes"); + } + extra_data[1..9].try_into().ok() + } else { + if !extra_data.is_empty() { + tracing::debug!(len = extra_data.len(), data = ?extra_data, "invalid extra data length in flashblock for pre holocene fork"); + bail!("extra data length should be 0 bytes"); + } + None + }; - // see https://specs.optimism.io/protocol/holocene/exec-engine.html#eip-1559-parameters-in-block-header - let eip_1559_parameters: B64 = extra_data[1..9].try_into().unwrap(); let payload_config = PayloadConfig::new( Arc::new(SealedHeader::new(parent_header.clone(), parent_hash)), OpPayloadBuilderAttributes { - eip_1559_params: Some(eip_1559_parameters), + eip_1559_params: eip_1559_parameters, payload_attributes: EthPayloadBuilderAttributes { id: payload.id(), // unused parent: parent_hash, // unused From 73a2b87ce208d9b7d1eb27984473ae25ae0d21b7 Mon Sep 17 00:00:00 2001 From: Niven Date: Thu, 4 Dec 2025 21:56:36 +0800 Subject: [PATCH 2/3] Fix external flashblock payload validation after jovian fork (#39) * Fix jovian da footprint scalar field * Fixes --- crates/op-rbuilder/src/builders/context.rs | 5 +++ .../builders/flashblocks/payload_handler.rs | 32 ++++++++++++++++--- .../src/primitives/reth/execution.rs | 3 ++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/crates/op-rbuilder/src/builders/context.rs b/crates/op-rbuilder/src/builders/context.rs index 1c042fc4..8618c98f 100644 --- a/crates/op-rbuilder/src/builders/context.rs +++ b/crates/op-rbuilder/src/builders/context.rs @@ -153,6 +153,11 @@ impl OpPayloadBuilderCtx { &self, info: &ExecutionInfo, ) -> (Option, Option) { + // For payload validation + if let Some(blob_fields) = info.optional_blob_fields { + return blob_fields; + } + // Compute from execution info if self.is_jovian_active() { let scalar = info .da_footprint_scalar diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs b/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs index b33eed48..125ab59e 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs @@ -9,6 +9,7 @@ use alloy_evm::eth::receipt_builder::ReceiptBuilderCtx; use alloy_primitives::B64; use eyre::{WrapErr as _, bail}; use op_alloy_consensus::OpTxEnvelope; +use op_revm::L1BlockInfo; use reth::revm::{State, database::StateProviderDatabase}; use reth_basic_payload_builder::PayloadConfig; use reth_evm::FromRecoveredTx; @@ -191,32 +192,44 @@ where .wrap_err("failed to apply pre execution changes")?; let mut info = ExecutionInfo::with_capacity(payload.block().body().transactions.len()); + info.optional_blob_fields = Some(( + payload.block().sealed_header().excess_blob_gas, + payload.block().sealed_header().blob_gas_used, + )); let extra_data = payload.block().sealed_header().extra_data.clone(); - let eip_1559_parameters: Option = if chain_spec.is_jovian_active_at_timestamp(timestamp) { + let (eip_1559_parameters, min_base_fee): (Option, Option) = if chain_spec + .is_jovian_active_at_timestamp(timestamp) + { if extra_data.len() != 17 { tracing::debug!(len = extra_data.len(), data = ?extra_data, "invalid extra data length in flashblock for jovian fork"); bail!("extra data length should be 17 bytes"); } - extra_data[1..9].try_into().ok() + let eip_1559_params = extra_data[1..9].try_into().ok(); + let min_base_fee_bytes: [u8; 8] = extra_data[9..17] + .try_into() + .wrap_err("failed to extract min base fee from jovian extra data")?; + let min_base_fee = u64::from_be_bytes(min_base_fee_bytes); + (eip_1559_params, Some(min_base_fee)) } else if chain_spec.is_holocene_active_at_timestamp(timestamp) { if extra_data.len() != 9 { tracing::debug!(len = extra_data.len(), data = ?extra_data, "invalid extra data length in flashblock for holocene fork"); bail!("extra data length should be 9 bytes"); } - extra_data[1..9].try_into().ok() + (extra_data[1..9].try_into().ok(), None) } else { if !extra_data.is_empty() { tracing::debug!(len = extra_data.len(), data = ?extra_data, "invalid extra data length in flashblock for pre holocene fork"); bail!("extra data length should be 0 bytes"); } - None + (None, None) }; let payload_config = PayloadConfig::new( Arc::new(SealedHeader::new(parent_header.clone(), parent_hash)), OpPayloadBuilderAttributes { eip_1559_params: eip_1559_parameters, + min_base_fee, payload_attributes: EthPayloadBuilderAttributes { id: payload.id(), // unused parent: parent_hash, // unused @@ -242,6 +255,7 @@ where payload.block().header().gas_used, ctx.evm_config(), evm_env.clone(), + chain_spec.clone(), ctx.max_gas_per_txn(), is_canyon_active(&chain_spec, timestamp), is_regolith_active(&chain_spec, timestamp), @@ -292,6 +306,7 @@ fn execute_transactions( gas_limit: u64, evm_config: &reth_optimism_evm::OpEvmConfig, evm_env: alloy_evm::EvmEnv, + chain_spec: Arc, max_gas_per_txn: Option, is_canyon_active: bool, is_regolith_active: bool, @@ -410,6 +425,15 @@ fn execute_transactions( info.executed_transactions.push(tx.clone()); } + // Fetch DA footprint gas scalar for Jovian blocks + let da_footprint_gas_scalar = chain_spec + .is_jovian_active_at_timestamp(timestamp) + .then(|| { + L1BlockInfo::fetch_da_footprint_gas_scalar(evm.db_mut()) + .expect("DA footprint should always be available from the database post jovian") + }); + info.da_footprint_scalar = da_footprint_gas_scalar; + Ok(()) } diff --git a/crates/op-rbuilder/src/primitives/reth/execution.rs b/crates/op-rbuilder/src/primitives/reth/execution.rs index 7865a1c8..9b656260 100644 --- a/crates/op-rbuilder/src/primitives/reth/execution.rs +++ b/crates/op-rbuilder/src/primitives/reth/execution.rs @@ -42,6 +42,8 @@ pub struct ExecutionInfo { pub extra: Extra, /// DA Footprint Scalar for Jovian pub da_footprint_scalar: Option, + /// Optional blob fields for payload validation + pub optional_blob_fields: Option<(Option, Option)>, } impl ExecutionInfo { @@ -56,6 +58,7 @@ impl ExecutionInfo { total_fees: U256::ZERO, extra: Default::default(), da_footprint_scalar: None, + optional_blob_fields: None, } } From 5d91cbf024e189509eebf6db0053e79c8e80bd16 Mon Sep 17 00:00:00 2001 From: Niven Date: Thu, 4 Dec 2025 22:08:35 +0800 Subject: [PATCH 3/3] Fix --- crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs b/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs index 125ab59e..41f9f2c6 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs @@ -253,6 +253,7 @@ where &mut state, payload.block().body().transactions.clone(), payload.block().header().gas_used, + timestamp, ctx.evm_config(), evm_env.clone(), chain_spec.clone(), @@ -304,6 +305,7 @@ fn execute_transactions( state: &mut State, txs: Vec, gas_limit: u64, + timestamp: u64, evm_config: &reth_optimism_evm::OpEvmConfig, evm_env: alloy_evm::EvmEnv, chain_spec: Arc,