From 507cf58db0ce4a3923ec9da7c1ee0c60ae8cecb9 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 26 Jan 2026 14:47:20 +0100 Subject: [PATCH] fix(rpc): add block number validation in eth_simulateV1 (#21396) Co-authored-by: Amp --- crates/rpc/rpc-eth-api/src/helpers/call.rs | 19 +++++++++++++++++++ crates/rpc/rpc-eth-types/src/simulate.rs | 11 ++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 7617130dd7..d37f0b5bb4 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -96,7 +96,23 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA self.spawn_with_state_at_block(block, move |this, mut db| { let mut blocks: Vec>> = Vec::with_capacity(block_state_calls.len()); + + // Track previous block number for validation + let mut prev_block_number = parent.number(); + for block in block_state_calls { + // Validate block number ordering if overridden + if let Some(number) = block.block_overrides.as_ref().and_then(|o| o.number) { + let number: u64 = number.try_into().unwrap_or(u64::MAX); + if number <= prev_block_number { + return Err(EthApiError::other(EthSimulateError::BlockNumberInvalid { + got: number, + parent: prev_block_number, + }) + .into()); + } + } + let mut evm_env = this .evm_config() .next_evm_env(&parent, &this.next_env_attributes(&parent)?) @@ -234,6 +250,9 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA parent = result.block.clone_sealed_header(); + // Update tracking for next iteration's validation + prev_block_number = parent.number(); + let block = simulate::build_simulated_block::( result.block, results, diff --git a/crates/rpc/rpc-eth-types/src/simulate.rs b/crates/rpc/rpc-eth-types/src/simulate.rs index 4ba0f331d0..d7f248f0e0 100644 --- a/crates/rpc/rpc-eth-types/src/simulate.rs +++ b/crates/rpc/rpc-eth-types/src/simulate.rs @@ -39,8 +39,13 @@ pub enum EthSimulateError { #[error("Client adjustable limit reached")] GasLimitReached, /// Block number in sequence did not increase. - #[error("Block number in sequence did not increase")] - BlockNumberInvalid, + #[error("block numbers must be in order: {got} <= {parent}")] + BlockNumberInvalid { + /// The block number that was provided. + got: u64, + /// The parent block number. + parent: u64, + }, /// Block timestamp in sequence did not increase or stay the same. #[error("Block timestamp in sequence did not increase")] BlockTimestampInvalid, @@ -96,7 +101,7 @@ impl EthSimulateError { Self::IntrinsicGasTooLow => -38013, Self::InsufficientFunds { .. } => -38014, Self::BlockGasLimitExceeded => -38015, - Self::BlockNumberInvalid => -38020, + Self::BlockNumberInvalid { .. } => -38020, Self::BlockTimestampInvalid => -38021, Self::PrecompileSelfReference => -38022, Self::PrecompileDuplicateAddress => -38023,