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