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