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