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::{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::MultiProofTargets;
26use std::{collections::HashMap, sync::Arc};
27
28pub trait EthState: LoadState + SpawnBlocking {
30 fn max_proof_window(&self) -> u64;
32
33 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 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 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 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 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 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 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 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 = MultiProofTargets::with_capacity(targets.len());
215 for (address, slots) in &targets {
216 proof_targets
217 .entry(keccak256(address))
218 .or_default()
219 .extend(slots.iter().map(keccak256));
220 }
221
222 let multiproof = state
223 .multiproof(Default::default(), proof_targets)
224 .map_err(Self::Error::from_eth_err)?;
225
226 targets
227 .into_iter()
228 .map(|(address, slots)| {
229 let proof = multiproof
230 .account_proof(address, &slots)
231 .map_err(RethError::other)
232 .map_err(Self::Error::from_eth_err)?;
233 let storage_keys =
234 slots.into_iter().map(JsonStorageKey::from).collect::<Vec<_>>();
235 Ok(proof.into_eip1186_response(storage_keys))
236 })
237 .collect::<Result<Vec<_>, Self::Error>>()
238 })
239 .await
240 })
241 }
242
243 fn get_account(
245 &self,
246 address: Address,
247 block_id: BlockId,
248 ) -> impl Future<Output = Result<Option<Account>, Self::Error>> + Send
249 where
250 Self: EthApiSpec,
251 {
252 async move {
253 self.ensure_within_proof_window(block_id)?;
254
255 self.spawn_blocking_io_fut(async move |this| {
256 let state = this.state_at_block_id(block_id).await?;
257 let account = state.basic_account(&address).map_err(Self::Error::from_eth_err)?;
258 let Some(account) = account else { return Ok(None) };
259
260 let balance = account.balance;
261 let nonce = account.nonce;
262 let code_hash = account.bytecode_hash.unwrap_or(KECCAK_EMPTY);
263
264 let storage_root = state
267 .storage_root(address, Default::default())
268 .map_err(Self::Error::from_eth_err)?;
269
270 Ok(Some(Account { balance, nonce, code_hash, storage_root }))
271 })
272 .await
273 }
274 }
275
276 fn get_account_info(
278 &self,
279 address: Address,
280 block_id: BlockId,
281 ) -> impl Future<Output = Result<AccountInfo, Self::Error>> + Send {
282 self.spawn_blocking_io_fut(async move |this| {
283 let state = this.state_at_block_id(block_id).await?;
284 let account = state
285 .basic_account(&address)
286 .map_err(Self::Error::from_eth_err)?
287 .unwrap_or_default();
288
289 let balance = account.balance;
290 let nonce = account.nonce;
291 let code = if account.get_bytecode_hash() == KECCAK_EMPTY {
292 Default::default()
293 } else {
294 state
295 .account_code(&address)
296 .map_err(Self::Error::from_eth_err)?
297 .unwrap_or_default()
298 .original_bytes()
299 };
300
301 Ok(AccountInfo { balance, nonce, code })
302 })
303 }
304}
305
306pub trait LoadState:
310 LoadPendingBlock
311 + EthApiTypes<
312 Error: FromEvmError<Self::Evm> + FromEthApiError,
313 RpcConvert: RpcConvert<Network = Self::NetworkTypes>,
314 > + RpcNodeCoreExt
315{
316 fn state_at_hash(&self, block_hash: B256) -> Result<StateProviderBox, Self::Error> {
318 self.provider().history_by_block_hash(block_hash).map_err(Self::Error::from_eth_err)
319 }
320
321 fn state_at_block_id(
326 &self,
327 at: BlockId,
328 ) -> impl Future<Output = Result<StateProviderBox, Self::Error>> + Send
329 where
330 Self: SpawnBlocking,
331 {
332 async move {
333 if at.is_pending() &&
334 let Ok(Some(state)) = self.local_pending_state().await
335 {
336 return Ok(state)
337 }
338
339 self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)
340 }
341 }
342
343 fn latest_state(&self) -> Result<StateProviderBox, Self::Error> {
345 self.provider().latest().map_err(Self::Error::from_eth_err)
346 }
347
348 fn state_at_block_id_or_latest(
352 &self,
353 block_id: Option<BlockId>,
354 ) -> impl Future<Output = Result<StateProviderBox, Self::Error>> + Send
355 where
356 Self: SpawnBlocking,
357 {
358 async move {
359 if let Some(block_id) = block_id {
360 self.state_at_block_id(block_id).await
361 } else {
362 Ok(self.latest_state()?)
363 }
364 }
365 }
366
367 fn evm_env_for_header(
369 &self,
370 header: &SealedHeaderFor<Self::Primitives>,
371 ) -> Result<EvmEnvFor<Self::Evm>, Self::Error> {
372 self.evm_config()
373 .evm_env(header)
374 .map_err(RethError::other)
375 .map_err(Self::Error::from_eth_err)
376 }
377
378 fn evm_env_at(
385 &self,
386 at: BlockId,
387 ) -> impl Future<Output = Result<(EvmEnvFor<Self::Evm>, BlockId), Self::Error>> + Send
388 where
389 Self: SpawnBlocking,
390 {
391 async move {
392 if at.is_pending() {
393 let PendingBlockEnv { evm_env, origin } = self.pending_block_env_and_cfg()?;
394 Ok((evm_env, origin.state_block_id()))
395 } else {
396 let header = RpcNodeCore::provider(self)
400 .sealed_header_by_id(at)
401 .map_err(Self::Error::from_eth_err)?
402 .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
403 let evm_env = self.evm_env_for_header(&header)?;
404
405 Ok((evm_env, header.hash().into()))
406 }
407 }
408 }
409
410 #[expect(clippy::type_complexity)]
417 fn evm_env_and_recovered_block_at(
418 &self,
419 at: BlockId,
420 ) -> impl Future<
421 Output = Result<
422 (Arc<RecoveredBlock<BlockTy<Self::Primitives>>>, EvmEnvFor<Self::Evm>, BlockId),
423 Self::Error,
424 >,
425 > + Send
426 where
427 Self: SpawnBlocking + LoadBlock,
428 {
429 async move {
430 if at.is_pending() {
431 let (evm_env, block_id) = self.evm_env_at(at).await?;
432 let block = self
433 .recovered_block(block_id)
434 .await?
435 .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
436
437 Ok((block, evm_env, block_id))
438 } else {
439 let block = self
440 .recovered_block(at)
441 .await?
442 .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
443 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
444 let block_id = block.hash().into();
445
446 Ok((block, evm_env, block_id))
447 }
448 }
449 }
450
451 fn next_available_nonce_for(
457 &self,
458 request: &RpcTxReq<Self::NetworkTypes>,
459 ) -> impl Future<Output = Result<u64, Self::Error>> + Send
460 where
461 Self: SpawnBlocking,
462 {
463 let address = request.as_ref().from;
464 self.spawn_blocking_io(move |this| {
465 let address = match address {
466 Some(address) => address,
467 None => return Err(SignError::NoAccount.into_eth_err()),
468 };
469
470 let mut next_nonce = this
472 .latest_state()?
473 .account_nonce(&address)
474 .map_err(Self::Error::from_eth_err)?
475 .unwrap_or_default();
476
477 if let Some(highest_tx) =
479 this.pool().get_highest_consecutive_transaction_by_sender(address, next_nonce)
480 {
481 next_nonce = highest_tx.nonce().checked_add(1).ok_or_else(|| {
483 Self::Error::from(EthApiError::InvalidTransaction(
484 RpcInvalidTransactionError::NonceMaxValue,
485 ))
486 })?;
487 }
488
489 Ok(next_nonce)
490 })
491 }
492
493 fn transaction_count(
498 &self,
499 address: Address,
500 block_id: Option<BlockId>,
501 ) -> impl Future<Output = Result<U256, Self::Error>> + Send
502 where
503 Self: SpawnBlocking,
504 {
505 self.spawn_blocking_io_fut(async move |this| {
506 let on_chain_account_nonce = this
508 .state_at_block_id_or_latest(block_id)
509 .await?
510 .account_nonce(&address)
511 .map_err(Self::Error::from_eth_err)?
512 .unwrap_or_default();
513
514 if block_id == Some(BlockId::pending()) {
515 if let Some(highest_pool_tx) = this
517 .pool()
518 .get_highest_consecutive_transaction_by_sender(address, on_chain_account_nonce)
519 {
520 {
521 let next_tx_nonce =
524 highest_pool_tx.nonce().checked_add(1).ok_or_else(|| {
525 Self::Error::from(EthApiError::InvalidTransaction(
526 RpcInvalidTransactionError::NonceMaxValue,
527 ))
528 })?;
529
530 let next_tx_nonce = on_chain_account_nonce.max(next_tx_nonce);
532
533 let tx_count = on_chain_account_nonce.max(next_tx_nonce);
534 return Ok(U256::from(tx_count));
535 }
536 }
537 }
538 Ok(U256::from(on_chain_account_nonce))
539 })
540 }
541
542 fn get_code(
544 &self,
545 address: Address,
546 block_id: Option<BlockId>,
547 ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send
548 where
549 Self: SpawnBlocking,
550 {
551 self.spawn_blocking_io_fut(async move |this| {
552 Ok(this
553 .state_at_block_id_or_latest(block_id)
554 .await?
555 .account_code(&address)
556 .map_err(Self::Error::from_eth_err)?
557 .unwrap_or_default()
558 .original_bytes())
559 })
560 }
561}