1use crate::EthApi;
4use alloy_eips::BlockId;
5use alloy_primitives::{Address, U256};
6use reth_rpc_convert::RpcConvert;
7use reth_rpc_eth_api::{
8 helpers::{EthState, LoadPendingBlock, LoadState, SpawnBlocking},
9 FromEthApiError, RpcNodeCore,
10};
11use reth_rpc_eth_types::EthApiError;
12use reth_storage_api::StateProviderFactory;
13
14impl<N, Rpc> EthState for EthApi<N, Rpc>
15where
16 N: RpcNodeCore,
17 Rpc: RpcConvert<Primitives = N::Primitives, Error = EthApiError>,
18 Self: LoadPendingBlock,
19{
20}
21
22impl<N, Rpc> LoadState for EthApi<N, Rpc>
23where
24 N: RpcNodeCore,
25 Rpc: RpcConvert<Primitives = N::Primitives>,
26 Self: LoadPendingBlock,
27{
28 fn transaction_count(
34 &self,
35 address: Address,
36 block_id: Option<BlockId>,
37 ) -> impl Future<Output = Result<U256, Self::Error>> + Send
38 where
39 Self: SpawnBlocking,
40 {
41 let at = block_id.unwrap_or_default();
42 self.spawn_blocking_io(move |this| {
43 let state = if at.is_latest() {
44 this.latest_state()?
45 } else {
46 this.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)?
47 };
48 this.transaction_count_with_state(address, at, &state)
49 })
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use crate::eth::helpers::types::EthRpcConverter;
56
57 use super::*;
58 use alloy_eips::BlockId;
59 use alloy_primitives::{
60 map::{AddressMap, B256Map},
61 Address, StorageKey, StorageValue, B256, U256,
62 };
63 use alloy_rpc_types_eth::TransactionRequest;
64 use alloy_serde::JsonStorageKey;
65 use reth_chainspec::ChainSpec;
66 use reth_ethereum_primitives::Block;
67 use reth_evm_ethereum::EthEvmConfig;
68 use reth_network_api::noop::NoopNetwork;
69 use reth_provider::{
70 test_utils::{ExtendedAccount, MockEthProvider, NoopProvider},
71 ChainSpecProvider,
72 };
73 use reth_rpc_eth_api::{
74 helpers::{pending_block::PendingEnvBuilder, EthCall, EthState, SpawnBlocking},
75 node::{RpcNodeCoreAdapter, RpcNodeCoreExt},
76 EthApiTypes,
77 };
78 use reth_rpc_eth_types::{EthApiSettings, EthStateCache, PendingBlock};
79 use reth_rpc_server_types::constants::DEFAULT_MAX_PROOF_SLOTS;
80 use reth_storage_api::{StateProviderBox, StateProviderFactory};
81 use reth_tasks::{
82 pool::{BlockingTaskGuard, BlockingTaskPool},
83 Runtime,
84 };
85 use reth_transaction_pool::{
86 test_utils::{testing_pool, MockTransaction, TestPool},
87 TransactionOrigin, TransactionPool,
88 };
89 use std::{future::Future, sync::Arc, time::Duration};
90 use tokio::sync::{Mutex, Semaphore};
91
92 fn noop_eth_api() -> EthApi<
93 RpcNodeCoreAdapter<NoopProvider, TestPool, NoopNetwork, EthEvmConfig>,
94 EthRpcConverter<ChainSpec>,
95 > {
96 let provider = NoopProvider::default();
97 let pool = testing_pool();
98 let evm_config = EthEvmConfig::mainnet();
99
100 EthApi::builder(provider, pool, NoopNetwork::default(), evm_config).build()
101 }
102
103 fn mock_eth_api(
104 accounts: AddressMap<ExtendedAccount>,
105 ) -> EthApi<
106 RpcNodeCoreAdapter<MockEthProvider, TestPool, NoopNetwork, EthEvmConfig>,
107 EthRpcConverter<ChainSpec>,
108 > {
109 let pool = testing_pool();
110 let mock_provider = MockEthProvider::default();
111
112 let evm_config = EthEvmConfig::new(mock_provider.chain_spec());
113 mock_provider.extend_accounts(accounts);
114
115 EthApi::builder(mock_provider, pool, NoopNetwork::default(), evm_config).build()
116 }
117
118 #[tokio::test]
119 async fn test_storage() {
120 let eth_api = noop_eth_api();
122 let address = Address::random();
123 let storage = eth_api.storage_at(address, U256::ZERO.into(), None).await.unwrap();
124 assert_eq!(storage, U256::ZERO.to_be_bytes());
125
126 let storage_value = StorageValue::from(1337);
128 let storage_key = StorageKey::random();
129 let storage: B256Map<_> = core::iter::once((storage_key, storage_value)).collect();
130
131 let accounts = AddressMap::from_iter([(
132 address,
133 ExtendedAccount::new(0, U256::ZERO).extend_storage(storage),
134 )]);
135 let eth_api = mock_eth_api(accounts);
136
137 let storage_key: U256 = storage_key.into();
138 let storage = eth_api.storage_at(address, storage_key.into(), None).await.unwrap();
139 assert_eq!(storage, storage_value.to_be_bytes());
140 }
141
142 #[tokio::test]
143 async fn test_get_account_missing() {
144 let eth_api = noop_eth_api();
145 let address = Address::random();
146 let account = eth_api.get_account(address, Default::default()).await.unwrap();
147 assert!(account.is_none());
148 }
149
150 #[test]
151 fn test_get_proof_key_cap() {
152 let eth_api = noop_eth_api();
153 let address = Address::random();
154 let keys = |len| vec![JsonStorageKey::from(B256::ZERO); len];
155
156 assert!(eth_api.get_proof(address, keys(DEFAULT_MAX_PROOF_SLOTS), None).is_ok());
157
158 let Err(err) = eth_api.get_proof(address, keys(DEFAULT_MAX_PROOF_SLOTS + 1), None) else {
159 panic!("expected over-limit request to be rejected");
160 };
161 assert_eq!(err.to_string(), "storage key count 1025 exceeds limit 1024");
162 }
163
164 #[test]
165 fn test_get_multi_proof_cap() {
166 let eth_api = noop_eth_api();
167 let address = Address::random();
168
169 let targets = vec![(address, vec![B256::ZERO; DEFAULT_MAX_PROOF_SLOTS])];
171 assert!(eth_api.get_multi_proof(targets, None).is_ok());
172 let targets = vec![(address, Vec::new()); DEFAULT_MAX_PROOF_SLOTS];
173 assert!(eth_api.get_multi_proof(targets, None).is_ok());
174
175 let targets = vec![(address, vec![B256::ZERO; DEFAULT_MAX_PROOF_SLOTS + 1])];
177 let Err(err) = eth_api.get_multi_proof(targets, None) else {
178 panic!("expected over-limit request to be rejected");
179 };
180 assert_eq!(err.to_string(), "total slot count 1025 exceeds limit 1024");
181
182 let targets = vec![
184 (address, vec![B256::ZERO; DEFAULT_MAX_PROOF_SLOTS]),
185 (Address::random(), vec![B256::ZERO]),
186 ];
187 let Err(err) = eth_api.get_multi_proof(targets, None) else {
188 panic!("expected over-limit request to be rejected");
189 };
190 assert_eq!(err.to_string(), "total slot count 1025 exceeds limit 1024");
191
192 let targets = vec![(address, Vec::new()); DEFAULT_MAX_PROOF_SLOTS + 1];
194 let Err(err) = eth_api.get_multi_proof(targets, None) else {
195 panic!("expected over-limit request to be rejected");
196 };
197 assert_eq!(err.to_string(), "target count 1025 exceeds limit 1024");
198 }
199
200 #[cfg(feature = "account-ext")]
201 #[tokio::test]
202 async fn test_get_account_extension() {
203 let address = Address::random();
204 let extension = reth_primitives_traits::AccountExtension::copy_from_slice(&[0x01]);
205 let eth_api = mock_eth_api(AddressMap::from_iter([(
206 address,
207 ExtendedAccount::new(0, U256::ZERO).with_extension(extension.clone()),
208 )]));
209 eth_api.provider().add_block(B256::ZERO, Block::default());
210
211 let info = eth_api.get_account_info(address, Default::default()).await.unwrap();
213 assert!(!info.is_empty());
214 assert_eq!(info.extension, extension);
215 let json = serde_json::to_value(&info).unwrap();
216 assert_eq!(json["extension"], "0x01");
217 assert_eq!(serde_json::from_value::<alloy_rpc_types_eth::AccountInfo>(json).unwrap(), info);
218
219 let account = eth_api.get_account(address, Default::default()).await.unwrap().unwrap();
220 assert_eq!(account.extension, extension);
221
222 let missing =
223 eth_api.get_account_info(Address::random(), Default::default()).await.unwrap();
224 assert!(missing.is_empty());
225 assert!(serde_json::to_value(missing).unwrap().get("extension").is_none());
226 }
227
228 #[test]
229 fn pending_state_and_access_list_do_not_deadlock() {
230 let runtime = tokio::runtime::Builder::new_multi_thread()
231 .worker_threads(2)
232 .max_blocking_threads(1)
233 .enable_all()
234 .build()
235 .unwrap();
236
237 let result = runtime.block_on(async {
238 let address = Address::random();
239 let accounts =
240 AddressMap::from_iter([(address, ExtendedAccount::new(0, U256::from(1337)))]);
241 let eth_api = mock_eth_api(accounts);
242 let mut block = Block::default();
244 block.header.number = 13_000_000;
245 block.header.timestamp = 1_629_000_000;
246 block.header.gas_limit = 30_000_000;
247 block.header.base_fee_per_gas = Some(1_000_000_000);
248 eth_api.provider().add_block(B256::ZERO, block);
249 tokio::time::timeout(Duration::from_secs(10), async {
250 let balance = eth_api.balance(address, Some(BlockId::pending())).await?;
251 eth_api
252 .create_access_list_at(
253 TransactionRequest { gas_price: Some(0), ..Default::default() },
254 Some(BlockId::latest()),
255 None,
256 )
257 .await?;
258 Ok::<_, EthApiError>(balance)
259 })
260 .await
261 });
262 runtime.shutdown_background();
264 assert_eq!(
265 result.expect("RPC timed out on one blocking thread").expect("RPC returned an error"),
266 U256::from(1337)
267 );
268 }
269
270 type MockEthApi = EthApi<
271 RpcNodeCoreAdapter<MockEthProvider, TestPool, NoopNetwork, EthEvmConfig>,
272 EthRpcConverter<ChainSpec>,
273 >;
274
275 #[derive(Clone)]
276 struct CustomPendingState {
277 inner: MockEthApi,
278 pending: MockEthProvider,
279 }
280
281 impl EthApiTypes for CustomPendingState {
282 type Error = EthApiError;
283 type NetworkTypes = <MockEthApi as EthApiTypes>::NetworkTypes;
284 type RpcConvert = <MockEthApi as EthApiTypes>::RpcConvert;
285
286 fn eth_api_settings(&self) -> &EthApiSettings {
287 self.inner.eth_api_settings()
288 }
289
290 fn converter(&self) -> &Self::RpcConvert {
291 self.inner.converter()
292 }
293 }
294
295 impl RpcNodeCore for CustomPendingState {
296 type Primitives = <MockEthApi as RpcNodeCore>::Primitives;
297 type Provider = <MockEthApi as RpcNodeCore>::Provider;
298 type Pool = <MockEthApi as RpcNodeCore>::Pool;
299 type Evm = <MockEthApi as RpcNodeCore>::Evm;
300 type Network = <MockEthApi as RpcNodeCore>::Network;
301
302 fn pool(&self) -> &Self::Pool {
303 self.inner.pool()
304 }
305
306 fn evm_config(&self) -> &Self::Evm {
307 self.inner.evm_config()
308 }
309
310 fn network(&self) -> &Self::Network {
311 self.inner.network()
312 }
313
314 fn provider(&self) -> &Self::Provider {
315 self.inner.provider()
316 }
317 }
318
319 impl RpcNodeCoreExt for CustomPendingState {
320 fn cache(&self) -> &EthStateCache<Self::Primitives> {
321 self.inner.cache()
322 }
323 }
324
325 impl SpawnBlocking for CustomPendingState {
326 fn io_task_spawner(&self) -> &Runtime {
327 self.inner.io_task_spawner()
328 }
329
330 fn tracing_task_pool(&self) -> &BlockingTaskPool {
331 self.inner.tracing_task_pool()
332 }
333
334 fn tracing_task_guard(&self) -> &BlockingTaskGuard {
335 self.inner.tracing_task_guard()
336 }
337
338 fn blocking_io_task_guard(&self) -> &Arc<Semaphore> {
339 self.inner.blocking_io_task_guard()
340 }
341 }
342
343 impl LoadPendingBlock for CustomPendingState {
344 fn pending_block(&self) -> &Mutex<Option<PendingBlock<Self::Primitives>>> {
345 self.inner.pending_block()
346 }
347
348 fn pending_env_builder(&self) -> &dyn PendingEnvBuilder<Self::Evm> {
349 self.inner.pending_env_builder()
350 }
351
352 fn local_pending_state(
353 &self,
354 ) -> impl Future<Output = Result<Option<StateProviderBox>, Self::Error>> + Send {
355 let state = self.pending.latest().map_err(EthApiError::from);
356 async move { state.map(Some) }
357 }
358 }
359
360 impl LoadState for CustomPendingState {}
361
362 impl EthState for CustomPendingState {}
363
364 #[tokio::test]
365 async fn pending_state_reads_use_existing_override() {
366 let address = Address::random();
367 let chain = AddressMap::from_iter([(address, ExtendedAccount::new(0, U256::from(1337)))]);
368 let eth_api = mock_eth_api(chain);
369 eth_api.provider().add_block(B256::ZERO, Block::default());
370
371 let pending = MockEthProvider::default();
372 pending.extend_accounts([(address, ExtendedAccount::new(0, U256::from(42)))]);
373 let eth_api = CustomPendingState { inner: eth_api, pending };
374
375 assert_eq!(
376 eth_api.balance(address, Some(BlockId::pending())).await.unwrap(),
377 U256::from(42)
378 );
379 assert_eq!(eth_api.balance(address, None).await.unwrap(), U256::from(1337));
380 }
381
382 #[tokio::test]
383 async fn pending_transaction_count_skips_local_pending_block() {
384 let address = Address::random();
385 let accounts = AddressMap::from_iter([(address, ExtendedAccount::new(7, U256::ZERO))]);
386 let eth_api = mock_eth_api(accounts);
387 let mut block = Block::default();
388 block.header.number = 13_000_000;
389 block.header.timestamp = 1_629_000_000;
390 block.header.gas_limit = 30_000_000;
391 block.header.base_fee_per_gas = Some(1_000_000_000);
392 eth_api.provider().add_block(B256::ZERO, block);
393
394 let pending = Some(BlockId::pending());
395 let count = EthState::transaction_count(ð_api, address, pending).await.unwrap();
396 assert_eq!(count, U256::from(7));
397
398 for nonce in [7, 8, 10] {
400 let tx = MockTransaction::eip1559().with_sender(address).with_nonce(nonce);
401 eth_api.pool().add_transaction(TransactionOrigin::External, tx).await.unwrap();
402 }
403 let count = EthState::transaction_count(ð_api, address, pending).await.unwrap();
404 assert_eq!(count, U256::from(9));
405 assert_eq!(
406 EthState::transaction_count(ð_api, address, None).await.unwrap(),
407 U256::from(7)
408 );
409
410 assert!(eth_api.pending_block().lock().await.is_none());
411 }
412}