feat: add --rpc.evm-memory-limit flag (#19279)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Wojtek Łopata
2025-10-30 20:53:43 +01:00
committed by GitHub
parent fccf76a19a
commit dc8efbf9b3
19 changed files with 87 additions and 24 deletions

21
Cargo.lock generated
View File

@@ -10650,7 +10650,7 @@ dependencies = [
"reth-storage-api",
"reth-tasks",
"reth-tracing",
"revm-interpreter 27.0.2",
"revm-interpreter",
"revm-primitives",
"rustc-hash",
"schnellru",
@@ -10867,7 +10867,7 @@ dependencies = [
"revm-database-interface",
"revm-handler",
"revm-inspector",
"revm-interpreter 28.0.0",
"revm-interpreter",
"revm-precompile",
"revm-primitives",
"revm-state",
@@ -10957,7 +10957,7 @@ dependencies = [
"revm-context",
"revm-context-interface",
"revm-database-interface",
"revm-interpreter 28.0.0",
"revm-interpreter",
"revm-precompile",
"revm-primitives",
"revm-state",
@@ -10975,7 +10975,7 @@ dependencies = [
"revm-context",
"revm-database-interface",
"revm-handler",
"revm-interpreter 28.0.0",
"revm-interpreter",
"revm-primitives",
"revm-state",
"serde",
@@ -11002,19 +11002,6 @@ dependencies = [
"thiserror 2.0.17",
]
[[package]]
name = "revm-interpreter"
version = "27.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0834fc25c020061f0f801d8de8bb53c88a63631cca5884a6c65b90c85e241138"
dependencies = [
"revm-bytecode",
"revm-context-interface",
"revm-primitives",
"revm-state",
"serde",
]
[[package]]
name = "revm-interpreter"
version = "28.0.0"

View File

@@ -474,7 +474,7 @@ revm-bytecode = { version = "7.0.2", default-features = false }
revm-database = { version = "9.0.2", default-features = false }
revm-state = { version = "8.0.2", default-features = false }
revm-primitives = { version = "21.0.1", default-features = false }
revm-interpreter = { version = "27.0.2", default-features = false }
revm-interpreter = { version = "28.0.0", default-features = false }
revm-inspector = { version = "11.1.2", default-features = false }
revm-context = { version = "10.1.2", default-features = false }
revm-context-interface = { version = "11.1.2", default-features = false }

View File

@@ -49,7 +49,7 @@ tokio.workspace = true
# revm with required ethereum features
# Note: this must be kept to ensure all features are properly enabled/forwarded
revm = { workspace = true, features = ["secp256k1", "blst", "c-kzg"] }
revm = { workspace = true, features = ["secp256k1", "blst", "c-kzg", "memory_limit"] }
# misc
eyre.workspace = true

View File

@@ -1156,6 +1156,7 @@ impl<'a, N: FullNodeComponents<Types: NodeTypes<ChainSpec: Hardforks + EthereumH
.max_batch_size(self.config.max_batch_size)
.pending_block_kind(self.config.pending_block_kind)
.raw_tx_forwarder(self.config.raw_tx_forwarder)
.evm_memory_limit(self.config.rpc_evm_memory_limit)
}
}

View File

@@ -188,6 +188,16 @@ pub struct RpcServerArgs {
)]
pub rpc_gas_cap: u64,
/// Maximum memory the EVM can allocate per RPC request.
#[arg(
long = "rpc.evm-memory-limit",
alias = "rpc-evm-memory-limit",
value_name = "MEMORY_LIMIT",
value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
default_value_t = (1 << 32) - 1
)]
pub rpc_evm_memory_limit: u64,
/// Maximum eth transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)
#[arg(
long = "rpc.txfeecap",
@@ -408,6 +418,7 @@ impl Default for RpcServerArgs {
rpc_max_blocks_per_filter: constants::DEFAULT_MAX_BLOCKS_PER_FILTER.into(),
rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(),
rpc_gas_cap: constants::gas_oracle::RPC_DEFAULT_GAS_CAP,
rpc_evm_memory_limit: (1 << 32) - 1,
rpc_tx_fee_cap: constants::DEFAULT_TX_FEE_CAP_WEI,
rpc_max_simulate_blocks: constants::DEFAULT_MAX_SIMULATE_BLOCKS,
rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW,

View File

@@ -45,7 +45,7 @@ reth-optimism-primitives = { workspace = true, features = ["serde", "serde-binco
# revm with required optimism features
# Note: this must be kept to ensure all features are properly enabled/forwarded
revm = { workspace = true, features = ["secp256k1", "blst", "c-kzg"] }
revm = { workspace = true, features = ["secp256k1", "blst", "c-kzg", "memory_limit"] }
op-revm.workspace = true
# ethereum

View File

@@ -35,4 +35,9 @@ where
fn max_simulate_blocks(&self) -> u64 {
self.inner.eth_api.max_simulate_blocks()
}
#[inline]
fn evm_memory_limit(&self) -> u64 {
self.inner.eth_api.evm_memory_limit()
}
}

View File

@@ -68,3 +68,4 @@ optional-checks = [
"optional-eip3607",
"optional-no-base-fee",
]
memory_limit = ["revm/memory_limit"]

View File

@@ -105,6 +105,7 @@ impl RethRpcServerConfig for RpcServerArgs {
.proof_permits(self.rpc_proof_permits)
.pending_block_kind(self.rpc_pending_block)
.raw_tx_forwarder(self.rpc_forwarder.clone())
.rpc_evm_memory_limit(self.rpc_evm_memory_limit)
}
fn flashbots_config(&self) -> ValidationApiConfig {

View File

@@ -13,7 +13,7 @@ workspace = true
[dependencies]
# reth
revm = { workspace = true, features = ["optional_block_gas_limit", "optional_eip3607", "optional_no_base_fee", "optional_fee_charge"] }
revm = { workspace = true, features = ["optional_block_gas_limit", "optional_eip3607", "optional_no_base_fee", "optional_fee_charge", "memory_limit"] }
reth-chain-state.workspace = true
revm-inspectors.workspace = true
reth-primitives-traits = { workspace = true, features = ["rpc-compat"] }

View File

@@ -493,6 +493,9 @@ pub trait Call:
/// Returns the maximum number of blocks accepted for `eth_simulateV1`.
fn max_simulate_blocks(&self) -> u64;
/// Returns the maximum memory the EVM can allocate per RPC request.
fn evm_memory_limit(&self) -> u64;
/// Returns the max gas limit that the caller can afford given a transaction environment.
fn caller_gas_allowance(
&self,
@@ -811,6 +814,8 @@ pub trait Call:
// <https://github.com/paradigmxyz/reth/issues/18470>
evm_env.cfg_env.disable_fee_charge = true;
evm_env.cfg_env.memory_limit = self.evm_memory_limit();
// set nonce to None so that the correct nonce is chosen by the EVM
request.as_mut().take_nonce();

View File

@@ -95,6 +95,8 @@ pub struct EthConfig {
pub raw_tx_forwarder: ForwardConfig,
/// Timeout duration for `send_raw_transaction_sync` RPC method.
pub send_raw_transaction_sync_timeout: Duration,
/// Maximum memory the EVM can allocate per RPC request.
pub rpc_evm_memory_limit: u64,
}
impl EthConfig {
@@ -126,6 +128,7 @@ impl Default for EthConfig {
pending_block_kind: PendingBlockKind::Full,
raw_tx_forwarder: ForwardConfig::default(),
send_raw_transaction_sync_timeout: RPC_DEFAULT_SEND_RAW_TX_SYNC_TIMEOUT_SECS,
rpc_evm_memory_limit: (1 << 32) - 1,
}
}
}
@@ -216,6 +219,12 @@ impl EthConfig {
self.send_raw_transaction_sync_timeout = timeout;
self
}
/// Configures the maximum memory the EVM can allocate per RPC request.
pub const fn rpc_evm_memory_limit(mut self, memory_limit: u64) -> Self {
self.rpc_evm_memory_limit = memory_limit;
self
}
}
/// Config for the filter

View File

@@ -619,6 +619,9 @@ pub enum RpcInvalidTransactionError {
/// Contains the gas limit.
#[error("out of gas: gas exhausted during memory expansion: {0}")]
MemoryOutOfGas(u64),
/// Memory limit was exceeded during memory expansion.
#[error("out of memory: memory limit exceeded during memory expansion")]
MemoryLimitOutOfGas,
/// Gas limit was exceeded during precompile execution.
/// Contains the gas limit.
#[error("out of gas: gas exhausted during precompiled contract execution: {0}")]
@@ -723,7 +726,8 @@ impl RpcInvalidTransactionError {
OutOfGasError::Basic | OutOfGasError::ReentrancySentry => {
Self::BasicOutOfGas(gas_limit)
}
OutOfGasError::Memory | OutOfGasError::MemoryLimit => Self::MemoryOutOfGas(gas_limit),
OutOfGasError::Memory => Self::MemoryOutOfGas(gas_limit),
OutOfGasError::MemoryLimit => Self::MemoryLimitOutOfGas,
OutOfGasError::Precompile => Self::PrecompileOutOfGas(gas_limit),
OutOfGasError::InvalidOperand => Self::InvalidOperandOutOfGas(gas_limit),
}

View File

@@ -63,7 +63,7 @@ alloy-rpc-types-txpool.workspace = true
alloy-rpc-types-admin.workspace = true
alloy-rpc-types-engine = { workspace = true, features = ["kzg"] }
alloy-serde.workspace = true
revm = { workspace = true, features = ["optional_block_gas_limit", "optional_eip3607", "optional_no_base_fee"] }
revm = { workspace = true, features = ["optional_block_gas_limit", "optional_eip3607", "optional_no_base_fee", "memory_limit"] }
revm-primitives = { workspace = true, features = ["serde"] }
# rpc

View File

@@ -44,6 +44,7 @@ pub struct EthApiBuilder<N: RpcNodeCore, Rpc, NextEnv = ()> {
pending_block_kind: PendingBlockKind,
raw_tx_forwarder: ForwardConfig,
send_raw_transaction_sync_timeout: Duration,
evm_memory_limit: u64,
}
impl<Provider, Pool, Network, EvmConfig, ChainSpec>
@@ -94,6 +95,7 @@ impl<N: RpcNodeCore, Rpc, NextEnv> EthApiBuilder<N, Rpc, NextEnv> {
pending_block_kind,
raw_tx_forwarder,
send_raw_transaction_sync_timeout,
evm_memory_limit,
} = self;
EthApiBuilder {
components,
@@ -114,6 +116,7 @@ impl<N: RpcNodeCore, Rpc, NextEnv> EthApiBuilder<N, Rpc, NextEnv> {
pending_block_kind,
raw_tx_forwarder,
send_raw_transaction_sync_timeout,
evm_memory_limit,
}
}
}
@@ -145,6 +148,7 @@ where
pending_block_kind: PendingBlockKind::Full,
raw_tx_forwarder: ForwardConfig::default(),
send_raw_transaction_sync_timeout: Duration::from_secs(30),
evm_memory_limit: (1 << 32) - 1,
}
}
}
@@ -183,6 +187,7 @@ where
pending_block_kind,
raw_tx_forwarder,
send_raw_transaction_sync_timeout,
evm_memory_limit,
} = self;
EthApiBuilder {
components,
@@ -203,6 +208,7 @@ where
pending_block_kind,
raw_tx_forwarder,
send_raw_transaction_sync_timeout,
evm_memory_limit,
}
}
@@ -230,6 +236,7 @@ where
pending_block_kind,
raw_tx_forwarder,
send_raw_transaction_sync_timeout,
evm_memory_limit,
} = self;
EthApiBuilder {
components,
@@ -250,6 +257,7 @@ where
pending_block_kind,
raw_tx_forwarder,
send_raw_transaction_sync_timeout,
evm_memory_limit,
}
}
@@ -477,6 +485,7 @@ where
pending_block_kind,
raw_tx_forwarder,
send_raw_transaction_sync_timeout,
evm_memory_limit,
} = self;
let provider = components.provider().clone();
@@ -517,6 +526,7 @@ where
pending_block_kind,
raw_tx_forwarder.forwarder_client(),
send_raw_transaction_sync_timeout,
evm_memory_limit,
)
}
@@ -541,4 +551,10 @@ where
self.send_raw_transaction_sync_timeout = timeout;
self
}
/// Sets the maximum memory the EVM can allocate per RPC request.
pub const fn evm_memory_limit(mut self, memory_limit: u64) -> Self {
self.evm_memory_limit = memory_limit;
self
}
}

View File

@@ -155,6 +155,7 @@ where
pending_block_kind: PendingBlockKind,
raw_tx_forwarder: ForwardConfig,
send_raw_transaction_sync_timeout: Duration,
evm_memory_limit: u64,
) -> Self {
let inner = EthApiInner::new(
components,
@@ -173,6 +174,7 @@ where
pending_block_kind,
raw_tx_forwarder.forwarder_client(),
send_raw_transaction_sync_timeout,
evm_memory_limit,
);
Self { inner: Arc::new(inner) }
@@ -318,6 +320,9 @@ pub struct EthApiInner<N: RpcNodeCore, Rpc: RpcConvert> {
/// Blob sidecar converter
blob_sidecar_converter: BlobSidecarConverter,
/// Maximum memory the EVM can allocate per RPC request.
evm_memory_limit: u64,
}
impl<N, Rpc> EthApiInner<N, Rpc>
@@ -344,6 +349,7 @@ where
pending_block_kind: PendingBlockKind,
raw_tx_forwarder: Option<RpcClient>,
send_raw_transaction_sync_timeout: Duration,
evm_memory_limit: u64,
) -> Self {
let signers = parking_lot::RwLock::new(Default::default());
// get the block number of the latest block
@@ -386,6 +392,7 @@ where
pending_block_kind,
send_raw_transaction_sync_timeout,
blob_sidecar_converter: BlobSidecarConverter::new(),
evm_memory_limit,
}
}
}
@@ -563,6 +570,12 @@ where
pub const fn blob_sidecar_converter(&self) -> &BlobSidecarConverter {
&self.blob_sidecar_converter
}
/// Returns the EVM memory limit.
#[inline]
pub const fn evm_memory_limit(&self) -> u64 {
self.evm_memory_limit
}
}
#[cfg(test)]

View File

@@ -31,6 +31,11 @@ where
fn max_simulate_blocks(&self) -> u64 {
self.inner.max_simulate_blocks()
}
#[inline]
fn evm_memory_limit(&self) -> u64 {
self.inner.evm_memory_limit()
}
}
impl<N, Rpc> EstimateCall for EthApi<N, Rpc>

View File

@@ -405,6 +405,11 @@ RPC:
[default: 50000000]
--rpc.evm-memory-limit <MEMORY_LIMIT>
Maximum memory the EVM can allocate per RPC request
[default: 4294967295]
--rpc.txfeecap <TX_FEE_CAP>
Maximum eth transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)

View File

@@ -32,7 +32,7 @@ reth-stateless = { workspace = true, features = ["secp256k1"] }
reth-tracing.workspace = true
reth-trie.workspace = true
reth-trie-db.workspace = true
revm = { workspace = true, features = ["secp256k1", "blst", "c-kzg"] }
revm = { workspace = true, features = ["secp256k1", "blst", "c-kzg", "memory_limit"] }
alloy-rlp.workspace = true
alloy-primitives.workspace = true