Skip to main content

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