feat(rpc): add eth_getStorageValues batch storage slot retrieval (#22186)

Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Georgios Konstantopoulos
2026-02-14 07:57:56 -08:00
committed by GitHub
parent 1f3fd5da2e
commit 2e5560b444
4 changed files with 71 additions and 0 deletions

View File

@@ -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.

View File

@@ -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<BlockId>,
) -> RpcResult<B256>;
/// Returns values from multiple storage positions across multiple addresses.
#[method(name = "getStorageValues")]
async fn storage_values(
&self,
requests: HashMap<Address, Vec<JsonStorageKey>>,
block_number: Option<BlockId>,
) -> RpcResult<HashMap<Address, Vec<B256>>>;
/// 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<Address, Vec<JsonStorageKey>>,
block_number: Option<BlockId>,
) -> RpcResult<HashMap<Address, Vec<B256>>> {
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,

View File

@@ -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<Address, Vec<JsonStorageKey>>,
block_id: Option<BlockId>,
) -> impl Future<Output = Result<HashMap<Address, Vec<B256>>, 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,

View File

@@ -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;