1use crate::EthApi;
4use reth_rpc_convert::RpcConvert;
5use reth_rpc_eth_api::{
6 helpers::{EthState, LoadPendingBlock, LoadState},
7 RpcNodeCore,
8};
9use reth_rpc_eth_types::EthApiError;
10
11impl<N, Rpc> EthState for EthApi<N, Rpc>
12where
13 N: RpcNodeCore,
14 Rpc: RpcConvert<Primitives = N::Primitives, Error = EthApiError>,
15 Self: LoadPendingBlock,
16{
17}
18
19impl<N, Rpc> LoadState for EthApi<N, Rpc>
20where
21 N: RpcNodeCore,
22 Rpc: RpcConvert<Primitives = N::Primitives>,
23 Self: LoadPendingBlock,
24{
25}
26
27#[cfg(test)]
28mod tests {
29 use crate::eth::helpers::types::EthRpcConverter;
30
31 use super::*;
32 use alloy_eips::BlockId;
33 use alloy_primitives::{
34 map::{AddressMap, B256Map},
35 Address, StorageKey, StorageValue, B256, U256,
36 };
37 use alloy_rpc_types_eth::TransactionRequest;
38 use reth_chainspec::ChainSpec;
39 use reth_ethereum_primitives::Block;
40 use reth_evm_ethereum::EthEvmConfig;
41 use reth_network_api::noop::NoopNetwork;
42 use reth_provider::{
43 test_utils::{ExtendedAccount, MockEthProvider, NoopProvider},
44 ChainSpecProvider,
45 };
46 use reth_rpc_eth_api::{
47 helpers::{pending_block::PendingEnvBuilder, EthCall, EthState, SpawnBlocking},
48 node::{RpcNodeCoreAdapter, RpcNodeCoreExt},
49 EthApiTypes,
50 };
51 use reth_rpc_eth_types::{EthApiSettings, EthStateCache, PendingBlock};
52 use reth_storage_api::{StateProviderBox, StateProviderFactory};
53 use reth_tasks::{
54 pool::{BlockingTaskGuard, BlockingTaskPool},
55 Runtime,
56 };
57 use reth_transaction_pool::test_utils::{testing_pool, TestPool};
58 use std::{future::Future, sync::Arc, time::Duration};
59 use tokio::sync::{Mutex, Semaphore};
60
61 fn noop_eth_api() -> EthApi<
62 RpcNodeCoreAdapter<NoopProvider, TestPool, NoopNetwork, EthEvmConfig>,
63 EthRpcConverter<ChainSpec>,
64 > {
65 let provider = NoopProvider::default();
66 let pool = testing_pool();
67 let evm_config = EthEvmConfig::mainnet();
68
69 EthApi::builder(provider, pool, NoopNetwork::default(), evm_config).build()
70 }
71
72 fn mock_eth_api(
73 accounts: AddressMap<ExtendedAccount>,
74 ) -> EthApi<
75 RpcNodeCoreAdapter<MockEthProvider, TestPool, NoopNetwork, EthEvmConfig>,
76 EthRpcConverter<ChainSpec>,
77 > {
78 let pool = testing_pool();
79 let mock_provider = MockEthProvider::default();
80
81 let evm_config = EthEvmConfig::new(mock_provider.chain_spec());
82 mock_provider.extend_accounts(accounts);
83
84 EthApi::builder(mock_provider, pool, NoopNetwork::default(), evm_config).build()
85 }
86
87 #[tokio::test]
88 async fn test_storage() {
89 let eth_api = noop_eth_api();
91 let address = Address::random();
92 let storage = eth_api.storage_at(address, U256::ZERO.into(), None).await.unwrap();
93 assert_eq!(storage, U256::ZERO.to_be_bytes());
94
95 let storage_value = StorageValue::from(1337);
97 let storage_key = StorageKey::random();
98 let storage: B256Map<_> = core::iter::once((storage_key, storage_value)).collect();
99
100 let accounts = AddressMap::from_iter([(
101 address,
102 ExtendedAccount::new(0, U256::ZERO).extend_storage(storage),
103 )]);
104 let eth_api = mock_eth_api(accounts);
105
106 let storage_key: U256 = storage_key.into();
107 let storage = eth_api.storage_at(address, storage_key.into(), None).await.unwrap();
108 assert_eq!(storage, storage_value.to_be_bytes());
109 }
110
111 #[tokio::test]
112 async fn test_get_account_missing() {
113 let eth_api = noop_eth_api();
114 let address = Address::random();
115 let account = eth_api.get_account(address, Default::default()).await.unwrap();
116 assert!(account.is_none());
117 }
118
119 #[cfg(feature = "account-ext")]
120 #[tokio::test]
121 async fn test_get_account_extension() {
122 let address = Address::random();
123 let extension = reth_primitives_traits::AccountExtension::copy_from_slice(&[0x01]);
124 let eth_api = mock_eth_api(AddressMap::from_iter([(
125 address,
126 ExtendedAccount::new(0, U256::ZERO).with_extension(extension.clone()),
127 )]));
128 eth_api.provider().add_block(B256::ZERO, Block::default());
129
130 let info = eth_api.get_account_info(address, Default::default()).await.unwrap();
132 assert!(!info.is_empty());
133 assert_eq!(info.extension, extension);
134 let json = serde_json::to_value(&info).unwrap();
135 assert_eq!(json["extension"], "0x01");
136 assert_eq!(serde_json::from_value::<alloy_rpc_types_eth::AccountInfo>(json).unwrap(), info);
137
138 let account = eth_api.get_account(address, Default::default()).await.unwrap().unwrap();
139 assert_eq!(account.extension, extension);
140
141 let missing =
142 eth_api.get_account_info(Address::random(), Default::default()).await.unwrap();
143 assert!(missing.is_empty());
144 assert!(serde_json::to_value(missing).unwrap().get("extension").is_none());
145 }
146
147 #[test]
148 fn pending_state_and_access_list_do_not_deadlock() {
149 let runtime = tokio::runtime::Builder::new_multi_thread()
150 .worker_threads(2)
151 .max_blocking_threads(1)
152 .enable_all()
153 .build()
154 .unwrap();
155
156 let result = runtime.block_on(async {
157 let address = Address::random();
158 let accounts =
159 AddressMap::from_iter([(address, ExtendedAccount::new(0, U256::from(1337)))]);
160 let eth_api = mock_eth_api(accounts);
161 let mut block = Block::default();
163 block.header.number = 13_000_000;
164 block.header.timestamp = 1_629_000_000;
165 block.header.gas_limit = 30_000_000;
166 block.header.base_fee_per_gas = Some(1_000_000_000);
167 eth_api.provider().add_block(B256::ZERO, block);
168 tokio::time::timeout(Duration::from_secs(10), async {
169 let balance = eth_api.balance(address, Some(BlockId::pending())).await?;
170 eth_api
171 .create_access_list_at(
172 TransactionRequest { gas_price: Some(0), ..Default::default() },
173 Some(BlockId::latest()),
174 None,
175 )
176 .await?;
177 Ok::<_, EthApiError>(balance)
178 })
179 .await
180 });
181 runtime.shutdown_background();
183 assert_eq!(
184 result.expect("RPC timed out on one blocking thread").expect("RPC returned an error"),
185 U256::from(1337)
186 );
187 }
188
189 type MockEthApi = EthApi<
190 RpcNodeCoreAdapter<MockEthProvider, TestPool, NoopNetwork, EthEvmConfig>,
191 EthRpcConverter<ChainSpec>,
192 >;
193
194 #[derive(Clone)]
195 struct CustomPendingState {
196 inner: MockEthApi,
197 pending: MockEthProvider,
198 }
199
200 impl EthApiTypes for CustomPendingState {
201 type Error = EthApiError;
202 type NetworkTypes = <MockEthApi as EthApiTypes>::NetworkTypes;
203 type RpcConvert = <MockEthApi as EthApiTypes>::RpcConvert;
204
205 fn eth_api_settings(&self) -> &EthApiSettings {
206 self.inner.eth_api_settings()
207 }
208
209 fn converter(&self) -> &Self::RpcConvert {
210 self.inner.converter()
211 }
212 }
213
214 impl RpcNodeCore for CustomPendingState {
215 type Primitives = <MockEthApi as RpcNodeCore>::Primitives;
216 type Provider = <MockEthApi as RpcNodeCore>::Provider;
217 type Pool = <MockEthApi as RpcNodeCore>::Pool;
218 type Evm = <MockEthApi as RpcNodeCore>::Evm;
219 type Network = <MockEthApi as RpcNodeCore>::Network;
220
221 fn pool(&self) -> &Self::Pool {
222 self.inner.pool()
223 }
224
225 fn evm_config(&self) -> &Self::Evm {
226 self.inner.evm_config()
227 }
228
229 fn network(&self) -> &Self::Network {
230 self.inner.network()
231 }
232
233 fn provider(&self) -> &Self::Provider {
234 self.inner.provider()
235 }
236 }
237
238 impl RpcNodeCoreExt for CustomPendingState {
239 fn cache(&self) -> &EthStateCache<Self::Primitives> {
240 self.inner.cache()
241 }
242 }
243
244 impl SpawnBlocking for CustomPendingState {
245 fn io_task_spawner(&self) -> &Runtime {
246 self.inner.io_task_spawner()
247 }
248
249 fn tracing_task_pool(&self) -> &BlockingTaskPool {
250 self.inner.tracing_task_pool()
251 }
252
253 fn tracing_task_guard(&self) -> &BlockingTaskGuard {
254 self.inner.tracing_task_guard()
255 }
256
257 fn blocking_io_task_guard(&self) -> &Arc<Semaphore> {
258 self.inner.blocking_io_task_guard()
259 }
260 }
261
262 impl LoadPendingBlock for CustomPendingState {
263 fn pending_block(&self) -> &Mutex<Option<PendingBlock<Self::Primitives>>> {
264 self.inner.pending_block()
265 }
266
267 fn pending_env_builder(&self) -> &dyn PendingEnvBuilder<Self::Evm> {
268 self.inner.pending_env_builder()
269 }
270
271 fn local_pending_state(
272 &self,
273 ) -> impl Future<Output = Result<Option<StateProviderBox>, Self::Error>> + Send {
274 let state = self.pending.latest().map_err(EthApiError::from);
275 async move { state.map(Some) }
276 }
277 }
278
279 impl LoadState for CustomPendingState {}
280
281 impl EthState for CustomPendingState {}
282
283 #[tokio::test]
284 async fn pending_state_reads_use_existing_override() {
285 let address = Address::random();
286 let chain = AddressMap::from_iter([(address, ExtendedAccount::new(0, U256::from(1337)))]);
287 let eth_api = mock_eth_api(chain);
288 eth_api.provider().add_block(B256::ZERO, Block::default());
289
290 let pending = MockEthProvider::default();
291 pending.extend_accounts([(address, ExtendedAccount::new(0, U256::from(42)))]);
292 let eth_api = CustomPendingState { inner: eth_api, pending };
293
294 assert_eq!(
295 eth_api.balance(address, Some(BlockId::pending())).await.unwrap(),
296 U256::from(42)
297 );
298 assert_eq!(eth_api.balance(address, None).await.unwrap(), U256::from(1337));
299 }
300}