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.
3use super::{EthApiSpec, LoadPendingBlock, SpawnBlocking};
4use crate::{EthApiTypes, FromEthApiError, RpcNodeCore, RpcNodeCoreExt};
5use alloy_consensus::{constants::KECCAK_EMPTY, BlockHeader};
6use alloy_eips::BlockId;
7use alloy_primitives::{Address, Bytes, B256, U256};
8use alloy_rpc_types_eth::{Account, EIP1186AccountProofResponse};
9use alloy_serde::JsonStorageKey;
10use futures::Future;
11use reth_chainspec::{EthChainSpec, EthereumHardforks};
12use reth_errors::RethError;
13use reth_evm::ConfigureEvmEnv;
14use reth_provider::{
15    BlockIdReader, BlockNumReader, ChainSpecProvider, EvmEnvProvider as _, ProviderHeader,
16    StateProvider, StateProviderBox, StateProviderFactory,
17};
18use reth_rpc_eth_types::{EthApiError, PendingBlockEnv, RpcInvalidTransactionError};
19use reth_transaction_pool::TransactionPool;
20use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, SpecId};
21
22/// Helper methods for `eth_` methods relating to state (accounts).
23pub trait EthState: LoadState + SpawnBlocking {
24    /// Returns the maximum number of blocks into the past for generating state proofs.
25    fn max_proof_window(&self) -> u64;
26
27    /// Returns the number of transactions sent from an address at the given block identifier.
28    ///
29    /// If this is [`BlockNumberOrTag::Pending`](alloy_eips::BlockNumberOrTag) then this will
30    /// look up the highest transaction in pool and return the next nonce (highest + 1).
31    fn transaction_count(
32        &self,
33        address: Address,
34        block_id: Option<BlockId>,
35    ) -> impl Future<Output = Result<U256, Self::Error>> + Send {
36        LoadState::transaction_count(self, address, block_id)
37    }
38
39    /// Returns code of given account, at given blocknumber.
40    fn get_code(
41        &self,
42        address: Address,
43        block_id: Option<BlockId>,
44    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send {
45        LoadState::get_code(self, address, block_id)
46    }
47
48    /// Returns balance of given account, at given blocknumber.
49    fn balance(
50        &self,
51        address: Address,
52        block_id: Option<BlockId>,
53    ) -> impl Future<Output = Result<U256, Self::Error>> + Send {
54        self.spawn_blocking_io(move |this| {
55            Ok(this
56                .state_at_block_id_or_latest(block_id)?
57                .account_balance(address)
58                .map_err(Self::Error::from_eth_err)?
59                .unwrap_or_default())
60        })
61    }
62
63    /// Returns values stored of given account, at given blocknumber.
64    fn storage_at(
65        &self,
66        address: Address,
67        index: JsonStorageKey,
68        block_id: Option<BlockId>,
69    ) -> impl Future<Output = Result<B256, Self::Error>> + Send {
70        self.spawn_blocking_io(move |this| {
71            let storage_value = this
72                .state_at_block_id_or_latest(block_id)?
73                .storage(address, index.as_b256())
74                .map_err(Self::Error::from_eth_err)?
75                .unwrap_or_default();
76            match storage_value.is_public() {
77                true => Ok(B256::new(storage_value.value.to_be_bytes())),
78                false => Ok(B256::ZERO),
79            }
80        })
81    }
82
83    /// Returns values stored of given account, with Merkle-proof, at given blocknumber.
84    fn get_proof(
85        &self,
86        address: Address,
87        keys: Vec<JsonStorageKey>,
88        block_id: Option<BlockId>,
89    ) -> Result<
90        impl Future<Output = Result<EIP1186AccountProofResponse, Self::Error>> + Send,
91        Self::Error,
92    >
93    where
94        Self: EthApiSpec,
95    {
96        Ok(async move {
97            let _permit = self
98                .acquire_owned()
99                .await
100                .map_err(RethError::other)
101                .map_err(EthApiError::Internal)?;
102
103            let chain_info = self.chain_info().map_err(Self::Error::from_eth_err)?;
104            let block_id = block_id.unwrap_or_default();
105
106            // Check whether the distance to the block exceeds the maximum configured window.
107            let block_number = self
108                .provider()
109                .block_number_for_id(block_id)
110                .map_err(Self::Error::from_eth_err)?
111                .ok_or(EthApiError::HeaderNotFound(block_id))?;
112            let max_window = self.max_proof_window();
113            if chain_info.best_number.saturating_sub(block_number) > max_window {
114                return Err(EthApiError::ExceedsMaxProofWindow.into())
115            }
116
117            self.spawn_blocking_io(move |this| {
118                let state = this.state_at_block_id(block_id)?;
119                let storage_keys = keys.iter().map(|key| key.as_b256()).collect::<Vec<_>>();
120                let proof = state
121                    .proof(Default::default(), address, &storage_keys)
122                    .map_err(Self::Error::from_eth_err)?;
123                Ok(proof.into_eip1186_response(keys))
124            })
125            .await
126        })
127    }
128
129    /// Returns the account at the given address for the provided block identifier.
130    fn get_account(
131        &self,
132        address: Address,
133        block_id: BlockId,
134    ) -> impl Future<Output = Result<Option<Account>, Self::Error>> + Send {
135        self.spawn_blocking_io(move |this| {
136            let state = this.state_at_block_id(block_id)?;
137            let account = state.basic_account(address).map_err(Self::Error::from_eth_err)?;
138            let Some(account) = account else { return Ok(None) };
139
140            // Check whether the distance to the block exceeds the maximum configured proof window.
141            let chain_info = this.provider().chain_info().map_err(Self::Error::from_eth_err)?;
142            let block_number = this
143                .provider()
144                .block_number_for_id(block_id)
145                .map_err(Self::Error::from_eth_err)?
146                .ok_or(EthApiError::HeaderNotFound(block_id))?;
147            let max_window = this.max_proof_window();
148            if chain_info.best_number.saturating_sub(block_number) > max_window {
149                return Err(EthApiError::ExceedsMaxProofWindow.into())
150            }
151
152            let balance = account.balance;
153            let nonce = account.nonce;
154            let code_hash = account.bytecode_hash.unwrap_or(KECCAK_EMPTY);
155
156            // Provide a default `HashedStorage` value in order to
157            // get the storage root hash of the current state.
158            let storage_root = state
159                .storage_root(address, Default::default())
160                .map_err(Self::Error::from_eth_err)?;
161
162            Ok(Some(Account { balance, nonce, code_hash, storage_root }))
163        })
164    }
165}
166
167/// Loads state from database.
168///
169/// Behaviour shared by several `eth_` RPC methods, not exclusive to `eth_` state RPC methods.
170pub trait LoadState:
171    EthApiTypes
172    + RpcNodeCoreExt<
173        Provider: StateProviderFactory
174                      + ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks>,
175        Pool: TransactionPool,
176    >
177{
178    /// Returns the state at the given block number
179    fn state_at_hash(&self, block_hash: B256) -> Result<StateProviderBox, Self::Error> {
180        self.provider().history_by_block_hash(block_hash).map_err(Self::Error::from_eth_err)
181    }
182
183    /// Returns the state at the given [`BlockId`] enum.
184    ///
185    /// Note: if not [`BlockNumberOrTag::Pending`](alloy_eips::BlockNumberOrTag) then this
186    /// will only return canonical state. See also <https://github.com/paradigmxyz/reth/issues/4515>
187    fn state_at_block_id(&self, at: BlockId) -> Result<StateProviderBox, Self::Error> {
188        self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)
189    }
190
191    /// Returns the _latest_ state
192    fn latest_state(&self) -> Result<StateProviderBox, Self::Error> {
193        self.provider().latest().map_err(Self::Error::from_eth_err)
194    }
195
196    /// Returns the state at the given [`BlockId`] enum or the latest.
197    ///
198    /// Convenience function to interprets `None` as `BlockId::Number(BlockNumberOrTag::Latest)`
199    fn state_at_block_id_or_latest(
200        &self,
201        block_id: Option<BlockId>,
202    ) -> Result<StateProviderBox, Self::Error> {
203        if let Some(block_id) = block_id {
204            self.state_at_block_id(block_id)
205        } else {
206            Ok(self.latest_state()?)
207        }
208    }
209
210    /// Returns the revm evm env for the requested [`BlockId`]
211    ///
212    /// If the [`BlockId`] this will return the [`BlockId`] of the block the env was configured
213    /// for.
214    /// If the [`BlockId`] is pending, this will return the "Pending" tag, otherwise this returns
215    /// the hash of the exact block.
216    fn evm_env_at(
217        &self,
218        at: BlockId,
219    ) -> impl Future<Output = Result<(CfgEnvWithHandlerCfg, BlockEnv, BlockId), Self::Error>> + Send
220    where
221        Self: LoadPendingBlock + SpawnBlocking,
222    {
223        async move {
224            if at.is_pending() {
225                let PendingBlockEnv { cfg, block_env, origin } =
226                    self.pending_block_env_and_cfg()?;
227                Ok((cfg, block_env, origin.state_block_id()))
228            } else {
229                // Use cached values if there is no pending block
230                let block_hash = RpcNodeCore::provider(self)
231                    .block_hash_for_id(at)
232                    .map_err(Self::Error::from_eth_err)?
233                    .ok_or(EthApiError::HeaderNotFound(at))?;
234
235                let header =
236                    self.cache().get_header(block_hash).await.map_err(Self::Error::from_eth_err)?;
237                let evm_config = self.evm_config().clone();
238                let (cfg, block_env) = self
239                    .provider()
240                    .env_with_header(&header, evm_config)
241                    .map_err(Self::Error::from_eth_err)?;
242                Ok((cfg, block_env, block_hash.into()))
243            }
244        }
245    }
246
247    /// Returns the revm evm env for the raw block header
248    ///
249    /// This is used for tracing raw blocks
250    fn evm_env_for_raw_block(
251        &self,
252        header: &ProviderHeader<Self::Provider>,
253    ) -> impl Future<Output = Result<(CfgEnvWithHandlerCfg, BlockEnv), Self::Error>> + Send
254    where
255        Self: LoadPendingBlock + SpawnBlocking,
256    {
257        async move {
258            // get the parent config first
259            let (cfg, mut block_env, _) = self.evm_env_at(header.parent_hash().into()).await?;
260
261            let after_merge = cfg.handler_cfg.spec_id >= SpecId::MERGE;
262            self.evm_config().fill_block_env(&mut block_env, header, after_merge);
263
264            Ok((cfg, block_env))
265        }
266    }
267
268    /// Returns the next available nonce without gaps for the given address
269    /// Next available nonce is either the on chain nonce of the account or the highest consecutive
270    /// nonce in the pool + 1
271    fn next_available_nonce(
272        &self,
273        address: Address,
274    ) -> impl Future<Output = Result<u64, Self::Error>> + Send
275    where
276        Self: SpawnBlocking,
277    {
278        self.spawn_blocking_io(move |this| {
279            // first fetch the on chain nonce of the account
280            let on_chain_account_nonce = this
281                .latest_state()?
282                .account_nonce(address)
283                .map_err(Self::Error::from_eth_err)?
284                .unwrap_or_default();
285
286            let mut next_nonce = on_chain_account_nonce;
287            // Retrieve the highest consecutive transaction for the sender from the transaction pool
288            if let Some(highest_tx) = this
289                .pool()
290                .get_highest_consecutive_transaction_by_sender(address, on_chain_account_nonce)
291            {
292                // Return the nonce of the highest consecutive transaction + 1
293                next_nonce = highest_tx.nonce().checked_add(1).ok_or_else(|| {
294                    Self::Error::from(EthApiError::InvalidTransaction(
295                        RpcInvalidTransactionError::NonceMaxValue,
296                    ))
297                })?;
298            }
299
300            Ok(next_nonce)
301        })
302    }
303
304    /// Returns the number of transactions sent from an address at the given block identifier.
305    ///
306    /// If this is [`BlockNumberOrTag::Pending`](alloy_eips::BlockNumberOrTag) then this will
307    /// look up the highest transaction in pool and return the next nonce (highest + 1).
308    fn transaction_count(
309        &self,
310        address: Address,
311        block_id: Option<BlockId>,
312    ) -> impl Future<Output = Result<U256, Self::Error>> + Send
313    where
314        Self: SpawnBlocking,
315    {
316        self.spawn_blocking_io(move |this| {
317            // first fetch the on chain nonce of the account
318            let on_chain_account_nonce = this
319                .state_at_block_id_or_latest(block_id)?
320                .account_nonce(address)
321                .map_err(Self::Error::from_eth_err)?
322                .unwrap_or_default();
323
324            if block_id == Some(BlockId::pending()) {
325                // for pending tag we need to find the highest nonce in the pool
326                if let Some(highest_pool_tx) =
327                    this.pool().get_highest_transaction_by_sender(address)
328                {
329                    {
330                        // and the corresponding txcount is nonce + 1 of the highest tx in the pool
331                        // (on chain nonce is increased after tx)
332                        let next_tx_nonce =
333                            highest_pool_tx.nonce().checked_add(1).ok_or_else(|| {
334                                Self::Error::from(EthApiError::InvalidTransaction(
335                                    RpcInvalidTransactionError::NonceMaxValue,
336                                ))
337                            })?;
338
339                        // guard against drifts in the pool
340                        let next_tx_nonce = on_chain_account_nonce.max(next_tx_nonce);
341
342                        let tx_count = on_chain_account_nonce.max(next_tx_nonce);
343                        return Ok(U256::from(tx_count));
344                    }
345                }
346            }
347            Ok(U256::from(on_chain_account_nonce))
348        })
349    }
350
351    /// Returns code of given account, at the given identifier.
352    fn get_code(
353        &self,
354        address: Address,
355        block_id: Option<BlockId>,
356    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send
357    where
358        Self: SpawnBlocking,
359    {
360        self.spawn_blocking_io(move |this| {
361            Ok(this
362                .state_at_block_id_or_latest(block_id)?
363                .account_code(address)
364                .map_err(Self::Error::from_eth_err)?
365                .unwrap_or_default()
366                .original_bytes())
367        })
368    }
369}