Skip to content

Commit b9d6d31

Browse files
Rimeeeeeemattsse
andauthored
feat: support getAccount mode (#48)
* feat: support getAccount mode * fixes * fixes * fixes * fixes * optimize for ethgetaccinfo * style * avoid double box --------- Co-authored-by: Matthias Seitz <[email protected]>
1 parent 50b8aff commit b9d6d31

File tree

1 file changed

+106
-8
lines changed

1 file changed

+106
-8
lines changed

src/backend.rs

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use alloy_rpc_types::BlockId;
1313
use eyre::WrapErr;
1414
use futures::{
1515
channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender},
16+
pin_mut,
1617
stream::Stream,
1718
task::{Context, Poll},
1819
Future, FutureExt,
@@ -32,10 +33,12 @@ use std::{
3233
path::Path,
3334
pin::Pin,
3435
sync::{
36+
atomic::{AtomicU8, Ordering},
3537
mpsc::{channel as oneshot_channel, Sender as OneshotSender},
3638
Arc,
3739
},
3840
};
41+
use tokio::select;
3942

4043
/// Logged when an error is indicative that the user is trying to fork from a non-archive node.
4144
pub const NON_ARCHIVE_NODE_WARNING: &str = "\
@@ -64,6 +67,14 @@ type AddressData = AddressHashMap<AccountInfo>;
6467
type StorageData = AddressHashMap<StorageInfo>;
6568
type BlockHashData = HashMap<U256, B256>;
6669

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+
6778
struct AnyRequestFuture<T, Err> {
6879
sender: OneshotSender<Result<T, Err>>,
6980
future: Pin<Box<dyn Future<Output = Result<T, Err>> + Send>>,
@@ -163,6 +174,8 @@ pub struct BackendHandler<P> {
163174
/// The block to fetch data from.
164175
// This is an `Option` so that we can have less code churn in the functions below
165176
block_id: Option<BlockId>,
177+
/// The mode for fetching account data
178+
account_fetch_mode: Arc<AtomicU8>,
166179
}
167180

168181
impl<P> BackendHandler<P>
@@ -185,6 +198,7 @@ where
185198
queued_requests: Default::default(),
186199
incoming: rx,
187200
block_id,
201+
account_fetch_mode: Arc::new(AtomicU8::new(ACCOUNT_FETCH_UNCHECKED)),
188202
}
189203
}
190204

@@ -281,16 +295,100 @@ where
281295
/// returns the future that fetches the account data
282296
fn get_account_req(&self, address: Address) -> ProviderRequest<eyre::Report> {
283297
trace!(target: "backendhandler", "preparing account request, address={:?}", address);
298+
284299
let provider = self.provider.clone();
285300
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+
}))
294392
}
295393

296394
/// process a request for an account

0 commit comments

Comments
 (0)