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