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::{MultiProofTargetsV2, ProofV2Target};
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(move |this| async move {
179 let _permit = permit;
180 let state = this.state_at_block_id(block_id).await?;
181 let storage_keys = keys.iter().map(|key| key.as_b256()).collect::<Vec<_>>();
182 let proof = state
183 .proof(Default::default(), address, &storage_keys)
184 .map_err(Self::Error::from_eth_err)?;
185 Ok(proof.into_eip1186_response(keys))
186 })
187 .await
188 })
189 }
190
191 fn get_multi_proof(
193 &self,
194 targets: Vec<(Address, Vec<B256>)>,
195 block_id: Option<BlockId>,
196 ) -> Result<
197 impl Future<Output = Result<Vec<EIP1186AccountProofResponse>, Self::Error>> + Send,
198 Self::Error,
199 >
200 where
201 Self: EthApiSpec,
202 {
203 Ok(async move {
204 let permit = self
205 .acquire_owned_tracing()
206 .await
207 .map_err(RethError::other)
208 .map_err(EthApiError::Internal)?;
209
210 let block_id = block_id.unwrap_or_default();
211 self.ensure_within_proof_window(block_id)?;
212
213 self.spawn_blocking_io_fut(move |this| async move {
214 let _permit = permit;
215 let state = this.state_at_block_id(block_id).await?;
216 let mut proof_targets = MultiProofTargetsV2::default();
217 proof_targets.account_targets.reserve(targets.len());
218 proof_targets.storage_targets.reserve(targets.len());
219 for (address, slots) in &targets {
220 let hashed_address = keccak256(address);
221 proof_targets.account_targets.push(ProofV2Target::new(hashed_address));
222 proof_targets
223 .storage_targets
224 .entry(hashed_address)
225 .or_default()
226 .extend(slots.iter().map(|slot| ProofV2Target::new(keccak256(slot))));
227 }
228
229 let multiproof = state
230 .multiproof_v2(Default::default(), proof_targets)
231 .map_err(Self::Error::from_eth_err)?;
232
233 targets
234 .into_iter()
235 .map(|(address, slots)| {
236 let proof = multiproof
237 .account_proof(address, &slots)
238 .map_err(RethError::other)
239 .map_err(Self::Error::from_eth_err)?;
240 let storage_keys =
241 slots.into_iter().map(JsonStorageKey::from).collect::<Vec<_>>();
242 Ok(proof.into_eip1186_response(storage_keys))
243 })
244 .collect::<Result<Vec<_>, Self::Error>>()
245 })
246 .await
247 })
248 }
249
250 fn get_account(
252 &self,
253 address: Address,
254 block_id: BlockId,
255 ) -> impl Future<Output = Result<Option<Account>, Self::Error>> + Send
256 where
257 Self: EthApiSpec,
258 {
259 async move {
260 self.ensure_within_proof_window(block_id)?;
261
262 self.spawn_blocking_io_fut(async move |this| {
263 let state = this.state_at_block_id(block_id).await?;
264 let account = state.basic_account(&address).map_err(Self::Error::from_eth_err)?;
265 let Some(account) = account else { return Ok(None) };
266
267 let balance = account.balance;
268 let nonce = account.nonce;
269 let code_hash = account.bytecode_hash.unwrap_or(KECCAK_EMPTY);
270
271 let storage_root = state
274 .storage_root(address, Default::default())
275 .map_err(Self::Error::from_eth_err)?;
276
277 Ok(Some(Account { balance, nonce, code_hash, storage_root }))
278 })
279 .await
280 }
281 }
282
283 fn get_account_info(
285 &self,
286 address: Address,
287 block_id: BlockId,
288 ) -> impl Future<Output = Result<AccountInfo, Self::Error>> + Send {
289 self.spawn_blocking_io_fut(async move |this| {
290 let state = this.state_at_block_id(block_id).await?;
291 let account = state
292 .basic_account(&address)
293 .map_err(Self::Error::from_eth_err)?
294 .unwrap_or_default();
295
296 let balance = account.balance;
297 let nonce = account.nonce;
298 let code = if account.get_bytecode_hash() == KECCAK_EMPTY {
299 Default::default()
300 } else {
301 state
302 .account_code(&address)
303 .map_err(Self::Error::from_eth_err)?
304 .unwrap_or_default()
305 .original_bytes()
306 };
307
308 Ok(AccountInfo { balance, nonce, code })
309 })
310 }
311}
312
313pub trait LoadState:
317 LoadPendingBlock
318 + EthApiTypes<
319 Error: FromEvmError<Self::Evm> + FromEthApiError,
320 RpcConvert: RpcConvert<Network = Self::NetworkTypes>,
321 > + RpcNodeCoreExt
322{
323 fn state_at_hash(&self, block_hash: B256) -> Result<StateProviderBox, Self::Error> {
325 self.provider().history_by_block_hash(block_hash).map_err(Self::Error::from_eth_err)
326 }
327
328 fn state_at_block_id(
333 &self,
334 at: BlockId,
335 ) -> impl Future<Output = Result<StateProviderBox, Self::Error>> + Send
336 where
337 Self: SpawnBlocking,
338 {
339 async move {
340 if at.is_pending() &&
341 let Ok(Some(state)) = self.local_pending_state().await
342 {
343 return Ok(state)
344 }
345
346 self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)
347 }
348 }
349
350 fn latest_state(&self) -> Result<StateProviderBox, Self::Error> {
352 self.provider().latest().map_err(Self::Error::from_eth_err)
353 }
354
355 fn state_at_block_id_or_latest(
359 &self,
360 block_id: Option<BlockId>,
361 ) -> impl Future<Output = Result<StateProviderBox, Self::Error>> + Send
362 where
363 Self: SpawnBlocking,
364 {
365 async move {
366 if let Some(block_id) = block_id {
367 self.state_at_block_id(block_id).await
368 } else {
369 Ok(self.latest_state()?)
370 }
371 }
372 }
373
374 fn evm_env_for_header(
376 &self,
377 header: &SealedHeaderFor<Self::Primitives>,
378 ) -> Result<EvmEnvFor<Self::Evm>, Self::Error> {
379 self.evm_config()
380 .evm_env(header)
381 .map_err(RethError::other)
382 .map_err(Self::Error::from_eth_err)
383 }
384
385 fn evm_env_at(
392 &self,
393 at: BlockId,
394 ) -> impl Future<Output = Result<(EvmEnvFor<Self::Evm>, BlockId), Self::Error>> + Send
395 where
396 Self: SpawnBlocking,
397 {
398 async move {
399 if at.is_pending() {
400 let PendingBlockEnv { evm_env, origin } = self.pending_block_env_and_cfg()?;
401 Ok((evm_env, origin.state_block_id()))
402 } else {
403 let header = RpcNodeCore::provider(self)
407 .sealed_header_by_id(at)
408 .map_err(Self::Error::from_eth_err)?
409 .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
410 let evm_env = self.evm_env_for_header(&header)?;
411
412 Ok((evm_env, header.hash().into()))
413 }
414 }
415 }
416
417 #[expect(clippy::type_complexity)]
424 fn evm_env_and_recovered_block_at(
425 &self,
426 at: BlockId,
427 ) -> impl Future<
428 Output = Result<
429 (Arc<RecoveredBlock<BlockTy<Self::Primitives>>>, EvmEnvFor<Self::Evm>, BlockId),
430 Self::Error,
431 >,
432 > + Send
433 where
434 Self: SpawnBlocking + LoadBlock,
435 {
436 async move {
437 if at.is_pending() {
438 let (evm_env, block_id) = self.evm_env_at(at).await?;
439 let block = self
440 .recovered_block(block_id)
441 .await?
442 .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
443
444 Ok((block, evm_env, block_id))
445 } else {
446 let block = self
447 .recovered_block(at)
448 .await?
449 .ok_or_else(|| EthApiError::HeaderNotFound(at))?;
450 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
451 let block_id = block.hash().into();
452
453 Ok((block, evm_env, block_id))
454 }
455 }
456 }
457
458 fn next_available_nonce_for(
464 &self,
465 request: &RpcTxReq<Self::NetworkTypes>,
466 ) -> impl Future<Output = Result<u64, Self::Error>> + Send
467 where
468 Self: SpawnBlocking,
469 {
470 let address = request.as_ref().from;
471 self.spawn_blocking_io(move |this| {
472 let address = match address {
473 Some(address) => address,
474 None => return Err(SignError::NoAccount.into_eth_err()),
475 };
476
477 let mut next_nonce = this
479 .latest_state()?
480 .account_nonce(&address)
481 .map_err(Self::Error::from_eth_err)?
482 .unwrap_or_default();
483
484 if let Some(highest_tx) =
486 this.pool().get_highest_consecutive_transaction_by_sender(address, next_nonce)
487 {
488 next_nonce = highest_tx.nonce().checked_add(1).ok_or_else(|| {
490 Self::Error::from(EthApiError::InvalidTransaction(
491 RpcInvalidTransactionError::NonceMaxValue,
492 ))
493 })?;
494 }
495
496 Ok(next_nonce)
497 })
498 }
499
500 fn transaction_count(
505 &self,
506 address: Address,
507 block_id: Option<BlockId>,
508 ) -> impl Future<Output = Result<U256, Self::Error>> + Send
509 where
510 Self: SpawnBlocking,
511 {
512 self.spawn_blocking_io_fut(async move |this| {
513 let on_chain_account_nonce = this
515 .state_at_block_id_or_latest(block_id)
516 .await?
517 .account_nonce(&address)
518 .map_err(Self::Error::from_eth_err)?
519 .unwrap_or_default();
520
521 if block_id == Some(BlockId::pending()) {
522 if let Some(highest_pool_tx) = this
524 .pool()
525 .get_highest_consecutive_transaction_by_sender(address, on_chain_account_nonce)
526 {
527 {
528 let next_tx_nonce =
531 highest_pool_tx.nonce().checked_add(1).ok_or_else(|| {
532 Self::Error::from(EthApiError::InvalidTransaction(
533 RpcInvalidTransactionError::NonceMaxValue,
534 ))
535 })?;
536
537 let next_tx_nonce = on_chain_account_nonce.max(next_tx_nonce);
539
540 let tx_count = on_chain_account_nonce.max(next_tx_nonce);
541 return Ok(U256::from(tx_count));
542 }
543 }
544 }
545 Ok(U256::from(on_chain_account_nonce))
546 })
547 }
548
549 fn get_code(
551 &self,
552 address: Address,
553 block_id: Option<BlockId>,
554 ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send
555 where
556 Self: SpawnBlocking,
557 {
558 self.spawn_blocking_io_fut(async move |this| {
559 Ok(this
560 .state_at_block_id_or_latest(block_id)
561 .await?
562 .account_code(&address)
563 .map_err(Self::Error::from_eth_err)?
564 .unwrap_or_default()
565 .original_bytes())
566 })
567 }
568}