diff --git a/Cargo.lock b/Cargo.lock index 895db6e47a8..a4e0e1fd0a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index c3ae24ecea3..c6a9abad754 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/crates/ethereum/node/Cargo.toml b/crates/ethereum/node/Cargo.toml index 3c0efdb0394..1594c6fad96 100644 --- a/crates/ethereum/node/Cargo.toml +++ b/crates/ethereum/node/Cargo.toml @@ -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 diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index ed0a3fb64d4..a66d7b222e4 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -1156,6 +1156,7 @@ impl<'a, N: FullNodeComponents::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, diff --git a/crates/optimism/node/Cargo.toml b/crates/optimism/node/Cargo.toml index 162700ac0ae..fdccffb869b 100644 --- a/crates/optimism/node/Cargo.toml +++ b/crates/optimism/node/Cargo.toml @@ -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 diff --git a/crates/optimism/rpc/src/eth/call.rs b/crates/optimism/rpc/src/eth/call.rs index 4e853984ac9..db96bda83f3 100644 --- a/crates/optimism/rpc/src/eth/call.rs +++ b/crates/optimism/rpc/src/eth/call.rs @@ -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() + } } diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 488a685b382..92036e39085 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -68,3 +68,4 @@ optional-checks = [ "optional-eip3607", "optional-no-base-fee", ] +memory_limit = ["revm/memory_limit"] diff --git a/crates/rpc/rpc-builder/src/config.rs b/crates/rpc/rpc-builder/src/config.rs index 011e24d468b..4d57bdec7d8 100644 --- a/crates/rpc/rpc-builder/src/config.rs +++ b/crates/rpc/rpc-builder/src/config.rs @@ -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 { diff --git a/crates/rpc/rpc-eth-api/Cargo.toml b/crates/rpc/rpc-eth-api/Cargo.toml index 88a7f059323..830e8cf83ae 100644 --- a/crates/rpc/rpc-eth-api/Cargo.toml +++ b/crates/rpc/rpc-eth-api/Cargo.toml @@ -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"] } diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 7eb10c10534..05f0de87464 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -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: // 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(); diff --git a/crates/rpc/rpc-eth-types/src/builder/config.rs b/crates/rpc/rpc-eth-types/src/builder/config.rs index 47f15ae5ae7..ded50ab4a83 100644 --- a/crates/rpc/rpc-eth-types/src/builder/config.rs +++ b/crates/rpc/rpc-eth-types/src/builder/config.rs @@ -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 diff --git a/crates/rpc/rpc-eth-types/src/error/mod.rs b/crates/rpc/rpc-eth-types/src/error/mod.rs index ef65e4ccc2b..b8814785478 100644 --- a/crates/rpc/rpc-eth-types/src/error/mod.rs +++ b/crates/rpc/rpc-eth-types/src/error/mod.rs @@ -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), } diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index e028e47448d..81df4bff44f 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -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 diff --git a/crates/rpc/rpc/src/eth/builder.rs b/crates/rpc/rpc/src/eth/builder.rs index c34d268d64a..ff01903736b 100644 --- a/crates/rpc/rpc/src/eth/builder.rs +++ b/crates/rpc/rpc/src/eth/builder.rs @@ -44,6 +44,7 @@ pub struct EthApiBuilder { pending_block_kind: PendingBlockKind, raw_tx_forwarder: ForwardConfig, send_raw_transaction_sync_timeout: Duration, + evm_memory_limit: u64, } impl @@ -94,6 +95,7 @@ impl EthApiBuilder { pending_block_kind, raw_tx_forwarder, send_raw_transaction_sync_timeout, + evm_memory_limit, } = self; EthApiBuilder { components, @@ -114,6 +116,7 @@ impl EthApiBuilder { 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 + } } diff --git a/crates/rpc/rpc/src/eth/core.rs b/crates/rpc/rpc/src/eth/core.rs index d2e5cf124ec..4084168c4f6 100644 --- a/crates/rpc/rpc/src/eth/core.rs +++ b/crates/rpc/rpc/src/eth/core.rs @@ -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 { /// Blob sidecar converter blob_sidecar_converter: BlobSidecarConverter, + + /// Maximum memory the EVM can allocate per RPC request. + evm_memory_limit: u64, } impl EthApiInner @@ -344,6 +349,7 @@ where pending_block_kind: PendingBlockKind, raw_tx_forwarder: Option, 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)] diff --git a/crates/rpc/rpc/src/eth/helpers/call.rs b/crates/rpc/rpc/src/eth/helpers/call.rs index abe06cb55ec..ad9f020bd0c 100644 --- a/crates/rpc/rpc/src/eth/helpers/call.rs +++ b/crates/rpc/rpc/src/eth/helpers/call.rs @@ -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 EstimateCall for EthApi diff --git a/docs/vocs/docs/pages/cli/reth/node.mdx b/docs/vocs/docs/pages/cli/reth/node.mdx index 6f8b6ae88a7..b53dda3fb1a 100644 --- a/docs/vocs/docs/pages/cli/reth/node.mdx +++ b/docs/vocs/docs/pages/cli/reth/node.mdx @@ -405,6 +405,11 @@ RPC: [default: 50000000] + --rpc.evm-memory-limit + Maximum memory the EVM can allocate per RPC request + + [default: 4294967295] + --rpc.txfeecap Maximum eth transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap) diff --git a/testing/ef-tests/Cargo.toml b/testing/ef-tests/Cargo.toml index 745172cd82c..e9cf465a98d 100644 --- a/testing/ef-tests/Cargo.toml +++ b/testing/ef-tests/Cargo.toml @@ -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