diff --git a/.changelog/merry-koalas-nod.md b/.changelog/merry-koalas-nod.md new file mode 100644 index 0000000000..626d13ed01 --- /dev/null +++ b/.changelog/merry-koalas-nod.md @@ -0,0 +1,6 @@ +--- +reth-rpc-eth-api: minor +reth-rpc-server-types: minor +--- + +Added `eth_getStorageValues` RPC method for batch storage slot retrieval across multiple addresses. diff --git a/crates/rpc/rpc-eth-api/src/core.rs b/crates/rpc/rpc-eth-api/src/core.rs index 946154d381..0550f430d3 100644 --- a/crates/rpc/rpc-eth-api/src/core.rs +++ b/crates/rpc/rpc-eth-api/src/core.rs @@ -20,6 +20,7 @@ use reth_primitives_traits::TxTy; use reth_rpc_convert::RpcTxReq; use reth_rpc_eth_types::FillTransaction; use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult}; +use std::collections::HashMap; use tracing::trace; /// Helper trait, unifies functionality that must be supported to implement all RPC methods for @@ -201,6 +202,14 @@ pub trait EthApi< block_number: Option, ) -> RpcResult; + /// Returns values from multiple storage positions across multiple addresses. + #[method(name = "getStorageValues")] + async fn storage_values( + &self, + requests: HashMap>, + block_number: Option, + ) -> RpcResult>>; + /// Returns the number of transactions sent from an address at given block number. #[method(name = "getTransactionCount")] async fn transaction_count( @@ -651,6 +660,16 @@ where Ok(EthState::storage_at(self, address, index, block_number).await?) } + /// Handler for: `eth_getStorageValues` + async fn storage_values( + &self, + requests: HashMap>, + block_number: Option, + ) -> RpcResult>> { + trace!(target: "rpc::eth", ?block_number, "Serving eth_getStorageValues"); + Ok(EthState::storage_values(self, requests, block_number).await?) + } + /// Handler for: `eth_getTransactionCount` async fn transaction_count( &self, diff --git a/crates/rpc/rpc-eth-api/src/helpers/state.rs b/crates/rpc/rpc-eth-api/src/helpers/state.rs index 0638e1eea1..b004c9ca4a 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/state.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/state.rs @@ -16,11 +16,13 @@ use reth_rpc_convert::RpcConvert; use reth_rpc_eth_types::{ error::FromEvmError, EthApiError, PendingBlockEnv, RpcInvalidTransactionError, }; +use reth_rpc_server_types::constants::DEFAULT_MAX_STORAGE_VALUES_SLOTS; use reth_storage_api::{ BlockIdReader, BlockNumReader, BlockReaderIdExt, StateProvider, StateProviderBox, StateProviderFactory, }; use reth_transaction_pool::TransactionPool; +use std::collections::HashMap; /// Helper methods for `eth_` methods relating to state (accounts). pub trait EthState: LoadState + SpawnBlocking { @@ -83,6 +85,47 @@ pub trait EthState: LoadState + SpawnBlocking { }) } + /// Returns values from multiple storage positions across multiple addresses. + /// + /// Enforces a cap on total slot count (sum of all slot arrays) and returns an error if + /// exceeded. + fn storage_values( + &self, + requests: HashMap>, + block_id: Option, + ) -> impl Future>, Self::Error>> + Send { + async move { + let total_slots: usize = requests.values().map(|slots| slots.len()).sum(); + if total_slots > DEFAULT_MAX_STORAGE_VALUES_SLOTS { + return Err(Self::Error::from_eth_err(EthApiError::InvalidParams( + format!( + "total slot count {total_slots} exceeds limit {DEFAULT_MAX_STORAGE_VALUES_SLOTS}", + ), + ))); + } + + self.spawn_blocking_io_fut(move |this| async move { + let state = this.state_at_block_id_or_latest(block_id).await?; + + let mut result = HashMap::with_capacity(requests.len()); + for (address, slots) in requests { + let mut values = Vec::with_capacity(slots.len()); + for slot in &slots { + let value = state + .storage(address, slot.as_b256()) + .map_err(Self::Error::from_eth_err)? + .unwrap_or_default(); + values.push(B256::new(value.to_be_bytes())); + } + result.insert(address, values); + } + + Ok(result) + }) + .await + } + } + /// Returns values stored of given account, with Merkle-proof, at given blocknumber. fn get_proof( &self, diff --git a/crates/rpc/rpc-server-types/src/constants.rs b/crates/rpc/rpc-server-types/src/constants.rs index ed03dc8566..ddb3619178 100644 --- a/crates/rpc/rpc-server-types/src/constants.rs +++ b/crates/rpc/rpc-server-types/src/constants.rs @@ -55,6 +55,9 @@ pub const DEFAULT_ENGINE_API_IPC_ENDPOINT: &str = "/tmp/reth_engine_api.ipc"; /// The default limit for blocks count in `eth_simulateV1`. pub const DEFAULT_MAX_SIMULATE_BLOCKS: u64 = 256; +/// The default maximum number of total storage slots for `eth_getStorageValues`. +pub const DEFAULT_MAX_STORAGE_VALUES_SLOTS: usize = 1024; + /// The default eth historical proof window. pub const DEFAULT_ETH_PROOF_WINDOW: u64 = 0;