mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-19 03:04:27 -05:00
feat(rpc): add eth_getStorageValues batch storage slot retrieval (#22186)
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
committed by
GitHub
parent
1f3fd5da2e
commit
2e5560b444
6
.changelog/merry-koalas-nod.md
Normal file
6
.changelog/merry-koalas-nod.md
Normal 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.
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user