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