reth_rpc_eth_api/helpers/
state.rs

1//! Loads a pending block from database. Helper trait for `eth_` block, transaction, call and trace
2//! RPC methods.
3use super::{EthApiSpec, LoadPendingBlock, SpawnBlocking};
4use crate::{EthApiTypes, FromEthApiError, RpcNodeCore, RpcNodeCoreExt};
5use alloy_consensus::constants::KECCAK_EMPTY;
6use alloy_eips::BlockId;
7use alloy_primitives::{Address, Bytes, B256, U256};
8use alloy_rpc_types_eth::{Account, AccountInfo, EIP1186AccountProofResponse};
9use alloy_serde::JsonStorageKey;
10use futures::Future;
11use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
12use reth_errors::RethError;
13use reth_evm::{ConfigureEvm, EvmEnvFor};
14use reth_rpc_eth_types::{EthApiError, PendingBlockEnv, RpcInvalidTransactionError};
15use reth_storage_api::{
16    BlockIdReader, BlockNumReader, StateProvider, StateProviderBox, StateProviderFactory,
17};
18use reth_transaction_pool::TransactionPool;
19
20/// Helper methods for `eth_` methods relating to state (accounts).
21pub trait EthState: LoadState + SpawnBlocking {
22    /// Returns the maximum number of blocks into the past for generating state proofs.
23    fn max_proof_window(&self) -> u64;
24
25    /// Returns the number of transactions sent from an address at the given block identifier.
26    ///
27    /// If this is [`BlockNumberOrTag::Pending`](alloy_eips::BlockNumberOrTag) then this will
28    /// look up the highest transaction in pool and return the next nonce (highest + 1).
29    fn transaction_count(
30        &self,
31        address: Address,
32        block_id: Option<BlockId>,
33    ) -> impl Future<Output = Result<U256, Self::Error>> + Send {
34        LoadState::transaction_count(self, address, block_id)
35    }
36
37    /// Returns code of given account, at given blocknumber.
38    fn get_code(
39        &self,
40        address: Address,
41        block_id: Option<BlockId>,
42    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send {
43        LoadState::get_code(self, address, block_id)
44    }
45
46    /// Returns balance of given account, at given blocknumber.
47    fn balance(
48        &self,
49        address: Address,
50        block_id: Option<BlockId>,
51    ) -> impl Future<Output = Result<U256, Self::Error>> + Send {
52        self.spawn_blocking_io(move |this| {
53            Ok(this
54                .state_at_block_id_or_latest(block_id)?
55                .account_balance(&address)
56                .map_err(Self::Error::from_eth_err)?
57                .unwrap_or_default())
58        })
59    }
60
61    /// Returns values stored of given account, at given blocknumber.
62    fn storage_at(
63        &self,
64        address: Address,
65        index: JsonStorageKey,
66        block_id: Option<BlockId>,
67    ) -> impl Future<Output = Result<B256, Self::Error>> + Send {
68        self.spawn_blocking_io(move |this| {
69            let storage_value = this
70                .state_at_block_id_or_latest(block_id)?
71                .storage(address, index.as_b256())
72                .map_err(Self::Error::from_eth_err)?
73                .unwrap_or_default();
74            match storage_value.is_public() {
75                true => Ok(B256::new(storage_value.value.to_be_bytes())),
76                false => Ok(B256::ZERO),
77            }
78        })
79    }
80
81    /// Returns values stored of given account, with Merkle-proof, at given blocknumber.
82    fn get_proof(
83        &self,
84        address: Address,
85        keys: Vec<JsonStorageKey>,
86        block_id: Option<BlockId>,
87    ) -> Result<
88        impl Future<Output = Result<EIP1186AccountProofResponse, Self::Error>> + Send,
89        Self::Error,
90    >
91    where
92        Self: EthApiSpec,
93    {
94        Ok(async move {
95            let _permit = self
96                .acquire_owned()
97                .await
98                .map_err(RethError::other)
99                .map_err(EthApiError::Internal)?;
100
101            let chain_info = self.chain_info().map_err(Self::Error::from_eth_err)?;
102            let block_id = block_id.unwrap_or_default();
103
104            // Check whether the distance to the block exceeds the maximum configured window.
105            let block_number = self
106                .provider()
107                .block_number_for_id(block_id)
108                .map_err(Self::Error::from_eth_err)?
109                .ok_or(EthApiError::HeaderNotFound(block_id))?;
110            let max_window = self.max_proof_window();
111            if chain_info.best_number.saturating_sub(block_number) > max_window {
112                return Err(EthApiError::ExceedsMaxProofWindow.into())
113            }
114
115            self.spawn_blocking_io(move |this| {
116                let state = this.state_at_block_id(block_id)?;
117                let storage_keys = keys.iter().map(|key| key.as_b256()).collect::<Vec<_>>();
118                let proof = state
119                    .proof(Default::default(), address, &storage_keys)
120                    .map_err(Self::Error::from_eth_err)?;
121                Ok(proof.into_eip1186_response(keys))
122            })
123            .await
124        })
125    }
126
127    /// Returns the account at the given address for the provided block identifier.
128    fn get_account(
129        &self,
130        address: Address,
131        block_id: BlockId,
132    ) -> impl Future<Output = Result<Option<Account>, Self::Error>> + Send {
133        self.spawn_blocking_io(move |this| {
134            let state = this.state_at_block_id(block_id)?;
135            let account = state.basic_account(&address).map_err(Self::Error::from_eth_err)?;
136            let Some(account) = account else { return Ok(None) };
137
138            // Check whether the distance to the block exceeds the maximum configured proof window.
139            let chain_info = this.provider().chain_info().map_err(Self::Error::from_eth_err)?;
140            let block_number = this
141                .provider()
142                .block_number_for_id(block_id)
143                .map_err(Self::Error::from_eth_err)?
144                .ok_or(EthApiError::HeaderNotFound(block_id))?;
145            let max_window = this.max_proof_window();
146            if chain_info.best_number.saturating_sub(block_number) > max_window {
147                return Err(EthApiError::ExceedsMaxProofWindow.into())
148            }
149
150            let balance = account.balance;
151            let nonce = account.nonce;
152            let code_hash = account.bytecode_hash.unwrap_or(KECCAK_EMPTY);
153
154            // Provide a default `HashedStorage` value in order to
155            // get the storage root hash of the current state.
156            let storage_root = state
157                .storage_root(address, Default::default())
158                .map_err(Self::Error::from_eth_err)?;
159
160            Ok(Some(Account { balance, nonce, code_hash, storage_root }))
161        })
162    }
163
164    /// Retrieves the account's balance, nonce, and code for a given address.
165    fn get_account_info(
166        &self,
167        address: Address,
168        block_id: BlockId,
169    ) -> impl Future<Output = Result<AccountInfo, Self::Error>> + Send {
170        self.spawn_blocking_io(move |this| {
171            let state = this.state_at_block_id(block_id)?;
172            let account = state
173                .basic_account(&address)
174                .map_err(Self::Error::from_eth_err)?
175                .unwrap_or_default();
176
177            let balance = account.balance;
178            let nonce = account.nonce;
179            let code = if account.get_bytecode_hash() == KECCAK_EMPTY {
180                Default::default()
181            } else {
182                state
183                    .account_code(&address)
184                    .map_err(Self::Error::from_eth_err)?
185                    .unwrap_or_default()
186                    .original_bytes()
187            };
188
189            Ok(AccountInfo { balance, nonce, code })
190        })
191    }
192}
193
194/// Loads state from database.
195///
196/// Behaviour shared by several `eth_` RPC methods, not exclusive to `eth_` state RPC methods.
197pub trait LoadState:
198    EthApiTypes
199    + RpcNodeCoreExt<
200        Provider: StateProviderFactory
201                      + ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks>,
202        Pool: TransactionPool,
203    >
204{
205    /// Returns the state at the given block number
206    fn state_at_hash(&self, block_hash: B256) -> Result<StateProviderBox, Self::Error> {
207        self.provider().history_by_block_hash(block_hash).map_err(Self::Error::from_eth_err)
208    }
209
210    /// Returns the state at the given [`BlockId`] enum.
211    ///
212    /// Note: if not [`BlockNumberOrTag::Pending`](alloy_eips::BlockNumberOrTag) then this
213    /// will only return canonical state. See also <https://github.com/paradigmxyz/reth/issues/4515>
214    fn state_at_block_id(&self, at: BlockId) -> Result<StateProviderBox, Self::Error> {
215        self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)
216    }
217
218    /// Returns the _latest_ state
219    fn latest_state(&self) -> Result<StateProviderBox, Self::Error> {
220        self.provider().latest().map_err(Self::Error::from_eth_err)
221    }
222
223    /// Returns the state at the given [`BlockId`] enum or the latest.
224    ///
225    /// Convenience function to interprets `None` as `BlockId::Number(BlockNumberOrTag::Latest)`
226    fn state_at_block_id_or_latest(
227        &self,
228        block_id: Option<BlockId>,
229    ) -> Result<StateProviderBox, Self::Error> {
230        if let Some(block_id) = block_id {
231            self.state_at_block_id(block_id)
232        } else {
233            Ok(self.latest_state()?)
234        }
235    }
236
237    /// Returns the revm evm env for the requested [`BlockId`]
238    ///
239    /// If the [`BlockId`] this will return the [`BlockId`] of the block the env was configured
240    /// for.
241    /// If the [`BlockId`] is pending, this will return the "Pending" tag, otherwise this returns
242    /// the hash of the exact block.
243    fn evm_env_at(
244        &self,
245        at: BlockId,
246    ) -> impl Future<Output = Result<(EvmEnvFor<Self::Evm>, BlockId), Self::Error>> + Send
247    where
248        Self: LoadPendingBlock + SpawnBlocking,
249    {
250        async move {
251            if at.is_pending() {
252                let PendingBlockEnv { evm_env, origin } = self.pending_block_env_and_cfg()?;
253                Ok((evm_env, origin.state_block_id()))
254            } else {
255                // Use cached values if there is no pending block
256                let block_hash = RpcNodeCore::provider(self)
257                    .block_hash_for_id(at)
258                    .map_err(Self::Error::from_eth_err)?
259                    .ok_or(EthApiError::HeaderNotFound(at))?;
260
261                let header =
262                    self.cache().get_header(block_hash).await.map_err(Self::Error::from_eth_err)?;
263                let evm_env = self.evm_config().evm_env(&header);
264
265                Ok((evm_env, block_hash.into()))
266            }
267        }
268    }
269
270    /// Returns the next available nonce without gaps for the given address
271    /// Next available nonce is either the on chain nonce of the account or the highest consecutive
272    /// nonce in the pool + 1
273    fn next_available_nonce(
274        &self,
275        address: Address,
276    ) -> impl Future<Output = Result<u64, Self::Error>> + Send
277    where
278        Self: SpawnBlocking,
279    {
280        self.spawn_blocking_io(move |this| {
281            // first fetch the on chain nonce of the account
282            let on_chain_account_nonce = this
283                .latest_state()?
284                .account_nonce(&address)
285                .map_err(Self::Error::from_eth_err)?
286                .unwrap_or_default();
287
288            let mut next_nonce = on_chain_account_nonce;
289            // Retrieve the highest consecutive transaction for the sender from the transaction pool
290            if let Some(highest_tx) = this
291                .pool()
292                .get_highest_consecutive_transaction_by_sender(address, on_chain_account_nonce)
293            {
294                // Return the nonce of the highest consecutive transaction + 1
295                next_nonce = highest_tx.nonce().checked_add(1).ok_or_else(|| {
296                    Self::Error::from(EthApiError::InvalidTransaction(
297                        RpcInvalidTransactionError::NonceMaxValue,
298                    ))
299                })?;
300            }
301
302            Ok(next_nonce)
303        })
304    }
305
306    /// Returns the number of transactions sent from an address at the given block identifier.
307    ///
308    /// If this is [`BlockNumberOrTag::Pending`](alloy_eips::BlockNumberOrTag) then this will
309    /// look up the highest transaction in pool and return the next nonce (highest + 1).
310    fn transaction_count(
311        &self,
312        address: Address,
313        block_id: Option<BlockId>,
314    ) -> impl Future<Output = Result<U256, Self::Error>> + Send
315    where
316        Self: SpawnBlocking,
317    {
318        self.spawn_blocking_io(move |this| {
319            // first fetch the on chain nonce of the account
320            let on_chain_account_nonce = this
321                .state_at_block_id_or_latest(block_id)?
322                .account_nonce(&address)
323                .map_err(Self::Error::from_eth_err)?
324                .unwrap_or_default();
325
326            if block_id == Some(BlockId::pending()) {
327                // for pending tag we need to find the highest nonce of txn in the pending state.
328                if let Some(highest_pool_tx) = this
329                    .pool()
330                    .get_highest_consecutive_transaction_by_sender(address, on_chain_account_nonce)
331                {
332                    {
333                        // and the corresponding txcount is nonce + 1 of the highest tx in the pool
334                        // (on chain nonce is increased after tx)
335                        let next_tx_nonce =
336                            highest_pool_tx.nonce().checked_add(1).ok_or_else(|| {
337                                Self::Error::from(EthApiError::InvalidTransaction(
338                                    RpcInvalidTransactionError::NonceMaxValue,
339                                ))
340                            })?;
341
342                        // guard against drifts in the pool
343                        let next_tx_nonce = on_chain_account_nonce.max(next_tx_nonce);
344
345                        let tx_count = on_chain_account_nonce.max(next_tx_nonce);
346                        return Ok(U256::from(tx_count));
347                    }
348                }
349            }
350            Ok(U256::from(on_chain_account_nonce))
351        })
352    }
353
354    /// Returns code of given account, at the given identifier.
355    fn get_code(
356        &self,
357        address: Address,
358        block_id: Option<BlockId>,
359    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send
360    where
361        Self: SpawnBlocking,
362    {
363        self.spawn_blocking_io(move |this| {
364            Ok(this
365                .state_at_block_id_or_latest(block_id)?
366                .account_code(&address)
367                .map_err(Self::Error::from_eth_err)?
368                .unwrap_or_default()
369                .original_bytes())
370        })
371    }
372}