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