@@ -13,6 +13,7 @@ use alloy_rpc_types::BlockId;
13
13
use eyre:: WrapErr ;
14
14
use futures:: {
15
15
channel:: mpsc:: { unbounded, UnboundedReceiver , UnboundedSender } ,
16
+ pin_mut,
16
17
stream:: Stream ,
17
18
task:: { Context , Poll } ,
18
19
Future , FutureExt ,
@@ -32,10 +33,12 @@ use std::{
32
33
path:: Path ,
33
34
pin:: Pin ,
34
35
sync:: {
36
+ atomic:: { AtomicU8 , Ordering } ,
35
37
mpsc:: { channel as oneshot_channel, Sender as OneshotSender } ,
36
38
Arc ,
37
39
} ,
38
40
} ;
41
+ use tokio:: select;
39
42
40
43
/// Logged when an error is indicative that the user is trying to fork from a non-archive node.
41
44
pub const NON_ARCHIVE_NODE_WARNING : & str = "\
@@ -64,6 +67,14 @@ type AddressData = AddressHashMap<AccountInfo>;
64
67
type StorageData = AddressHashMap < StorageInfo > ;
65
68
type BlockHashData = HashMap < U256 , B256 > ;
66
69
70
+ /// States for tracking which account endpoints should be used when account info
71
+ const ACCOUNT_FETCH_UNCHECKED : u8 = 0 ;
72
+ /// Endpoints supports the non standard eth_getAccountInfo which is more efficient than sending 3
73
+ /// separate requests
74
+ const ACCOUNT_FETCH_SUPPORTS_ACC_INFO : u8 = 1 ;
75
+ /// Use regular individual getCode, getNonce, getBalance calls
76
+ const ACCOUNT_FETCH_SEPARATE_REQUESTS : u8 = 2 ;
77
+
67
78
struct AnyRequestFuture < T , Err > {
68
79
sender : OneshotSender < Result < T , Err > > ,
69
80
future : Pin < Box < dyn Future < Output = Result < T , Err > > + Send > > ,
@@ -163,6 +174,8 @@ pub struct BackendHandler<P> {
163
174
/// The block to fetch data from.
164
175
// This is an `Option` so that we can have less code churn in the functions below
165
176
block_id : Option < BlockId > ,
177
+ /// The mode for fetching account data
178
+ account_fetch_mode : Arc < AtomicU8 > ,
166
179
}
167
180
168
181
impl < P > BackendHandler < P >
@@ -185,6 +198,7 @@ where
185
198
queued_requests : Default :: default ( ) ,
186
199
incoming : rx,
187
200
block_id,
201
+ account_fetch_mode : Arc :: new ( AtomicU8 :: new ( ACCOUNT_FETCH_UNCHECKED ) ) ,
188
202
}
189
203
}
190
204
@@ -281,16 +295,100 @@ where
281
295
/// returns the future that fetches the account data
282
296
fn get_account_req ( & self , address : Address ) -> ProviderRequest < eyre:: Report > {
283
297
trace ! ( target: "backendhandler" , "preparing account request, address={:?}" , address) ;
298
+
284
299
let provider = self . provider . clone ( ) ;
285
300
let block_id = self . block_id . unwrap_or_default ( ) ;
286
- let fut = Box :: pin ( async move {
287
- let balance = provider. get_balance ( address) . block_id ( block_id) . into_future ( ) ;
288
- let nonce = provider. get_transaction_count ( address) . block_id ( block_id) . into_future ( ) ;
289
- let code = provider. get_code_at ( address) . block_id ( block_id) . into_future ( ) ;
290
- let resp = tokio:: try_join!( balance, nonce, code) . map_err ( Into :: into) ;
291
- ( resp, address)
292
- } ) ;
293
- ProviderRequest :: Account ( fut)
301
+ let mode = Arc :: clone ( & self . account_fetch_mode ) ;
302
+ let fut = async move {
303
+ // depending on the tracked mode we can dispatch requests.
304
+ let initial_mode = mode. load ( Ordering :: Relaxed ) ;
305
+ match initial_mode {
306
+ ACCOUNT_FETCH_UNCHECKED => {
307
+ // single request for accountinfo object
308
+ let acc_info_fut =
309
+ provider. get_account_info ( address) . block_id ( block_id) . into_future ( ) ;
310
+
311
+ // tri request for account info
312
+ let balance_fut =
313
+ provider. get_balance ( address) . block_id ( block_id) . into_future ( ) ;
314
+ let nonce_fut =
315
+ provider. get_transaction_count ( address) . block_id ( block_id) . into_future ( ) ;
316
+ let code_fut = provider. get_code_at ( address) . block_id ( block_id) . into_future ( ) ;
317
+ let triple_fut = futures:: future:: try_join3 ( balance_fut, nonce_fut, code_fut) ;
318
+ pin_mut ! ( acc_info_fut, triple_fut) ;
319
+
320
+ select ! {
321
+ acc_info = & mut acc_info_fut => {
322
+ match acc_info {
323
+ Ok ( info) => {
324
+ trace!( target: "backendhandler" , "endpoint supports eth_getAccountInfo" ) ;
325
+ mode. store( ACCOUNT_FETCH_SUPPORTS_ACC_INFO , Ordering :: Relaxed ) ;
326
+ Ok ( ( info. balance, info. nonce, info. code) )
327
+ }
328
+ Err ( err) => {
329
+ trace!( target: "backendhandler" , ?err, "failed initial eth_getAccountInfo call" ) ;
330
+ mode. store( ACCOUNT_FETCH_SEPARATE_REQUESTS , Ordering :: Relaxed ) ;
331
+ Ok ( triple_fut. await ?)
332
+ }
333
+ }
334
+ }
335
+ triple = & mut triple_fut => {
336
+ match triple {
337
+ Ok ( ( balance, nonce, code) ) => {
338
+ mode. store( ACCOUNT_FETCH_SEPARATE_REQUESTS , Ordering :: Relaxed ) ;
339
+ Ok ( ( balance, nonce, code) )
340
+ }
341
+ Err ( err) => Err ( err. into( ) )
342
+ }
343
+ }
344
+ }
345
+ }
346
+
347
+ ACCOUNT_FETCH_SUPPORTS_ACC_INFO => {
348
+ let mut res = provider
349
+ . get_account_info ( address)
350
+ . block_id ( block_id)
351
+ . into_future ( )
352
+ . await
353
+ . map ( |info| ( info. balance , info. nonce , info. code ) ) ;
354
+
355
+ // it's possible that the configured endpoint load balances requests to multiple
356
+ // instances and not all support that endpoint so we should reset here
357
+ if res. is_err ( ) {
358
+ mode. store ( ACCOUNT_FETCH_SEPARATE_REQUESTS , Ordering :: Relaxed ) ;
359
+
360
+ let balance_fut =
361
+ provider. get_balance ( address) . block_id ( block_id) . into_future ( ) ;
362
+ let nonce_fut = provider
363
+ . get_transaction_count ( address)
364
+ . block_id ( block_id)
365
+ . into_future ( ) ;
366
+ let code_fut =
367
+ provider. get_code_at ( address) . block_id ( block_id) . into_future ( ) ;
368
+ res = futures:: future:: try_join3 ( balance_fut, nonce_fut, code_fut) . await ;
369
+ }
370
+
371
+ Ok ( res?)
372
+ }
373
+
374
+ ACCOUNT_FETCH_SEPARATE_REQUESTS => {
375
+ let balance_fut =
376
+ provider. get_balance ( address) . block_id ( block_id) . into_future ( ) ;
377
+ let nonce_fut =
378
+ provider. get_transaction_count ( address) . block_id ( block_id) . into_future ( ) ;
379
+ let code_fut = provider. get_code_at ( address) . block_id ( block_id) . into_future ( ) ;
380
+
381
+ Ok ( futures:: future:: try_join3 ( balance_fut, nonce_fut, code_fut) . await ?)
382
+ }
383
+
384
+ _ => unreachable ! ( "Invalid account fetch mode" ) ,
385
+ }
386
+ } ;
387
+
388
+ ProviderRequest :: Account ( Box :: pin ( async move {
389
+ let result = fut. await ;
390
+ ( result, address)
391
+ } ) )
294
392
}
295
393
296
394
/// process a request for an account
0 commit comments