From 4f936ecddc595b6a8d8677b28c477b70fa807c29 Mon Sep 17 00:00:00 2001 From: Daniel Freire Date: Wed, 30 Sep 2026 15:30:45 -0300 Subject: [PATCH 1/2] enha: configurable max gas limit for calls and transactions --- config/stratus.example.toml | 8 ++++- src/config/loader.rs | 14 ++++++-- src/eth/executor/config.rs | 32 ++++++++++++++++--- src/eth/executor/evm/mod.rs | 26 +++++++++------ .../evm/types/input/call_execution.rs | 5 ++- src/eth/executor/evm/types/input/mod.rs | 6 ++-- .../evm/types/input/transaction_execution.rs | 5 ++- src/eth/executor/evm/types/mod.rs | 6 ---- src/eth/executor/evm/util.rs | 13 ++++---- 9 files changed, 75 insertions(+), 40 deletions(-) diff --git a/config/stratus.example.toml b/config/stratus.example.toml index d3f57525e..561e544af 100644 --- a/config/stratus.example.toml +++ b/config/stratus.example.toml @@ -73,8 +73,14 @@ chain_id = 2008 # inspector_evms = 50 # Should reject contract transactions and calls to accounts that are not contracts? # reject_not_contract = true + +[executor.evm] # EVM hardfork spec, e.g. Prague, Cancun, Shanghai. -# evm_spec = "Prague" +# spec = "Prague" +# Maximum gas limit applied when executing a transaction. +# transaction_max_gas_limit = 100000000 +# Maximum gas limit applied when executing a call (eth_call and eth_estimateGas). +# call_max_gas_limit = 100000000 [miner] # Target block time: `automine`, `external`, or a duration like `1s`. diff --git a/src/config/loader.rs b/src/config/loader.rs index dacf4e79c..7ef8192cf 100644 --- a/src/config/loader.rs +++ b/src/config/loader.rs @@ -379,7 +379,11 @@ mod tests { call_past_evms = 2 inspector_evms = 3 reject_not_contract = false - evm_spec = "Cancun" + + [executor.evm] + spec = "Cancun" + transaction_max_gas_limit = 100000000 + call_max_gas_limit = 100000000 [miner] block_mode = "1s" @@ -576,7 +580,11 @@ mod tests { call_past_evms = 2 inspector_evms = 3 reject_not_contract = false - evm_spec = "Cancun" + + [executor.evm] + spec = "Cancun" + transaction_max_gas_limit = 100000000 + call_max_gas_limit = 100000000 [miner] block_mode = "1s" @@ -673,7 +681,7 @@ mod tests { assert_eq!(config.rpc_server.rpc_address.to_string(), "0.0.0.0:3001"); assert_eq!(config.rpc_server.rpc_max_response_size_bytes, 20971520); assert_eq!(config.executor.executor_chain_id, 100); - assert_eq!(config.executor.executor_evm_spec.to_string(), "Cancun"); + assert_eq!(config.executor.evm.spec.to_string(), "Cancun"); assert_eq!(config.miner.block_mode, MinerMode::Interval(std::time::Duration::from_secs(1))); assert_eq!(config.exporter.async_threads, 2); assert_eq!(config.exporter.blocking_threads, 32); diff --git a/src/eth/executor/config.rs b/src/eth/executor/config.rs index 40e9542a5..fc3bab35b 100644 --- a/src/eth/executor/config.rs +++ b/src/eth/executor/config.rs @@ -38,9 +38,27 @@ pub struct ExecutorConfig { #[serde(rename = "reject_not_contract")] pub executor_reject_not_contract: bool, - #[arg(id = "executor.evm_spec", long = "executor-evm-spec", default_value = "Prague", value_parser = parse_evm_spec)] - #[serde(rename = "evm_spec", with = "spec_id_serde")] - pub executor_evm_spec: SpecId, + #[clap(flatten)] + pub evm: EvmConfig, +} + +#[derive(Parser, DebugAsJson, Clone, Copy, serde::Serialize)] +pub struct EvmConfig { + #[arg(id = "executor.evm.spec", long = "executor-evm-spec", default_value = "Prague", value_parser = parse_spec)] + #[serde(with = "spec_id_serde")] + pub spec: SpecId, + + /// Maximum gas limit applied when executing a transaction. + #[arg( + id = "executor.evm.transaction_max_gas_limit", + long = "executor-transaction-max-gas-limit", + default_value = "100000000" + )] + pub transaction_max_gas_limit: u64, + + /// Maximum gas limit applied when executing a call (eth_call and eth_estimateGas). + #[arg(id = "executor.evm.call_max_gas_limit", long = "executor-call-max-gas-limit", default_value = "100000000")] + pub call_max_gas_limit: u64, } #[cfg(test)] @@ -52,7 +70,11 @@ impl Default for ExecutorConfig { call_past_evms: 50, inspector_evms: 50, executor_reject_not_contract: true, - executor_evm_spec: SpecId::PRAGUE, + evm: EvmConfig { + spec: SpecId::PRAGUE, + transaction_max_gas_limit: 100_000_000, + call_max_gas_limit: 100_000_000, + }, } } } @@ -76,7 +98,7 @@ fn parse_chain_id(input: &str) -> anyhow::Result { Ok(chain_id) } -fn parse_evm_spec(input: &str) -> anyhow::Result { +fn parse_spec(input: &str) -> anyhow::Result { SpecId::from_str(input).map_err(|err| anyhow::anyhow!("unknown hard fork: {err:?}")) } diff --git a/src/eth/executor/evm/mod.rs b/src/eth/executor/evm/mod.rs index d9958097c..12ae1e13e 100644 --- a/src/eth/executor/evm/mod.rs +++ b/src/eth/executor/evm/mod.rs @@ -51,6 +51,7 @@ pub type RevmResultAndState = ExecResultAndState; pub struct Evm { evm: GeneralRevm, kind: EvmKind, + max_gas_limit: u64, _input_type: PhantomData, } @@ -62,9 +63,16 @@ impl Evm { // configure revm let chain_id = config.executor_chain_id; + // the inspector reexecutes and traces mined transactions, so it follows the transaction limit + let max_gas_limit = match kind { + EvmKind::Transaction | EvmKind::Inspect => config.evm.transaction_max_gas_limit, + EvmKind::CallPast | EvmKind::CallPresent => config.evm.call_max_gas_limit, + }; + Self { - evm: create_evm(chain_id, config.executor_evm_spec, RevmSession::new(storage), kind), + evm: create_evm(chain_id, config.evm.spec, RevmSession::new(storage), kind), kind, + max_gas_limit, _input_type: PhantomData, } } @@ -75,7 +83,7 @@ impl Evm { // configure session self.evm.journaled_state.database.reset(input.kind()); - input.fill_env(&mut self.evm); + input.fill_env(&mut self.evm, self.max_gas_limit); let tx = std::mem::take(&mut self.evm.tx); let evm_result = self.evm.transact(tx); @@ -159,7 +167,7 @@ impl Evm { let tx_input: TransactionExecutionInput = tx.execution.input; // Configure EVM state - evm.fill_env(tx_input); + evm.fill_env(tx_input, self.max_gas_limit); let tx = std::mem::take(&mut evm.tx); evm.transact_commit(tx)?; } @@ -168,7 +176,7 @@ impl Evm { GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::FourByteTracer) => { let mut inspector = FourByteInspector::default(); let mut evm_with_inspector = evm.with_inspector(&mut inspector); - evm_with_inspector.fill_env(inspect_input); + evm_with_inspector.fill_env(inspect_input, self.max_gas_limit); let tx = std::mem::take(&mut evm_with_inspector.tx); evm_with_inspector.inspect_tx(tx)?; FourByteFrame::from(&inspector).into() @@ -177,7 +185,7 @@ impl Evm { let call_config = opts.tracer_config.into_call_config()?; let mut inspector = TracingInspector::new(TracingInspectorConfig::from_geth_call_config(&call_config)); let mut evm_with_inspector = evm.with_inspector(&mut inspector); - evm_with_inspector.fill_env(inspect_input); + evm_with_inspector.fill_env(inspect_input, self.max_gas_limit); let tx = std::mem::take(&mut evm_with_inspector.tx); let res = evm_with_inspector.inspect_tx(tx)?; let mut trace = inspector.geth_builder().geth_call_traces(call_config, res.result.tx_gas_used()).into(); @@ -188,7 +196,7 @@ impl Evm { let prestate_config = opts.tracer_config.into_pre_state_config()?; let mut inspector = TracingInspector::new(TracingInspectorConfig::from_geth_prestate_config(&prestate_config)); let mut evm_with_inspector = evm.with_inspector(&mut inspector); - evm_with_inspector.fill_env(inspect_input); + evm_with_inspector.fill_env(inspect_input, self.max_gas_limit); let tx = std::mem::take(&mut evm_with_inspector.tx); let res = evm_with_inspector.inspect_tx(tx)?; @@ -199,7 +207,7 @@ impl Evm { let mux_config = opts.tracer_config.into_mux_config()?; let mut inspector = MuxInspector::try_from_config(mux_config).map_err(|e| anyhow!(e))?; let mut evm_with_inspector = evm.with_inspector(&mut inspector); - evm_with_inspector.fill_env(inspect_input); + evm_with_inspector.fill_env(inspect_input, self.max_gas_limit); let tx = std::mem::take(&mut evm_with_inspector.tx); let res = evm_with_inspector.inspect_tx(tx)?; inspector.try_into_mux_frame(&res, &cache_db, tx_info)?.into() @@ -208,7 +216,7 @@ impl Evm { let flat_call_config = opts.tracer_config.into_flat_call_config()?; let mut inspector = TracingInspector::new(TracingInspectorConfig::from_flat_call_config(&flat_call_config)); let mut evm_with_inspector = evm.with_inspector(&mut inspector); - evm_with_inspector.fill_env(inspect_input); + evm_with_inspector.fill_env(inspect_input, self.max_gas_limit); let tx = std::mem::take(&mut evm_with_inspector.tx); let res = evm_with_inspector.inspect_tx(tx)?; inspector @@ -220,7 +228,7 @@ impl Evm { GethDebugTracerType::JsTracer(code) => { let mut inspector = JsInspector::new(code, opts.tracer_config.into_json()).map_err(|e| anyhow!(e.to_string()))?; let mut evm_with_inspector = evm.with_inspector(&mut inspector); - evm_with_inspector.fill_env(inspect_input); + evm_with_inspector.fill_env(inspect_input, self.max_gas_limit); let tx = std::mem::take(&mut evm_with_inspector.tx); let block = std::mem::take(&mut evm_with_inspector.block); let res = evm_with_inspector.inspect_tx(tx.clone())?; diff --git a/src/eth/executor/evm/types/input/call_execution.rs b/src/eth/executor/evm/types/input/call_execution.rs index a82bd28fc..6979fd8f8 100644 --- a/src/eth/executor/evm/types/input/call_execution.rs +++ b/src/eth/executor/evm/types/input/call_execution.rs @@ -5,7 +5,6 @@ use revm::context::TransactTo; use crate::eth::executor::evm::types::EvmInput; use crate::eth::executor::evm::types::ExecutionMetricsContext; -use crate::eth::executor::evm::types::GAS_MAX_LIMIT; use crate::eth::executor::evm::types::GeneralRevm; use crate::eth::storage::ExecutionKind; use crate::eth::types::Address; @@ -80,13 +79,13 @@ impl EvmInput for CallExecutionInput { evm.block.basefee = 0; } - fn fill_tx_env(self, evm: &mut GeneralRevm) { + fn fill_tx_env(self, evm: &mut GeneralRevm, gas_limit: u64) { evm.tx.caller = self.from.into(); evm.tx.kind = match self.to { Some(contract) => TransactTo::Call(contract.into()), None => TransactTo::Create, }; - evm.tx.gas_limit = GAS_MAX_LIMIT; + evm.tx.gas_limit = gas_limit; evm.tx.gas_price = 0; evm.tx.chain_id = None; evm.tx.nonce = 0; diff --git a/src/eth/executor/evm/types/input/mod.rs b/src/eth/executor/evm/types/input/mod.rs index d2c695e4d..47b3db493 100644 --- a/src/eth/executor/evm/types/input/mod.rs +++ b/src/eth/executor/evm/types/input/mod.rs @@ -13,12 +13,12 @@ pub trait EvmInput: Default + Clone { fn metrics_context(&self) -> ExecutionMetricsContext; - fn fill_tx_env(self, evm: &mut GeneralRevm); + fn fill_tx_env(self, evm: &mut GeneralRevm, gas_limit: u64); fn fill_block_env(&self, evm: &mut GeneralRevm); - fn fill_env(self, evm: &mut GeneralRevm) { + fn fill_env(self, evm: &mut GeneralRevm, gas_limit: u64) { self.fill_block_env(evm); - self.fill_tx_env(evm); + self.fill_tx_env(evm, gas_limit); } } diff --git a/src/eth/executor/evm/types/input/transaction_execution.rs b/src/eth/executor/evm/types/input/transaction_execution.rs index 48b617d08..20b5c234a 100644 --- a/src/eth/executor/evm/types/input/transaction_execution.rs +++ b/src/eth/executor/evm/types/input/transaction_execution.rs @@ -5,7 +5,6 @@ use revm::context::TransactTo; use crate::eth::executor::evm::types::EvmInput; use crate::eth::executor::evm::types::ExecutionMetricsContext; -use crate::eth::executor::evm::types::GAS_MAX_LIMIT; use crate::eth::executor::evm::types::GeneralRevm; use crate::eth::storage::ExecutionKind; use crate::eth::types::Address; @@ -112,13 +111,13 @@ impl EvmInput for TransactionExecutionInput { evm.block.basefee = 0; } - fn fill_tx_env(self, evm: &mut GeneralRevm) { + fn fill_tx_env(self, evm: &mut GeneralRevm, gas_limit: u64) { evm.tx.caller = self.from.into(); evm.tx.kind = match self.to { Some(contract) => TransactTo::Call(contract.into()), None => TransactTo::Create, }; - evm.tx.gas_limit = GAS_MAX_LIMIT; + evm.tx.gas_limit = gas_limit; evm.tx.gas_price = 0; evm.tx.chain_id = self.chain_id.map_into(); evm.tx.nonce = self.nonce.into(); diff --git a/src/eth/executor/evm/types/mod.rs b/src/eth/executor/evm/types/mod.rs index 220861995..1b82c01f9 100644 --- a/src/eth/executor/evm/types/mod.rs +++ b/src/eth/executor/evm/types/mod.rs @@ -25,12 +25,6 @@ pub use output::call_execution::CallExecutionOutput; pub use output::transaction_execution::TransactionExecutionOutput; pub use output::transaction_execution::TransactionExecutionResult; -/// Maximum gas limit allowed for a transaction. Prevents a transaction from consuming too many resources. -#[cfg(feature = "dev")] -pub const GAS_MAX_LIMIT: u64 = 1_000_000_000; -#[cfg(not(feature = "dev"))] -pub const GAS_MAX_LIMIT: u64 = 100_000_000; - pub type ContextWithDB = Context>; pub type GeneralRevm = RevmEvm, I, EthInstructions>, EthPrecompiles, EthFrame>; diff --git a/src/eth/executor/evm/util.rs b/src/eth/executor/evm/util.rs index 48360cacf..d456f5a32 100644 --- a/src/eth/executor/evm/util.rs +++ b/src/eth/executor/evm/util.rs @@ -22,7 +22,6 @@ use crate::eth::executor::EvmKind; use crate::eth::executor::TransactionExecution; use crate::eth::executor::TransactionExecutionInput; use crate::eth::executor::evm::GeneralRevm; -use crate::eth::executor::evm::types::GAS_MAX_LIMIT; use crate::eth::types::Address; use crate::ext::OptionExt; @@ -76,28 +75,28 @@ fn enhance_call_frame_errors(frame: &mut CallFrame) { } pub trait TxEnvExt { - fn fill_env(&mut self, input: TransactionExecutionInput); + fn fill_env(&mut self, input: TransactionExecutionInput, gas_limit: u64); } pub trait EvmExt { - fn fill_env(&mut self, input: TransactionExecutionInput); + fn fill_env(&mut self, input: TransactionExecutionInput, gas_limit: u64); } impl EvmExt for GeneralRevm { - fn fill_env(&mut self, input: TransactionExecutionInput) { + fn fill_env(&mut self, input: TransactionExecutionInput, gas_limit: u64) { self.block.fill_env(&input); - self.tx.fill_env(input); + self.tx.fill_env(input, gas_limit); } } impl TxEnvExt for TxEnv { - fn fill_env(&mut self, input: TransactionExecutionInput) { + fn fill_env(&mut self, input: TransactionExecutionInput, gas_limit: u64) { self.caller = input.from.into(); self.kind = match input.to { Some(contract) => TransactTo::Call(contract.into()), None => TransactTo::Create, }; - self.gas_limit = GAS_MAX_LIMIT; + self.gas_limit = gas_limit; self.gas_price = 0; self.chain_id = input.chain_id.map_into(); self.nonce = input.nonce.into(); From 93c0f1afb7a7e342afe47925ff35bbb25755c5b6 Mon Sep 17 00:00:00 2001 From: Daniel Freire Date: Thu, 1 Oct 2026 11:34:34 -0300 Subject: [PATCH 2/2] fix: tests --- justfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/justfile b/justfile index d96db1865..aba7b3a82 100644 --- a/justfile +++ b/justfile @@ -275,7 +275,11 @@ e2e-stratus block-mode="automine" test="": fi just _log "Starting Stratus" - just stratus-test -a 0.0.0.0:3000 --block-mode {{block-mode}} + if [[ {{block-mode}} =~ ^[0-9]+(ms|s)$ ]]; then + just stratus-test -a 0.0.0.0:3000 --block-mode {{block-mode}} --executor-transaction-max-gas-limit 1000000000 --executor-call-max-gas-limit 1000000000 + else + just stratus-test -a 0.0.0.0:3000 --block-mode {{block-mode}} + fi just _log "Running E2E tests" if [[ {{block-mode}} =~ ^[0-9]+(ms|s)$ ]]; then