reth_rpc_eth_api/helpers/
state.rs1use super::{EthApiSpec, LoadBlock, 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::{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 std::{collections::HashMap, sync::Arc};
26
27pub trait EthState: LoadState + SpawnBlocking {
29 fn max_proof_window(&self) -> u64;
31
32 fn ensure_within_proof_window(&self, block_id: BlockId) -> Result<(), Self::Error>
37 where
38 Self: EthApiSpec,
39 {
40 let chain_info = self.chain_info().map_err(Self::Error::from_eth_err)?;
41 let block_number = self
42 .provider()
43 .block_number_for_id(block_id)
44 .map_err(Self::Error::from_eth_err)?
45 .ok_or(EthApiError::HeaderNotFound(block_id))?;
46 if chain_info.best_number.saturating_sub(block_number) > self.max_proof_window() {
47 return Err(EthApiError::ExceedsMaxProofWindow.into())
48 }
49 Ok(())
50 }
51
52 fn transaction_count(
57 &self,
58 address: Address,
59 block_id: Option<BlockId>,
60 ) -> impl Future<Output = Result<U256, Self::Error>> + Send {
61 LoadState::transaction_count(self, address, block_id)
62 }
63
64 fn get_code(
66 &self,
67 address: Address,
68 block_id: Option<BlockId>,
69 ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send {
70 LoadState::get_code(self, address, block_id)
71 }
72
73 fn balance(
75 &self,
76 address: Address,
77 block_id: Option<BlockId>,
78 ) -> impl Future<Output = Result<U256, Self::Error>> + Send {
79 self.spawn_blocking_io_fut(async move |this| {
80 Ok(this
81 .state_at_block_id_or_latest(block_id)
82 .await?
83 .account_balance(&address)
84 .map_err(Self::Error::from_eth_err)?
85 .unwrap_or_default())
86 })
87 }
88
89 fn storage_at(
91 &self,
92 address: Address,
93 index: JsonStorageKey,
94 block_id: Option<BlockId>,
95 ) -> impl Future<Output = Result<B256, Self::Error>> + Send {
96 self.spawn_blocking_io_fut(async move |this| {
97 Ok(B256::new(
98 this.state_at_block_id_or_latest(block_id)
99 .await?
100 .storage(address, index.as_b256())
101 .map_err(Self::Error::from_eth_err)?
102 .unwrap_or_default()
103 .to_be_bytes(),
104 ))
105 })
106 }
107
108 fn storage_values(
113 &self,
114 requests: HashMap<Address, Vec<JsonStorageKey>>,
115 block_id: Option<BlockId>,
116 ) -> impl Future<Output = Result<HashMap<Address, Vec<B256>>, Self::Error>> + Send {
117 async move {
118 if requests.is_empty() {
119 return Err(Self::Error::from_eth_err(EthApiError::InvalidParams(
120 "empty request".to_string(),
121 )));
122 }
123 let total_slots: usize = requests.values().map(|slots| slots.len()).sum();
124 if total_slots > DEFAULT_MAX_STORAGE_VALUES_SLOTS {
125 return Err(Self::Error::from_eth_err(EthApiError::InvalidParams(
126 format!(
127 "total slot count {total_slots} exceeds limit {DEFAULT_MAX_STORAGE_VALUES_SLOTS}",
128 ),
129 )));
130 }
131
132 self.spawn_blocking_io_fut(async move |this| {
133 let state = this.state_at_block_id_or_latest(block_id).await?;
134
135 let mut result = HashMap::with_capacity(requests.len());
136 for (address, slots) in requests {
137 let mut values = Vec::with_capacity(slots.len());
138 for slot in &slots {
139 let value = state
140 .storage(address, slot.as_b256())
141 .map_err(Self::Error::from_eth_err)?
142 .unwrap_or_default();
143 values.push(B256::new(value.to_be_bytes()));
144 }
145 result.insert(address, values);
146 }
147
148 Ok(result)
149 })
150 .await
151 }
152 }
153
154 fn get_proof(
156 &self,
157 address: Address,
158 keys: Vec<JsonStorageKey>,
159 block_id: Option<BlockId>,
160 ) -> Result<
161 impl Future<Output = Result<EIP1186AccountProofResponse, Self::Error>> + Send,
162 Self::Error,
163 >
164 where
165 Self: EthApiSpec,
166 {
167 Ok(async move {
168 let _permit = self
169 .acquire_owned_tracing()
170 .await
171 .map_err(RethError::other)
172 .map_err(EthApiError::Internal)?;
173
174 let block_id = block_id.unwrap_or_default();
175 self.ensure_within_proof_window(block_id)?;
176
177 self.spawn_blocking_io_fut(async move |this| {
178 let state = this.state_at_block_id(block_id).await?;
179 let storage_keys = keys.iter().map(|key| key.as_b256()).collect::<Vec<_>>();
180 let proof = state
181 .proof(Default::default(), address, &storage_keys)
182 .map_err(Self::Error::from_eth_err)?;
183 Ok(proof.into_eip1186_response(keys))
184 })
185 .await
186 })
187 }
188
189 fn get_account(
191 &self,
192 address: Address,
193 block_id: BlockId,
194 ) -> impl Future<Output = Result<Option<Account>, Self::Error>> + Send
195 where
196 Self: EthApiSpec,
197 {
198 async move {
199 self.ensure_within_proof_window(block_id)?;
200
201 self.spawn_blocking_io_fut(async move |this| {
202 let state = this.state_at_block_id(block_id).await?;
203 let account = state.basic_account(&address).map_err(Self::Error::from_eth_err)?;
204 let Some(account) = account else { return Ok(None) };
205
206 let balance = account.balance;
207 let nonce = account.nonce;
208 let code_hash = account.bytecode_hash.unwrap_or(KECCAK_EMPTY);
209
210 let storage_root = state
213 .storage_root(address, Default::default())
214 .map_err(Self::Error::from_eth_err)?;
215
216 Ok(Some(Account { balance, nonce, code_hash, storage_root }))
217 })
218 .await
219 }
220 }
221
222 fn get_account_info(
224 &self,
225 address: Address,
226 block_id: BlockId,
227 ) -> impl Future<Output = Result<AccountInfo, Self::Error>> + Send {
228 self.spawn_blocking_io_fut(async move |this| {
229 let state = this.state_at_block_id(block_id).await?;
230 let account = state
231 .basic_account(&address)
232 .map_err(Self::Error::from_eth_err)?
233 .unwrap_or_default();
234
235 let balance = account.balance;
236 let nonce = account.nonce;
237 let code = if account.get_bytecode_hash() == KECCAK_EMPTY {
238 Default::default()
239 } else {
240 state
241 .account_code(&address)
242 .map_err(Self::Error::from_eth_err)?
243 .unwrap_or_default()
244 .original_bytes()
245 };
246
247 Ok(AccountInfo { balance, nonce, code })
248 })
249 }
250}
251
252pub trait LoadState:
256 LoadPendingBlock
257 + EthApiTypes<
258 Error: FromEvmError<Self::Evm> + FromEthApiError,
259 RpcConvert: RpcConvert<Network = Self::NetworkTypes>,
260 > + RpcNodeCoreExt
261{
262 fn state_at_hash(&self, block_hash: B256) -> Result<StateProviderBox, Self::Error> {
264 self.provider().history_by_block_hash(block_hash).map_err(Self::Error::from_eth_err)
265 }
266
267 fn state_at_block_id(
272 &self,
273 at: BlockId,
274 ) -> impl Future<Output = Result<StateProviderBox, Self::Error>> + Send
275 where
276 Self: SpawnBlocking,
277 {
278 async move {
279 if at.is_pending() &&
280 let Ok(Some(state)) = self.local_pending_state().await
281 {
282 return Ok(state)
283 }
284
285 self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)
286 }
287 }
288
289 fn latest_state(&self) -> Result<StateProviderBox, Self::Error> {
291 self.provider().latest().map_err(Self::Error::from_eth_err)
292 }
293
294 fn state_at_block_id_or_latest(
298 &self,
299 block_id: Option<BlockId>,
300 ) -> impl Future<Output = Result<StateProviderBox, Self::Error>> + Send
301 where
302 Self: SpawnBlocking,
303 {
304 async move {
305 if let Some(block_id) = block_id {
306 self.state_at_block_id(block_id).await
307 } else {
308 Ok(self.latest_state()?)
309 }
310 }
311 }
312
313 fn evm_env_for_header(
315 &self,
316 header: &SealedHeaderFor<Self::Primitives>,
317 ) -> Result<EvmEnvFor<Self::Evm>, Self::Error> {
318 self.evm_config()
319 .evm_env(header)
320 .map_err(RethError::other)
321 .map_err(Self::Error::from_eth_err)
322 }
323
324 fn evm_env_at(
331 &self,
332 at: BlockId,
333 ) -> impl Future<Output = Result<(EvmEnvFor<Self::Evm>, BlockId), Self::Error>> + Send
334 where
335 Self: SpawnBlocking,
336 {
337 async move {
338 if at.is_pending() {
339 let PendingBlockEnv { evm_env, origin } = self.pending_block_env_and_cfg()?;
340 Ok((evm_env, origin.state_block_id()))
341 } else {
342 let header = RpcNodeCore::provider(self)
346 .sealed_header_by_id(at)
347 .map_err(Self::Error::from_eth_err)?
348 .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
349 let evm_env = self.evm_env_for_header(&header)?;
350
351 Ok((evm_env, header.hash().into()))
352 }
353 }
354 }
355
356 #[expect(clippy::type_complexity)]
362 fn evm_env_and_recovered_block_at(
363 &self,
364 at: BlockId,
365 ) -> impl Future<
366 Output = Result<
367 (Arc<RecoveredBlock<BlockTy<Self::Primitives>>>, EvmEnvFor<Self::Evm>, BlockId),
368 Self::Error,
369 >,
370 > + Send
371 where
372 Self: SpawnBlocking + LoadBlock,
373 {
374 async move {
375 if at.is_pending() {
376 let (evm_env, block_id) = self.evm_env_at(at).await?;
377 let block = self
378 .recovered_block(block_id)
379 .await?
380 .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
381
382 Ok((block, evm_env, block_id))
383 } else {
384 let block = self
385 .recovered_block(at)
386 .await?
387 .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
388 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
389 let block_id = block.hash().into();
390
391 Ok((block, evm_env, block_id))
392 }
393 }
394 }
395
396 fn next_available_nonce_for(
402 &self,
403 request: &RpcTxReq<Self::NetworkTypes>,
404 ) -> impl Future<Output = Result<u64, Self::Error>> + Send
405 where
406 Self: SpawnBlocking,
407 {
408 let address = request.as_ref().from;
409 self.spawn_blocking_io(move |this| {
410 let address = match address {
411 Some(address) => address,
412 None => return Err(SignError::NoAccount.into_eth_err()),
413 };
414
415 let mut next_nonce = this
417 .latest_state()?
418 .account_nonce(&address)
419 .map_err(Self::Error::from_eth_err)?
420 .unwrap_or_default();
421
422 if let Some(highest_tx) =
424 this.pool().get_highest_consecutive_transaction_by_sender(address, next_nonce)
425 {
426 next_nonce = highest_tx.nonce().checked_add(1).ok_or_else(|| {
428 Self::Error::from(EthApiError::InvalidTransaction(
429 RpcInvalidTransactionError::NonceMaxValue,
430 ))
431 })?;
432 }
433
434 Ok(next_nonce)
435 })
436 }
437
438 fn transaction_count(
443 &self,
444 address: Address,
445 block_id: Option<BlockId>,
446 ) -> impl Future<Output = Result<U256, Self::Error>> + Send
447 where
448 Self: SpawnBlocking,
449 {
450 self.spawn_blocking_io_fut(async move |this| {
451 let on_chain_account_nonce = this
453 .state_at_block_id_or_latest(block_id)
454 .await?
455 .account_nonce(&address)
456 .map_err(Self::Error::from_eth_err)?
457 .unwrap_or_default();
458
459 if block_id == Some(BlockId::pending()) {
460 if let Some(highest_pool_tx) = this
462 .pool()
463 .get_highest_consecutive_transaction_by_sender(address, on_chain_account_nonce)
464 {
465 {
466 let next_tx_nonce =
469 highest_pool_tx.nonce().checked_add(1).ok_or_else(|| {
470 Self::Error::from(EthApiError::InvalidTransaction(
471 RpcInvalidTransactionError::NonceMaxValue,
472 ))
473 })?;
474
475 let next_tx_nonce = on_chain_account_nonce.max(next_tx_nonce);
477
478 let tx_count = on_chain_account_nonce.max(next_tx_nonce);
479 return Ok(U256::from(tx_count));
480 }
481 }
482 }
483 Ok(U256::from(on_chain_account_nonce))
484 })
485 }
486
487 fn get_code(
489 &self,
490 address: Address,
491 block_id: Option<BlockId>,
492 ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send
493 where
494 Self: SpawnBlocking,
495 {
496 self.spawn_blocking_io_fut(async move |this| {
497 Ok(this
498 .state_at_block_id_or_latest(block_id)
499 .await?
500 .account_code(&address)
501 .map_err(Self::Error::from_eth_err)?
502 .unwrap_or_default()
503 .original_bytes())
504 })
505 }
506}