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::{MultiProofTargetsV2, ProofV2Target};
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(move |this| async move {
179                let _permit = permit;
180                let state = this.state_at_block_id(block_id).await?;
181                let storage_keys = keys.iter().map(|key| key.as_b256()).collect::<Vec<_>>();
182                let proof = state
183                    .proof(Default::default(), address, &storage_keys)
184                    .map_err(Self::Error::from_eth_err)?;
185                Ok(proof.into_eip1186_response(keys))
186            })
187            .await
188        })
189    }
190
191    /// Returns account and storage proofs for multiple targets at the given block number.
192    fn get_multi_proof(
193        &self,
194        targets: Vec<(Address, Vec<B256>)>,
195        block_id: Option<BlockId>,
196    ) -> Result<
197        impl Future<Output = Result<Vec<EIP1186AccountProofResponse>, Self::Error>> + Send,
198        Self::Error,
199    >
200    where
201        Self: EthApiSpec,
202    {
203        Ok(async move {
204            let permit = self
205                .acquire_owned_tracing()
206                .await
207                .map_err(RethError::other)
208                .map_err(EthApiError::Internal)?;
209
210            let block_id = block_id.unwrap_or_default();
211            self.ensure_within_proof_window(block_id)?;
212
213            self.spawn_blocking_io_fut(move |this| async move {
214                let _permit = permit;
215                let state = this.state_at_block_id(block_id).await?;
216                let mut proof_targets = MultiProofTargetsV2::default();
217                proof_targets.account_targets.reserve(targets.len());
218                proof_targets.storage_targets.reserve(targets.len());
219                for (address, slots) in &targets {
220                    let hashed_address = keccak256(address);
221                    proof_targets.account_targets.push(ProofV2Target::new(hashed_address));
222                    proof_targets
223                        .storage_targets
224                        .entry(hashed_address)
225                        .or_default()
226                        .extend(slots.iter().map(|slot| ProofV2Target::new(keccak256(slot))));
227                }
228
229                let multiproof = state
230                    .multiproof_v2(Default::default(), proof_targets)
231                    .map_err(Self::Error::from_eth_err)?;
232
233                targets
234                    .into_iter()
235                    .map(|(address, slots)| {
236                        let proof = multiproof
237                            .account_proof(address, &slots)
238                            .map_err(RethError::other)
239                            .map_err(Self::Error::from_eth_err)?;
240                        let storage_keys =
241                            slots.into_iter().map(JsonStorageKey::from).collect::<Vec<_>>();
242                        Ok(proof.into_eip1186_response(storage_keys))
243                    })
244                    .collect::<Result<Vec<_>, Self::Error>>()
245            })
246            .await
247        })
248    }
249
250    /// Returns the account at the given address for the provided block identifier.
251    fn get_account(
252        &self,
253        address: Address,
254        block_id: BlockId,
255    ) -> impl Future<Output = Result<Option<Account>, Self::Error>> + Send
256    where
257        Self: EthApiSpec,
258    {
259        async move {
260            self.ensure_within_proof_window(block_id)?;
261
262            self.spawn_blocking_io_fut(async move |this| {
263                let state = this.state_at_block_id(block_id).await?;
264                let account = state.basic_account(&address).map_err(Self::Error::from_eth_err)?;
265                let Some(account) = account else { return Ok(None) };
266
267                let balance = account.balance;
268                let nonce = account.nonce;
269                let code_hash = account.bytecode_hash.unwrap_or(KECCAK_EMPTY);
270
271                // Provide a default `HashedStorage` value in order to
272                // get the storage root hash of the current state.
273                let storage_root = state
274                    .storage_root(address, Default::default())
275                    .map_err(Self::Error::from_eth_err)?;
276
277                Ok(Some(Account { balance, nonce, code_hash, storage_root }))
278            })
279            .await
280        }
281    }
282
283    /// Retrieves the account's balance, nonce, and code for a given address.
284    fn get_account_info(
285        &self,
286        address: Address,
287        block_id: BlockId,
288    ) -> impl Future<Output = Result<AccountInfo, Self::Error>> + Send {
289        self.spawn_blocking_io_fut(async move |this| {
290            let state = this.state_at_block_id(block_id).await?;
291            let account = state
292                .basic_account(&address)
293                .map_err(Self::Error::from_eth_err)?
294                .unwrap_or_default();
295
296            let balance = account.balance;
297            let nonce = account.nonce;
298            let code = if account.get_bytecode_hash() == KECCAK_EMPTY {
299                Default::default()
300            } else {
301                state
302                    .account_code(&address)
303                    .map_err(Self::Error::from_eth_err)?
304                    .unwrap_or_default()
305                    .original_bytes()
306            };
307
308            Ok(AccountInfo { balance, nonce, code })
309        })
310    }
311}
312
313/// Loads state from database.
314///
315/// Behaviour shared by several `eth_` RPC methods, not exclusive to `eth_` state RPC methods.
316pub trait LoadState:
317    LoadPendingBlock
318    + EthApiTypes<
319        Error: FromEvmError<Self::Evm> + FromEthApiError,
320        RpcConvert: RpcConvert<Network = Self::NetworkTypes>,
321    > + RpcNodeCoreExt
322{
323    /// Returns the state at the given block number
324    fn state_at_hash(&self, block_hash: B256) -> Result<StateProviderBox, Self::Error> {
325        self.provider().history_by_block_hash(block_hash).map_err(Self::Error::from_eth_err)
326    }
327
328    /// Returns the state at the given [`BlockId`] enum.
329    ///
330    /// Note: if not [`BlockNumberOrTag::Pending`](alloy_eips::BlockNumberOrTag) then this
331    /// will only return canonical state. See also <https://github.com/paradigmxyz/reth/issues/4515>
332    fn state_at_block_id(
333        &self,
334        at: BlockId,
335    ) -> impl Future<Output = Result<StateProviderBox, Self::Error>> + Send
336    where
337        Self: SpawnBlocking,
338    {
339        async move {
340            if at.is_pending() &&
341                let Ok(Some(state)) = self.local_pending_state().await
342            {
343                return Ok(state)
344            }
345
346            self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)
347        }
348    }
349
350    /// Returns the _latest_ state
351    fn latest_state(&self) -> Result<StateProviderBox, Self::Error> {
352        self.provider().latest().map_err(Self::Error::from_eth_err)
353    }
354
355    /// Returns the state at the given [`BlockId`] enum or the latest.
356    ///
357    /// Convenience function to interprets `None` as `BlockId::Number(BlockNumberOrTag::Latest)`
358    fn state_at_block_id_or_latest(
359        &self,
360        block_id: Option<BlockId>,
361    ) -> impl Future<Output = Result<StateProviderBox, Self::Error>> + Send
362    where
363        Self: SpawnBlocking,
364    {
365        async move {
366            if let Some(block_id) = block_id {
367                self.state_at_block_id(block_id).await
368            } else {
369                Ok(self.latest_state()?)
370            }
371        }
372    }
373
374    /// Returns the EVM environment for the given sealed header.
375    fn evm_env_for_header(
376        &self,
377        header: &SealedHeaderFor<Self::Primitives>,
378    ) -> Result<EvmEnvFor<Self::Evm>, Self::Error> {
379        self.evm_config()
380            .evm_env(header)
381            .map_err(RethError::other)
382            .map_err(Self::Error::from_eth_err)
383    }
384
385    /// Returns the EVM environment for the requested [`BlockId`]
386    ///
387    /// If the [`BlockId`] this will return the [`BlockId`] of the block the env was configured
388    /// for.
389    /// If the [`BlockId`] is pending, this will return the "Pending" tag, otherwise this returns
390    /// the hash of the exact block.
391    fn evm_env_at(
392        &self,
393        at: BlockId,
394    ) -> impl Future<Output = Result<(EvmEnvFor<Self::Evm>, BlockId), Self::Error>> + Send
395    where
396        Self: SpawnBlocking,
397    {
398        async move {
399            if at.is_pending() {
400                let PendingBlockEnv { evm_env, origin } = self.pending_block_env_and_cfg()?;
401                Ok((evm_env, origin.state_block_id()))
402            } else {
403                // we can assume that the blockid will be predominantly `Latest` (e.g. for
404                // `eth_call`) and if requested by number or hash we can quickly fetch just the
405                // header
406                let header = RpcNodeCore::provider(self)
407                    .sealed_header_by_id(at)
408                    .map_err(Self::Error::from_eth_err)?
409                    .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
410                let evm_env = self.evm_env_for_header(&header)?;
411
412                Ok((evm_env, header.hash().into()))
413            }
414        }
415    }
416
417    /// Returns the recovered block, EVM environment, and state block id for the requested
418    /// [`BlockId`].
419    ///
420    /// For pending blocks, this preserves the state id returned by [`Self::evm_env_at`], which can
421    /// be the pending tag for an actual pending block or the latest block hash when the pending env
422    /// is derived from latest.
423    #[expect(clippy::type_complexity)]
424    fn evm_env_and_recovered_block_at(
425        &self,
426        at: BlockId,
427    ) -> impl Future<
428        Output = Result<
429            (Arc<RecoveredBlock<BlockTy<Self::Primitives>>>, EvmEnvFor<Self::Evm>, BlockId),
430            Self::Error,
431        >,
432    > + Send
433    where
434        Self: SpawnBlocking + LoadBlock,
435    {
436        async move {
437            if at.is_pending() {
438                let (evm_env, block_id) = self.evm_env_at(at).await?;
439                let block = self
440                    .recovered_block(block_id)
441                    .await?
442                    .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
443
444                Ok((block, evm_env, block_id))
445            } else {
446                let block = self
447                    .recovered_block(at)
448                    .await?
449                    .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
450                let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
451                let block_id = block.hash().into();
452
453                Ok((block, evm_env, block_id))
454            }
455        }
456    }
457
458    /// Returns the next available nonce without gaps for the given address
459    /// Next available nonce is either the on chain nonce of the account or the highest consecutive
460    /// nonce in the pool + 1
461    ///
462    /// The provided request must have a from address set.
463    fn next_available_nonce_for(
464        &self,
465        request: &RpcTxReq<Self::NetworkTypes>,
466    ) -> impl Future<Output = Result<u64, Self::Error>> + Send
467    where
468        Self: SpawnBlocking,
469    {
470        let address = request.as_ref().from;
471        self.spawn_blocking_io(move |this| {
472            let address = match address {
473                Some(address) => address,
474                None => return Err(SignError::NoAccount.into_eth_err()),
475            };
476
477            // first fetch the on chain nonce of the account
478            let mut next_nonce = this
479                .latest_state()?
480                .account_nonce(&address)
481                .map_err(Self::Error::from_eth_err)?
482                .unwrap_or_default();
483
484            // Retrieve the highest consecutive transaction for the sender from the transaction pool
485            if let Some(highest_tx) =
486                this.pool().get_highest_consecutive_transaction_by_sender(address, next_nonce)
487            {
488                // Return the nonce of the highest consecutive transaction + 1
489                next_nonce = highest_tx.nonce().checked_add(1).ok_or_else(|| {
490                    Self::Error::from(EthApiError::InvalidTransaction(
491                        RpcInvalidTransactionError::NonceMaxValue,
492                    ))
493                })?;
494            }
495
496            Ok(next_nonce)
497        })
498    }
499
500    /// Returns the number of transactions sent from an address at the given block identifier.
501    ///
502    /// If this is [`BlockNumberOrTag::Pending`](alloy_eips::BlockNumberOrTag) then this will
503    /// look up the highest transaction in pool and return the next nonce (highest + 1).
504    fn transaction_count(
505        &self,
506        address: Address,
507        block_id: Option<BlockId>,
508    ) -> impl Future<Output = Result<U256, Self::Error>> + Send
509    where
510        Self: SpawnBlocking,
511    {
512        self.spawn_blocking_io_fut(async move |this| {
513            // first fetch the on chain nonce of the account
514            let on_chain_account_nonce = this
515                .state_at_block_id_or_latest(block_id)
516                .await?
517                .account_nonce(&address)
518                .map_err(Self::Error::from_eth_err)?
519                .unwrap_or_default();
520
521            if block_id == Some(BlockId::pending()) {
522                // for pending tag we need to find the highest nonce of txn in the pending state.
523                if let Some(highest_pool_tx) = this
524                    .pool()
525                    .get_highest_consecutive_transaction_by_sender(address, on_chain_account_nonce)
526                {
527                    {
528                        // and the corresponding txcount is nonce + 1 of the highest tx in the pool
529                        // (on chain nonce is increased after tx)
530                        let next_tx_nonce =
531                            highest_pool_tx.nonce().checked_add(1).ok_or_else(|| {
532                                Self::Error::from(EthApiError::InvalidTransaction(
533                                    RpcInvalidTransactionError::NonceMaxValue,
534                                ))
535                            })?;
536
537                        // guard against drifts in the pool
538                        let next_tx_nonce = on_chain_account_nonce.max(next_tx_nonce);
539
540                        let tx_count = on_chain_account_nonce.max(next_tx_nonce);
541                        return Ok(U256::from(tx_count));
542                    }
543                }
544            }
545            Ok(U256::from(on_chain_account_nonce))
546        })
547    }
548
549    /// Returns code of given account, at the given identifier.
550    fn get_code(
551        &self,
552        address: Address,
553        block_id: Option<BlockId>,
554    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send
555    where
556        Self: SpawnBlocking,
557    {
558        self.spawn_blocking_io_fut(async move |this| {
559            Ok(this
560                .state_at_block_id_or_latest(block_id)
561                .await?
562                .account_code(&address)
563                .map_err(Self::Error::from_eth_err)?
564                .unwrap_or_default()
565                .original_bytes())
566        })
567    }
568}