reth_rpc_eth_api/helpers/
state.rsuse super::{EthApiSpec, LoadPendingBlock, SpawnBlocking};
use crate::{EthApiTypes, FromEthApiError, RpcNodeCore, RpcNodeCoreExt};
use alloy_consensus::constants::KECCAK_EMPTY;
use alloy_eips::BlockId;
use alloy_primitives::{Address, Bytes, B256, U256};
use alloy_rpc_types_eth::{Account, EIP1186AccountProofResponse};
use alloy_serde::JsonStorageKey;
use futures::Future;
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_errors::RethError;
use reth_evm::env::EvmEnv;
use reth_provider::{
BlockIdReader, BlockNumReader, ChainSpecProvider, EvmEnvProvider as _, StateProvider,
StateProviderBox, StateProviderFactory,
};
use reth_rpc_eth_types::{EthApiError, PendingBlockEnv, RpcInvalidTransactionError};
use reth_transaction_pool::TransactionPool;
pub trait EthState: LoadState + SpawnBlocking {
fn max_proof_window(&self) -> u64;
fn transaction_count(
&self,
address: Address,
block_id: Option<BlockId>,
) -> impl Future<Output = Result<U256, Self::Error>> + Send {
LoadState::transaction_count(self, address, block_id)
}
fn get_code(
&self,
address: Address,
block_id: Option<BlockId>,
) -> impl Future<Output = Result<Bytes, Self::Error>> + Send {
LoadState::get_code(self, address, block_id)
}
fn balance(
&self,
address: Address,
block_id: Option<BlockId>,
) -> impl Future<Output = Result<U256, Self::Error>> + Send {
self.spawn_blocking_io(move |this| {
Ok(this
.state_at_block_id_or_latest(block_id)?
.account_balance(address)
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default())
})
}
fn storage_at(
&self,
address: Address,
index: JsonStorageKey,
block_id: Option<BlockId>,
) -> impl Future<Output = Result<B256, Self::Error>> + Send {
self.spawn_blocking_io(move |this| {
Ok(B256::new(
this.state_at_block_id_or_latest(block_id)?
.storage(address, index.as_b256())
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default()
.to_be_bytes(),
))
})
}
fn get_proof(
&self,
address: Address,
keys: Vec<JsonStorageKey>,
block_id: Option<BlockId>,
) -> Result<
impl Future<Output = Result<EIP1186AccountProofResponse, Self::Error>> + Send,
Self::Error,
>
where
Self: EthApiSpec,
{
Ok(async move {
let _permit = self
.acquire_owned()
.await
.map_err(RethError::other)
.map_err(EthApiError::Internal)?;
let chain_info = self.chain_info().map_err(Self::Error::from_eth_err)?;
let block_id = block_id.unwrap_or_default();
let block_number = self
.provider()
.block_number_for_id(block_id)
.map_err(Self::Error::from_eth_err)?
.ok_or(EthApiError::HeaderNotFound(block_id))?;
let max_window = self.max_proof_window();
if chain_info.best_number.saturating_sub(block_number) > max_window {
return Err(EthApiError::ExceedsMaxProofWindow.into())
}
self.spawn_blocking_io(move |this| {
let state = this.state_at_block_id(block_id)?;
let storage_keys = keys.iter().map(|key| key.as_b256()).collect::<Vec<_>>();
let proof = state
.proof(Default::default(), address, &storage_keys)
.map_err(Self::Error::from_eth_err)?;
Ok(proof.into_eip1186_response(keys))
})
.await
})
}
fn get_account(
&self,
address: Address,
block_id: BlockId,
) -> impl Future<Output = Result<Option<Account>, Self::Error>> + Send {
self.spawn_blocking_io(move |this| {
let state = this.state_at_block_id(block_id)?;
let account = state.basic_account(address).map_err(Self::Error::from_eth_err)?;
let Some(account) = account else { return Ok(None) };
let chain_info = this.provider().chain_info().map_err(Self::Error::from_eth_err)?;
let block_number = this
.provider()
.block_number_for_id(block_id)
.map_err(Self::Error::from_eth_err)?
.ok_or(EthApiError::HeaderNotFound(block_id))?;
let max_window = this.max_proof_window();
if chain_info.best_number.saturating_sub(block_number) > max_window {
return Err(EthApiError::ExceedsMaxProofWindow.into())
}
let balance = account.balance;
let nonce = account.nonce;
let code_hash = account.bytecode_hash.unwrap_or(KECCAK_EMPTY);
let storage_root = state
.storage_root(address, Default::default())
.map_err(Self::Error::from_eth_err)?;
Ok(Some(Account { balance, nonce, code_hash, storage_root }))
})
}
}
pub trait LoadState:
EthApiTypes
+ RpcNodeCoreExt<
Provider: StateProviderFactory
+ ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks>,
Pool: TransactionPool,
>
{
fn state_at_hash(&self, block_hash: B256) -> Result<StateProviderBox, Self::Error> {
self.provider().history_by_block_hash(block_hash).map_err(Self::Error::from_eth_err)
}
fn state_at_block_id(&self, at: BlockId) -> Result<StateProviderBox, Self::Error> {
self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)
}
fn latest_state(&self) -> Result<StateProviderBox, Self::Error> {
self.provider().latest().map_err(Self::Error::from_eth_err)
}
fn state_at_block_id_or_latest(
&self,
block_id: Option<BlockId>,
) -> Result<StateProviderBox, Self::Error> {
if let Some(block_id) = block_id {
self.state_at_block_id(block_id)
} else {
Ok(self.latest_state()?)
}
}
fn evm_env_at(
&self,
at: BlockId,
) -> impl Future<Output = Result<(EvmEnv, BlockId), Self::Error>> + Send
where
Self: LoadPendingBlock + SpawnBlocking,
{
async move {
if at.is_pending() {
let PendingBlockEnv { cfg, block_env, origin } =
self.pending_block_env_and_cfg()?;
Ok(((cfg, block_env).into(), origin.state_block_id()))
} else {
let block_hash = RpcNodeCore::provider(self)
.block_hash_for_id(at)
.map_err(Self::Error::from_eth_err)?
.ok_or(EthApiError::HeaderNotFound(at))?;
let header =
self.cache().get_header(block_hash).await.map_err(Self::Error::from_eth_err)?;
let evm_env = self
.provider()
.env_with_header(&header, self.evm_config().clone())
.map_err(Self::Error::from_eth_err)?;
Ok((evm_env, block_hash.into()))
}
}
}
fn next_available_nonce(
&self,
address: Address,
) -> impl Future<Output = Result<u64, Self::Error>> + Send
where
Self: SpawnBlocking,
{
self.spawn_blocking_io(move |this| {
let on_chain_account_nonce = this
.latest_state()?
.account_nonce(address)
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default();
let mut next_nonce = on_chain_account_nonce;
if let Some(highest_tx) = this
.pool()
.get_highest_consecutive_transaction_by_sender(address, on_chain_account_nonce)
{
next_nonce = highest_tx.nonce().checked_add(1).ok_or_else(|| {
Self::Error::from(EthApiError::InvalidTransaction(
RpcInvalidTransactionError::NonceMaxValue,
))
})?;
}
Ok(next_nonce)
})
}
fn transaction_count(
&self,
address: Address,
block_id: Option<BlockId>,
) -> impl Future<Output = Result<U256, Self::Error>> + Send
where
Self: SpawnBlocking,
{
self.spawn_blocking_io(move |this| {
let on_chain_account_nonce = this
.state_at_block_id_or_latest(block_id)?
.account_nonce(address)
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default();
if block_id == Some(BlockId::pending()) {
if let Some(highest_pool_tx) =
this.pool().get_highest_transaction_by_sender(address)
{
{
let next_tx_nonce =
highest_pool_tx.nonce().checked_add(1).ok_or_else(|| {
Self::Error::from(EthApiError::InvalidTransaction(
RpcInvalidTransactionError::NonceMaxValue,
))
})?;
let next_tx_nonce = on_chain_account_nonce.max(next_tx_nonce);
let tx_count = on_chain_account_nonce.max(next_tx_nonce);
return Ok(U256::from(tx_count));
}
}
}
Ok(U256::from(on_chain_account_nonce))
})
}
fn get_code(
&self,
address: Address,
block_id: Option<BlockId>,
) -> impl Future<Output = Result<Bytes, Self::Error>> + Send
where
Self: SpawnBlocking,
{
self.spawn_blocking_io(move |this| {
Ok(this
.state_at_block_id_or_latest(block_id)?
.account_code(address)
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default()
.original_bytes())
})
}
}