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, LoadBlock, 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::{BlockTy, RecoveredBlock, 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, sync::Arc};
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 EVM environment 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 EVM environment 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 recovered block, EVM environment, and state block id for the requested
357    /// [`BlockId`].
358    ///
359    /// For pending blocks, this preserves the state id returned by [`Self::evm_env_at`], which can
360    /// be the pending tag for an actual pending block or the latest block hash when the pending env
361    /// is derived from latest.
362    #[expect(clippy::type_complexity)]
363    fn evm_env_and_recovered_block_at(
364        &self,
365        at: BlockId,
366    ) -> impl Future<
367        Output = Result<
368            (Arc<RecoveredBlock<BlockTy<Self::Primitives>>>, EvmEnvFor<Self::Evm>, BlockId),
369            Self::Error,
370        >,
371    > + Send
372    where
373        Self: SpawnBlocking + LoadBlock,
374    {
375        async move {
376            if at.is_pending() {
377                let (evm_env, block_id) = self.evm_env_at(at).await?;
378                let block = self
379                    .recovered_block(block_id)
380                    .await?
381                    .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
382
383                Ok((block, evm_env, block_id))
384            } else {
385                let block = self
386                    .recovered_block(at)
387                    .await?
388                    .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
389                let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
390                let block_id = block.hash().into();
391
392                Ok((block, evm_env, block_id))
393            }
394        }
395    }
396
397    /// Returns the next available nonce without gaps for the given address
398    /// Next available nonce is either the on chain nonce of the account or the highest consecutive
399    /// nonce in the pool + 1
400    ///
401    /// The provided request must have a from address set.
402    fn next_available_nonce_for(
403        &self,
404        request: &RpcTxReq<Self::NetworkTypes>,
405    ) -> impl Future<Output = Result<u64, Self::Error>> + Send
406    where
407        Self: SpawnBlocking,
408    {
409        let address = request.as_ref().from;
410        self.spawn_blocking_io(move |this| {
411            let address = match address {
412                Some(address) => address,
413                None => return Err(SignError::NoAccount.into_eth_err()),
414            };
415
416            // first fetch the on chain nonce of the account
417            let mut next_nonce = this
418                .latest_state()?
419                .account_nonce(&address)
420                .map_err(Self::Error::from_eth_err)?
421                .unwrap_or_default();
422
423            // Retrieve the highest consecutive transaction for the sender from the transaction pool
424            if let Some(highest_tx) =
425                this.pool().get_highest_consecutive_transaction_by_sender(address, next_nonce)
426            {
427                // Return the nonce of the highest consecutive transaction + 1
428                next_nonce = highest_tx.nonce().checked_add(1).ok_or_else(|| {
429                    Self::Error::from(EthApiError::InvalidTransaction(
430                        RpcInvalidTransactionError::NonceMaxValue,
431                    ))
432                })?;
433            }
434
435            Ok(next_nonce)
436        })
437    }
438
439    /// Returns the number of transactions sent from an address at the given block identifier.
440    ///
441    /// If this is [`BlockNumberOrTag::Pending`](alloy_eips::BlockNumberOrTag) then this will
442    /// look up the highest transaction in pool and return the next nonce (highest + 1).
443    fn transaction_count(
444        &self,
445        address: Address,
446        block_id: Option<BlockId>,
447    ) -> impl Future<Output = Result<U256, Self::Error>> + Send
448    where
449        Self: SpawnBlocking,
450    {
451        self.spawn_blocking_io_fut(async move |this| {
452            // first fetch the on chain nonce of the account
453            let on_chain_account_nonce = this
454                .state_at_block_id_or_latest(block_id)
455                .await?
456                .account_nonce(&address)
457                .map_err(Self::Error::from_eth_err)?
458                .unwrap_or_default();
459
460            if block_id == Some(BlockId::pending()) {
461                // for pending tag we need to find the highest nonce of txn in the pending state.
462                if let Some(highest_pool_tx) = this
463                    .pool()
464                    .get_highest_consecutive_transaction_by_sender(address, on_chain_account_nonce)
465                {
466                    {
467                        // and the corresponding txcount is nonce + 1 of the highest tx in the pool
468                        // (on chain nonce is increased after tx)
469                        let next_tx_nonce =
470                            highest_pool_tx.nonce().checked_add(1).ok_or_else(|| {
471                                Self::Error::from(EthApiError::InvalidTransaction(
472                                    RpcInvalidTransactionError::NonceMaxValue,
473                                ))
474                            })?;
475
476                        // guard against drifts in the pool
477                        let next_tx_nonce = on_chain_account_nonce.max(next_tx_nonce);
478
479                        let tx_count = on_chain_account_nonce.max(next_tx_nonce);
480                        return Ok(U256::from(tx_count));
481                    }
482                }
483            }
484            Ok(U256::from(on_chain_account_nonce))
485        })
486    }
487
488    /// Returns code of given account, at the given identifier.
489    fn get_code(
490        &self,
491        address: Address,
492        block_id: Option<BlockId>,
493    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send
494    where
495        Self: SpawnBlocking,
496    {
497        self.spawn_blocking_io_fut(async move |this| {
498            Ok(this
499                .state_at_block_id_or_latest(block_id)
500                .await?
501                .account_code(&address)
502                .map_err(Self::Error::from_eth_err)?
503                .unwrap_or_default()
504                .original_bytes())
505        })
506    }
507}