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