1use crate::{
4 cache::{BlockchainDb, FlushJsonBlockCacheDB, ForkBlockEnv, MemDb, StorageInfo},
5 error::{DatabaseError, DatabaseResult},
6};
7use alloy_primitives::{Address, B256, Bytes, U256, keccak256, map::U256Map};
8use alloy_provider::{
9 DynProvider, Network, Provider,
10 network::{AnyNetwork, BlockResponse, primitives::HeaderResponse},
11};
12use alloy_rpc_types::BlockId;
13use eyre::WrapErr;
14use futures::{
15 FutureExt,
16 channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded},
17 pin_mut,
18 stream::Stream,
19 task::{Context, Poll},
20};
21use revm::{
22 context::BlockEnv,
23 database::DatabaseRef,
24 primitives::{
25 KECCAK_EMPTY,
26 map::{AddressHashMap, HashMap, hash_map::Entry},
27 },
28 state::{AccountInfo, Bytecode},
29};
30use serde::Serialize;
31use std::{
32 collections::VecDeque,
33 fmt,
34 path::Path,
35 pin::Pin,
36 sync::{
37 Arc,
38 atomic::{AtomicU8, Ordering},
39 mpsc::{Sender as OneshotSender, channel as oneshot_channel},
40 },
41};
42use tokio::select;
43
44pub const NON_ARCHIVE_NODE_WARNING: &str = "\
46It looks like you're trying to fork from an older block with a non-archive node which is not \
47supported. Please try to change your RPC url to an archive node if the issue persists.";
48
49type AccountFuture<Err> =
52 Pin<Box<dyn Future<Output = (Result<(U256, u64, Bytes), Err>, Address)> + Send>>;
53type StorageFuture<Err> = Pin<Box<dyn Future<Output = (Result<U256, Err>, Address, U256)> + Send>>;
54type BlockHashFuture<Err> = Pin<Box<dyn Future<Output = (Result<B256, Err>, u64)> + Send>>;
55type FullBlockFuture<Err, N = AnyNetwork> = Pin<
56 Box<
57 dyn Future<
58 Output = (
59 FullBlockSender<N>,
60 Result<Option<<N as Network>::BlockResponse>, Err>,
61 BlockId,
62 ),
63 > + Send,
64 >,
65>;
66type TransactionFuture<Err, N = AnyNetwork> = Pin<
67 Box<
68 dyn Future<
69 Output = (
70 TransactionSender<N>,
71 Result<<N as Network>::TransactionResponse, Err>,
72 B256,
73 ),
74 > + Send,
75 >,
76>;
77
78type AccountInfoSender = OneshotSender<DatabaseResult<AccountInfo>>;
79type StorageSender = OneshotSender<DatabaseResult<U256>>;
80type BlockHashSender = OneshotSender<DatabaseResult<B256>>;
81type FullBlockSender<N = AnyNetwork> = OneshotSender<DatabaseResult<<N as Network>::BlockResponse>>;
82type TransactionSender<N = AnyNetwork> =
83 OneshotSender<DatabaseResult<<N as Network>::TransactionResponse>>;
84
85type AddressData = AddressHashMap<AccountInfo>;
86type StorageData = AddressHashMap<StorageInfo>;
87type BlockHashData = U256Map<B256>;
88
89const ACCOUNT_FETCH_UNCHECKED: u8 = 0;
91const ACCOUNT_FETCH_SUPPORTS_ACC_INFO: u8 = 1;
94const ACCOUNT_FETCH_SEPARATE_REQUESTS: u8 = 2;
96
97struct AnyRequestFuture<T, Err> {
98 sender: OneshotSender<Result<T, Err>>,
99 future: Pin<Box<dyn Future<Output = Result<T, Err>> + Send>>,
100}
101
102impl<T, Err> fmt::Debug for AnyRequestFuture<T, Err> {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 f.debug_tuple("AnyRequestFuture").field(&self.sender).finish()
105 }
106}
107
108trait WrappedAnyRequest: Unpin + Send + fmt::Debug {
109 fn poll_inner(&mut self, cx: &mut Context<'_>) -> Poll<()>;
110}
111
112impl<T, Err> WrappedAnyRequest for AnyRequestFuture<T, Err>
121where
122 T: fmt::Debug + Send + 'static,
123 Err: fmt::Debug + Send + 'static,
124{
125 fn poll_inner(&mut self, cx: &mut Context<'_>) -> Poll<()> {
126 match self.future.poll_unpin(cx) {
127 Poll::Ready(result) => {
128 let _ = self.sender.send(result);
129 Poll::Ready(())
130 }
131 Poll::Pending => Poll::Pending,
132 }
133 }
134}
135
136enum ProviderRequest<Err, N: Network = AnyNetwork> {
138 Account(AccountFuture<Err>),
139 Storage(StorageFuture<Err>),
140 BlockHash(BlockHashFuture<Err>),
141 FullBlock(FullBlockFuture<Err, N>),
142 Transaction(TransactionFuture<Err, N>),
143 AnyRequest(Box<dyn WrappedAnyRequest>),
144}
145
146#[derive(Debug)]
148enum BackendRequest<N: Network = AnyNetwork> {
149 Basic(Address, AccountInfoSender),
151 Storage(Address, U256, StorageSender),
153 BlockHash(u64, BlockHashSender),
155 FullBlock(BlockId, FullBlockSender<N>),
157 Transaction(B256, TransactionSender<N>),
159 SetPinnedBlock(BlockId),
161
162 UpdateAddress(AddressData),
164 UpdateStorage(StorageData),
166 UpdateBlockHash(BlockHashData),
168 AnyRequest(Box<dyn WrappedAnyRequest>),
170}
171
172#[must_use = "futures do nothing unless polled"]
177pub struct BackendHandler<N: Network = AnyNetwork, B = BlockEnv> {
178 provider: DynProvider<N>,
179 db: BlockchainDb<B>,
181 pending_requests: Vec<ProviderRequest<eyre::Report, N>>,
183 account_requests: HashMap<Address, Vec<AccountInfoSender>>,
185 storage_requests: HashMap<(Address, U256), Vec<StorageSender>>,
187 block_requests: HashMap<u64, Vec<BlockHashSender>>,
189 incoming: UnboundedReceiver<BackendRequest<N>>,
191 queued_requests: VecDeque<BackendRequest<N>>,
193 block_id: Option<BlockId>,
196 account_fetch_mode: Arc<AtomicU8>,
198}
199
200impl<N: Network, B: ForkBlockEnv> BackendHandler<N, B> {
201 fn new(
202 provider: DynProvider<N>,
203 db: BlockchainDb<B>,
204 rx: UnboundedReceiver<BackendRequest<N>>,
205 block_id: Option<BlockId>,
206 ) -> Self {
207 Self {
208 provider,
209 db,
210 pending_requests: Default::default(),
211 account_requests: Default::default(),
212 storage_requests: Default::default(),
213 block_requests: Default::default(),
214 queued_requests: Default::default(),
215 incoming: rx,
216 block_id,
217 account_fetch_mode: Arc::new(AtomicU8::new(ACCOUNT_FETCH_UNCHECKED)),
218 }
219 }
220
221 fn on_request(&mut self, req: BackendRequest<N>) {
228 match req {
229 BackendRequest::Basic(addr, sender) => {
230 trace!(target: "backendhandler", "received request basic address={:?}", addr);
231 let acc = self.db.accounts().read().get(&addr).cloned();
232 if let Some(basic) = acc {
233 let _ = sender.send(Ok(basic));
234 } else {
235 self.request_account(addr, sender);
236 }
237 }
238 BackendRequest::BlockHash(number, sender) => {
239 let hash = self.db.block_hashes().read().get(&U256::from(number)).copied();
240 if let Some(hash) = hash {
241 let _ = sender.send(Ok(hash));
242 } else {
243 self.request_hash(number, sender);
244 }
245 }
246 BackendRequest::FullBlock(number, sender) => {
247 self.request_full_block(number, sender);
248 }
249 BackendRequest::Transaction(tx, sender) => {
250 self.request_transaction(tx, sender);
251 }
252 BackendRequest::Storage(addr, idx, sender) => {
253 let value =
255 self.db.storage().read().get(&addr).and_then(|acc| acc.get(&idx).copied());
256 if let Some(value) = value {
257 let _ = sender.send(Ok(value));
258 } else {
259 self.request_account_storage(addr, idx, sender);
261 }
262 }
263 BackendRequest::SetPinnedBlock(block_id) => {
264 self.block_id = Some(block_id);
265 }
266 BackendRequest::UpdateAddress(address_data) => {
267 for (address, data) in address_data {
268 self.db.accounts().write().insert(address, data);
269 }
270 }
271 BackendRequest::UpdateStorage(storage_data) => {
272 for (address, data) in storage_data {
273 self.db.storage().write().insert(address, data);
274 }
275 }
276 BackendRequest::UpdateBlockHash(block_hash_data) => {
277 for (block, hash) in block_hash_data {
278 self.db.block_hashes().write().insert(block, hash);
279 }
280 }
281 BackendRequest::AnyRequest(fut) => {
282 self.pending_requests.push(ProviderRequest::AnyRequest(fut));
283 }
284 }
285 }
286
287 fn request_account_storage(&mut self, address: Address, idx: U256, listener: StorageSender) {
289 match self.storage_requests.entry((address, idx)) {
290 Entry::Occupied(mut entry) => {
291 entry.get_mut().push(listener);
292 }
293 Entry::Vacant(entry) => {
294 trace!(target: "backendhandler", %address, %idx, "preparing storage request");
295 entry.insert(vec![listener]);
296 let provider = self.provider.clone();
297 let block_id = self.block_id.unwrap_or_default();
298 let fut = Box::pin(async move {
299 let storage = provider
300 .get_storage_at(address, idx)
301 .block_id(block_id)
302 .await
303 .map_err(Into::into);
304 (storage, address, idx)
305 });
306 self.pending_requests.push(ProviderRequest::Storage(fut));
307 }
308 }
309 }
310
311 fn get_account_req(&self, address: Address) -> ProviderRequest<eyre::Report, N> {
313 trace!(target: "backendhandler", "preparing account request, address={:?}", address);
314
315 let provider = self.provider.clone();
316 let block_id = self.block_id.unwrap_or_default();
317 let mode = Arc::clone(&self.account_fetch_mode);
318 let fut = async move {
319 let initial_mode = mode.load(Ordering::Relaxed);
321 match initial_mode {
322 ACCOUNT_FETCH_UNCHECKED => {
323 let acc_info_fut =
325 provider.get_account_info(address).block_id(block_id).into_future();
326
327 let balance_fut =
329 provider.get_balance(address).block_id(block_id).into_future();
330 let nonce_fut =
331 provider.get_transaction_count(address).block_id(block_id).into_future();
332 let code_fut = provider.get_code_at(address).block_id(block_id).into_future();
333 let triple_fut = futures::future::try_join3(balance_fut, nonce_fut, code_fut);
334 pin_mut!(acc_info_fut, triple_fut);
335
336 select! {
337 acc_info = &mut acc_info_fut => {
338 match acc_info {
339 Ok(info) => {
340 trace!(target: "backendhandler", "endpoint supports eth_getAccountInfo");
341 mode.store(ACCOUNT_FETCH_SUPPORTS_ACC_INFO, Ordering::Relaxed);
342 Ok((info.balance, info.nonce, info.code))
343 }
344 Err(err) => {
345 trace!(target: "backendhandler", ?err, "failed initial eth_getAccountInfo call");
346 mode.store(ACCOUNT_FETCH_SEPARATE_REQUESTS, Ordering::Relaxed);
347 Ok(triple_fut.await?)
348 }
349 }
350 }
351 triple = &mut triple_fut => {
352 match triple {
353 Ok((balance, nonce, code)) => {
354 mode.store(ACCOUNT_FETCH_SEPARATE_REQUESTS, Ordering::Relaxed);
355 Ok((balance, nonce, code))
356 }
357 Err(err) => Err(err.into())
358 }
359 }
360 }
361 }
362
363 ACCOUNT_FETCH_SUPPORTS_ACC_INFO => {
364 let mut res = provider
365 .get_account_info(address)
366 .block_id(block_id)
367 .into_future()
368 .await
369 .map(|info| (info.balance, info.nonce, info.code));
370
371 if res.is_err() {
374 mode.store(ACCOUNT_FETCH_SEPARATE_REQUESTS, Ordering::Relaxed);
375
376 let balance_fut =
377 provider.get_balance(address).block_id(block_id).into_future();
378 let nonce_fut = provider
379 .get_transaction_count(address)
380 .block_id(block_id)
381 .into_future();
382 let code_fut =
383 provider.get_code_at(address).block_id(block_id).into_future();
384 res = futures::future::try_join3(balance_fut, nonce_fut, code_fut).await;
385 }
386
387 Ok(res?)
388 }
389
390 ACCOUNT_FETCH_SEPARATE_REQUESTS => {
391 let balance_fut =
392 provider.get_balance(address).block_id(block_id).into_future();
393 let nonce_fut =
394 provider.get_transaction_count(address).block_id(block_id).into_future();
395 let code_fut = provider.get_code_at(address).block_id(block_id).into_future();
396
397 Ok(futures::future::try_join3(balance_fut, nonce_fut, code_fut).await?)
398 }
399
400 _ => unreachable!("Invalid account fetch mode"),
401 }
402 };
403
404 ProviderRequest::Account(Box::pin(async move {
405 let result = fut.await;
406 (result, address)
407 }))
408 }
409
410 fn request_account(&mut self, address: Address, listener: AccountInfoSender) {
412 match self.account_requests.entry(address) {
413 Entry::Occupied(mut entry) => {
414 entry.get_mut().push(listener);
415 }
416 Entry::Vacant(entry) => {
417 entry.insert(vec![listener]);
418 self.pending_requests.push(self.get_account_req(address));
419 }
420 }
421 }
422
423 fn request_full_block(&mut self, number: BlockId, sender: FullBlockSender<N>) {
425 let provider = self.provider.clone();
426 let fut = Box::pin(async move {
427 let block = provider
428 .get_block(number)
429 .full()
430 .await
431 .wrap_err(format!("could not fetch block {number:?}"));
432 (sender, block, number)
433 });
434
435 self.pending_requests.push(ProviderRequest::FullBlock(fut));
436 }
437
438 fn request_transaction(&mut self, tx: B256, sender: TransactionSender<N>) {
440 let provider = self.provider.clone();
441 let fut = Box::pin(async move {
442 let block = provider
443 .get_transaction_by_hash(tx)
444 .await
445 .wrap_err_with(|| format!("could not get transaction {tx}"))
446 .and_then(|maybe| {
447 maybe.ok_or_else(|| eyre::eyre!("could not get transaction {tx}"))
448 });
449 (sender, block, tx)
450 });
451
452 self.pending_requests.push(ProviderRequest::Transaction(fut));
453 }
454
455 fn request_hash(&mut self, number: u64, listener: BlockHashSender) {
457 match self.block_requests.entry(number) {
458 Entry::Occupied(mut entry) => {
459 entry.get_mut().push(listener);
460 }
461 Entry::Vacant(entry) => {
462 trace!(target: "backendhandler", number, "preparing block hash request");
463 entry.insert(vec![listener]);
464 let provider = self.provider.clone();
465 let fut = Box::pin(async move {
466 let block = provider
467 .get_block_by_number(number.into())
468 .hashes()
469 .await
470 .wrap_err("failed to get block");
471
472 let block_hash = match block {
473 Ok(Some(block)) => Ok(block.header().hash()),
474 Ok(None) => {
475 warn!(target: "backendhandler", ?number, "block not found");
476 Ok(KECCAK_EMPTY)
479 }
480 Err(err) => {
481 error!(target: "backendhandler", %err, ?number, "failed to get block");
482 Err(err)
483 }
484 };
485 (block_hash, number)
486 });
487 self.pending_requests.push(ProviderRequest::BlockHash(fut));
488 }
489 }
490 }
491}
492
493impl<N: Network, B: ForkBlockEnv> Future for BackendHandler<N, B> {
494 type Output = ();
495
496 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
497 let pin = self.get_mut();
498 loop {
499 while let Some(req) = pin.queued_requests.pop_front() {
501 pin.on_request(req)
502 }
503
504 loop {
506 match Pin::new(&mut pin.incoming).poll_next(cx) {
507 Poll::Ready(Some(req)) => {
508 pin.queued_requests.push_back(req);
509 }
510 Poll::Ready(None) => {
511 trace!(target: "backendhandler", "last sender dropped, ready to drop (&flush cache)");
512 return Poll::Ready(());
513 }
514 Poll::Pending => break,
515 }
516 }
517
518 for n in (0..pin.pending_requests.len()).rev() {
520 let mut request = pin.pending_requests.swap_remove(n);
521 match &mut request {
522 ProviderRequest::Account(fut) => {
523 if let Poll::Ready((resp, addr)) = fut.poll_unpin(cx) {
524 let (balance, nonce, code) = match resp {
526 Ok(res) => res,
527 Err(err) => {
528 let err = Arc::new(err);
529 if let Some(listeners) = pin.account_requests.remove(&addr) {
530 for l in listeners {
531 let _ = l.send(Err(DatabaseError::GetAccount(
532 addr,
533 Arc::clone(&err),
534 )));
535 }
536 }
537 continue;
538 }
539 };
540
541 let (code, code_hash) = if code.is_empty() {
543 (Bytes::default(), KECCAK_EMPTY)
544 } else {
545 (code.clone(), keccak256(&code))
546 };
547
548 let acc = AccountInfo {
550 nonce,
551 balance,
552 code: Some(Bytecode::new_raw(code)),
553 code_hash,
554 account_id: None,
555 };
556 pin.db.accounts().write().insert(addr, acc.clone());
557
558 if let Some(listeners) = pin.account_requests.remove(&addr) {
560 for l in listeners {
561 let _ = l.send(Ok(acc.clone()));
562 }
563 }
564 continue;
565 }
566 }
567 ProviderRequest::Storage(fut) => {
568 if let Poll::Ready((resp, addr, idx)) = fut.poll_unpin(cx) {
569 let value = match resp {
570 Ok(value) => value,
571 Err(err) => {
572 let err = Arc::new(err);
574 if let Some(listeners) =
575 pin.storage_requests.remove(&(addr, idx))
576 {
577 for l in listeners {
578 let _ = l.send(Err(DatabaseError::GetStorage(
579 addr,
580 idx,
581 Arc::clone(&err),
582 )));
583 }
584 }
585 continue;
586 }
587 };
588
589 pin.db.storage().write().entry(addr).or_default().insert(idx, value);
591
592 if let Some(listeners) = pin.storage_requests.remove(&(addr, idx)) {
594 for l in listeners {
595 let _ = l.send(Ok(value));
596 }
597 }
598 continue;
599 }
600 }
601 ProviderRequest::BlockHash(fut) => {
602 if let Poll::Ready((block_hash, number)) = fut.poll_unpin(cx) {
603 let value = match block_hash {
604 Ok(value) => value,
605 Err(err) => {
606 let err = Arc::new(err);
607 if let Some(listeners) = pin.block_requests.remove(&number) {
609 for l in listeners {
610 let _ = l.send(Err(DatabaseError::GetBlockHash(
611 number,
612 Arc::clone(&err),
613 )));
614 }
615 }
616 continue;
617 }
618 };
619
620 pin.db.block_hashes().write().insert(U256::from(number), value);
622
623 if let Some(listeners) = pin.block_requests.remove(&number) {
625 for l in listeners {
626 let _ = l.send(Ok(value));
627 }
628 }
629 continue;
630 }
631 }
632 ProviderRequest::FullBlock(fut) => {
633 if let Poll::Ready((sender, resp, number)) = fut.poll_unpin(cx) {
634 let msg = match resp {
635 Ok(Some(block)) => Ok(block),
636 Ok(None) => Err(DatabaseError::BlockNotFound(number)),
637 Err(err) => {
638 let err = Arc::new(err);
639 Err(DatabaseError::GetFullBlock(number, err))
640 }
641 };
642 let _ = sender.send(msg);
643 continue;
644 }
645 }
646 ProviderRequest::Transaction(fut) => {
647 if let Poll::Ready((sender, tx, tx_hash)) = fut.poll_unpin(cx) {
648 let msg = match tx {
649 Ok(tx) => Ok(tx),
650 Err(err) => {
651 let err = Arc::new(err);
652 Err(DatabaseError::GetTransaction(tx_hash, err))
653 }
654 };
655 let _ = sender.send(msg);
656 continue;
657 }
658 }
659 ProviderRequest::AnyRequest(fut) => {
660 if fut.poll_inner(cx).is_ready() {
661 continue;
662 }
663 }
664 }
665 pin.pending_requests.push(request);
667 }
668
669 if pin.queued_requests.is_empty() {
672 return Poll::Pending;
673 }
674 }
675 }
676}
677
678#[derive(Default, Clone, Debug, PartialEq, Eq)]
681pub enum BlockingMode {
682 #[default]
686 BlockInPlace,
687 Block,
691}
692
693impl BlockingMode {
694 pub fn run<F, R>(&self, f: F) -> R
696 where
697 F: FnOnce() -> R,
698 {
699 match self {
700 Self::BlockInPlace => tokio::task::block_in_place(f),
701 Self::Block => f(),
702 }
703 }
704}
705
706#[derive(Clone, Debug)]
735pub struct SharedBackend<N: Network = AnyNetwork, B: Serialize + Clone = BlockEnv> {
736 backend: UnboundedSender<BackendRequest<N>>,
738 cache: Arc<FlushJsonBlockCacheDB<B>>,
743
744 blocking_mode: BlockingMode,
746}
747
748impl<N: Network, B: ForkBlockEnv> SharedBackend<N, B> {
749 pub async fn spawn_backend<P: Provider<N> + 'static>(
755 provider: P,
756 db: BlockchainDb<B>,
757 pin_block: Option<BlockId>,
758 ) -> Self {
759 let (shared, handler) = Self::new(provider, db, pin_block);
760 trace!(target: "backendhandler", "spawning Backendhandler task");
762 tokio::spawn(handler);
763 shared
764 }
765
766 pub fn spawn_backend_thread<P: Provider<N> + 'static>(
769 provider: P,
770 db: BlockchainDb<B>,
771 pin_block: Option<BlockId>,
772 ) -> Self {
773 let (shared, handler) = Self::new(provider, db, pin_block);
774
775 std::thread::Builder::new()
778 .name("fork-backend".into())
779 .spawn(move || {
780 let rt = tokio::runtime::Builder::new_current_thread()
781 .enable_all()
782 .build()
783 .expect("failed to build tokio runtime");
784
785 rt.block_on(handler);
786 })
787 .expect("failed to spawn thread");
788 trace!(target: "backendhandler", "spawned Backendhandler thread");
789
790 shared
791 }
792
793 pub fn new<P: Provider<N> + 'static>(
795 provider: P,
796 db: BlockchainDb<B>,
797 pin_block: Option<BlockId>,
798 ) -> (Self, BackendHandler<N, B>) {
799 let (backend, backend_rx) = unbounded();
800 let cache = Arc::new(FlushJsonBlockCacheDB(Arc::clone(db.cache())));
801 let handler = BackendHandler::new(provider.erased(), db, backend_rx, pin_block);
802 (Self { backend, cache, blocking_mode: Default::default() }, handler)
803 }
804
805 pub fn with_blocking_mode(&self, mode: BlockingMode) -> Self {
807 Self { backend: self.backend.clone(), cache: self.cache.clone(), blocking_mode: mode }
808 }
809
810 pub fn set_pinned_block(&self, block: impl Into<BlockId>) -> eyre::Result<()> {
812 let req = BackendRequest::SetPinnedBlock(block.into());
813 self.backend.unbounded_send(req).map_err(|e| eyre::eyre!("{:?}", e))
814 }
815
816 pub fn get_full_block(&self, block: impl Into<BlockId>) -> DatabaseResult<N::BlockResponse> {
818 self.blocking_mode.run(|| {
819 let (sender, rx) = oneshot_channel();
820 let req = BackendRequest::FullBlock(block.into(), sender);
821 self.backend.unbounded_send(req)?;
822 rx.recv()?
823 })
824 }
825
826 pub fn get_transaction(&self, tx: B256) -> DatabaseResult<N::TransactionResponse> {
828 self.blocking_mode.run(|| {
829 let (sender, rx) = oneshot_channel();
830 let req = BackendRequest::Transaction(tx, sender);
831 self.backend.unbounded_send(req)?;
832 rx.recv()?
833 })
834 }
835
836 fn do_get_basic(&self, address: Address) -> DatabaseResult<Option<AccountInfo>> {
837 self.blocking_mode.run(|| {
838 let (sender, rx) = oneshot_channel();
839 let req = BackendRequest::Basic(address, sender);
840 self.backend.unbounded_send(req)?;
841 rx.recv()?.map(Some)
842 })
843 }
844
845 fn do_get_storage(&self, address: Address, index: U256) -> DatabaseResult<U256> {
846 self.blocking_mode.run(|| {
847 let (sender, rx) = oneshot_channel();
848 let req = BackendRequest::Storage(address, index, sender);
849 self.backend.unbounded_send(req)?;
850 rx.recv()?
851 })
852 }
853
854 fn do_get_block_hash(&self, number: u64) -> DatabaseResult<B256> {
855 self.blocking_mode.run(|| {
856 let (sender, rx) = oneshot_channel();
857 let req = BackendRequest::BlockHash(number, sender);
858 self.backend.unbounded_send(req)?;
859 rx.recv()?
860 })
861 }
862
863 pub fn insert_or_update_address(&self, address_data: AddressData) {
865 let req = BackendRequest::UpdateAddress(address_data);
866 let err = self.backend.unbounded_send(req);
867 match err {
868 Ok(_) => (),
869 Err(e) => {
870 error!(target: "sharedbackend", "Failed to send update address request: {:?}", e)
871 }
872 }
873 }
874
875 pub fn insert_or_update_storage(&self, storage_data: StorageData) {
877 let req = BackendRequest::UpdateStorage(storage_data);
878 let err = self.backend.unbounded_send(req);
879 match err {
880 Ok(_) => (),
881 Err(e) => {
882 error!(target: "sharedbackend", "Failed to send update address request: {:?}", e)
883 }
884 }
885 }
886
887 pub fn insert_or_update_block_hashes(&self, block_hash_data: BlockHashData) {
889 let req = BackendRequest::UpdateBlockHash(block_hash_data);
890 let err = self.backend.unbounded_send(req);
891 match err {
892 Ok(_) => (),
893 Err(e) => {
894 error!(target: "sharedbackend", "Failed to send update address request: {:?}", e)
895 }
896 }
897 }
898
899 pub fn do_any_request<T, F>(&mut self, fut: F) -> DatabaseResult<T>
901 where
902 F: Future<Output = Result<T, eyre::Report>> + Send + 'static,
903 T: fmt::Debug + Send + 'static,
904 {
905 self.blocking_mode.run(|| {
906 let (sender, rx) = oneshot_channel::<Result<T, eyre::Report>>();
907 let req = BackendRequest::AnyRequest(Box::new(AnyRequestFuture {
908 sender,
909 future: Box::pin(fut),
910 }));
911 self.backend.unbounded_send(req)?;
912 rx.recv()?.map_err(|err| DatabaseError::AnyRequest(Arc::new(err)))
913 })
914 }
915
916 pub fn flush_cache(&self) {
918 self.cache.0.flush();
919 }
920
921 pub fn flush_cache_to(&self, cache_path: &Path) {
923 self.cache.0.flush_to(cache_path);
924 }
925
926 pub fn data(&self) -> Arc<MemDb> {
928 self.cache.0.db().clone()
929 }
930
931 pub fn accounts(&self) -> AddressData {
933 self.cache.0.db().accounts.read().clone()
934 }
935
936 pub fn accounts_len(&self) -> usize {
938 self.cache.0.db().accounts.read().len()
939 }
940
941 pub fn storage(&self) -> StorageData {
943 self.cache.0.db().storage.read().clone()
944 }
945
946 pub fn storage_len(&self) -> usize {
948 self.cache.0.db().storage.read().len()
949 }
950
951 pub fn block_hashes(&self) -> BlockHashData {
953 self.cache.0.db().block_hashes.read().clone()
954 }
955
956 pub fn block_hashes_len(&self) -> usize {
958 self.cache.0.db().block_hashes.read().len()
959 }
960}
961
962impl<N: Network, B: ForkBlockEnv> DatabaseRef for SharedBackend<N, B> {
963 type Error = DatabaseError;
964
965 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
966 trace!(target: "sharedbackend", %address, "request basic");
967 self.do_get_basic(address).inspect_err(|err| {
968 error!(target: "sharedbackend", %err, %address, "Failed to send/recv `basic`");
969 if err.is_possibly_non_archive_node_error() {
970 error!(target: "sharedbackend", "{NON_ARCHIVE_NODE_WARNING}");
971 }
972 })
973 }
974
975 fn code_by_hash_ref(&self, hash: B256) -> Result<Bytecode, Self::Error> {
976 Err(DatabaseError::MissingCode(hash))
977 }
978
979 fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
980 trace!(target: "sharedbackend", "request storage {:?} at {:?}", address, index);
981 self.do_get_storage(address, index).inspect_err(|err| {
982 error!(target: "sharedbackend", %err, %address, %index, "Failed to send/recv `storage`");
983 if err.is_possibly_non_archive_node_error() {
984 error!(target: "sharedbackend", "{NON_ARCHIVE_NODE_WARNING}");
985 }
986 })
987 }
988
989 fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
990 trace!(target: "sharedbackend", "request block hash for number {:?}", number);
991 self.do_get_block_hash(number).inspect_err(|err| {
992 error!(target: "sharedbackend", %err, %number, "Failed to send/recv `block_hash`");
993 if err.is_possibly_non_archive_node_error() {
994 error!(target: "sharedbackend", "{NON_ARCHIVE_NODE_WARNING}");
995 }
996 })
997 }
998}
999
1000#[cfg(test)]
1001mod tests {
1002 use super::*;
1003 use crate::cache::{BlockchainDbMeta, JsonBlockCacheDB};
1004 use alloy_consensus::BlockHeader;
1005 use alloy_provider::ProviderBuilder;
1006 use alloy_rpc_client::ClientBuilder;
1007 use serde::Deserialize;
1008 use std::{fs, path::PathBuf};
1009 use tiny_http::{Response, Server};
1010
1011 pub fn get_http_provider(endpoint: &str) -> impl Provider<AnyNetwork> + Clone + use<> {
1012 ProviderBuilder::new()
1013 .network::<AnyNetwork>()
1014 .connect_client(ClientBuilder::default().http(endpoint.parse().unwrap()))
1015 }
1016
1017 const ENDPOINT: Option<&str> = option_env!("ETH_RPC_URL");
1018
1019 #[tokio::test(flavor = "multi_thread")]
1020 async fn test_builder() {
1021 let Some(endpoint) = ENDPOINT else { return };
1022 let provider = get_http_provider(endpoint);
1023
1024 let any_rpc_block = provider.get_block(BlockId::latest()).hashes().await.unwrap().unwrap();
1025 let block_env =
1026 BlockEnv { number: U256::from(any_rpc_block.header.number()), ..Default::default() };
1027 let meta = BlockchainDbMeta::default().set_block_env(block_env);
1028
1029 assert_eq!(meta.block_env.number, U256::from(any_rpc_block.header.number()));
1030 }
1031
1032 #[tokio::test(flavor = "multi_thread")]
1033 async fn shared_backend() {
1034 let Some(endpoint) = ENDPOINT else { return };
1035
1036 let provider = get_http_provider(endpoint);
1037 let meta = BlockchainDbMeta::new(BlockEnv::default(), endpoint.to_string());
1038
1039 let db = BlockchainDb::new(meta, None);
1040 let backend = SharedBackend::spawn_backend(Arc::new(provider), db.clone(), None).await;
1041
1042 let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap();
1044
1045 let idx = U256::from(0u64);
1046 let value = backend.storage_ref(address, idx).unwrap();
1047 let account = backend.basic_ref(address).unwrap().unwrap();
1048
1049 let mem_acc = db.accounts().read().get(&address).unwrap().clone();
1050 assert_eq!(account.balance, mem_acc.balance);
1051 assert_eq!(account.nonce, mem_acc.nonce);
1052 let slots = db.storage().read().get(&address).unwrap().clone();
1053 assert_eq!(slots.len(), 1);
1054 assert_eq!(slots.get(&idx).copied().unwrap(), value);
1055
1056 let num = 10u64;
1057 let hash = backend.block_hash_ref(num).unwrap();
1058 let mem_hash = *db.block_hashes().read().get(&U256::from(num)).unwrap();
1059 assert_eq!(hash, mem_hash);
1060
1061 let max_slots = 5;
1062 let handle = std::thread::spawn(move || {
1063 for i in 1..max_slots {
1064 let idx = U256::from(i);
1065 let _ = backend.storage_ref(address, idx);
1066 }
1067 });
1068 handle.join().unwrap();
1069 let slots = db.storage().read().get(&address).unwrap().clone();
1070 assert_eq!(slots.len() as u64, max_slots);
1071 }
1072
1073 #[test]
1074 fn can_read_cache() {
1075 let cache_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/storage.json");
1076 let json = JsonBlockCacheDB::<BlockEnv>::load(cache_path).unwrap();
1077 assert!(!json.db().accounts.read().is_empty());
1078 }
1079
1080 #[tokio::test(flavor = "multi_thread")]
1081 async fn can_modify_address() {
1082 let Some(endpoint) = ENDPOINT else { return };
1083
1084 let provider = get_http_provider(endpoint);
1085 let meta = BlockchainDbMeta::new(BlockEnv::default(), endpoint.to_string());
1086
1087 let db = BlockchainDb::new(meta, None);
1088 let backend = SharedBackend::spawn_backend(Arc::new(provider), db.clone(), None).await;
1089
1090 let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap();
1092
1093 let new_acc = AccountInfo {
1094 nonce: 1000u64,
1095 balance: U256::from(2000),
1096 code: None,
1097 code_hash: KECCAK_EMPTY,
1098 account_id: None,
1099 };
1100 let expected_nonce = new_acc.nonce;
1101 let expected_balance = new_acc.balance;
1102 let mut account_data = AddressData::default();
1103 account_data.insert(address, new_acc);
1104
1105 backend.insert_or_update_address(account_data);
1106
1107 let max_slots = 5;
1108 let handle = std::thread::spawn(move || {
1109 for i in 1..max_slots {
1110 let idx = U256::from(i);
1111 let result_address = backend.basic_ref(address).unwrap();
1112 match result_address {
1113 Some(acc) => {
1114 assert_eq!(
1115 acc.nonce, expected_nonce,
1116 "The nonce was not changed in instance of index {idx}"
1117 );
1118 assert_eq!(
1119 acc.balance, expected_balance,
1120 "The balance was not changed in instance of index {idx}"
1121 );
1122
1123 let db_address = {
1125 let accounts = db.accounts().read();
1126 accounts.get(&address).unwrap().clone()
1127 };
1128
1129 assert_eq!(
1130 db_address.nonce, expected_nonce,
1131 "The nonce was not changed in instance of index {idx}"
1132 );
1133 assert_eq!(
1134 db_address.balance, expected_balance,
1135 "The balance was not changed in instance of index {idx}"
1136 );
1137 }
1138 None => panic!("Account not found"),
1139 }
1140 }
1141 });
1142 handle.join().unwrap();
1143 }
1144
1145 #[tokio::test(flavor = "multi_thread")]
1146 async fn can_modify_storage() {
1147 let Some(endpoint) = ENDPOINT else { return };
1148
1149 let provider = get_http_provider(endpoint);
1150 let meta = BlockchainDbMeta::new(BlockEnv::default(), endpoint.to_string());
1151
1152 let db = BlockchainDb::new(meta, None);
1153 let backend = SharedBackend::spawn_backend(Arc::new(provider), db.clone(), None).await;
1154
1155 let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap();
1157
1158 let mut storage_data = StorageData::default();
1159 let mut storage_info = StorageInfo::default();
1160 storage_info.insert(U256::from(20), U256::from(10));
1161 storage_info.insert(U256::from(30), U256::from(15));
1162 storage_info.insert(U256::from(40), U256::from(20));
1163
1164 storage_data.insert(address, storage_info);
1165
1166 backend.insert_or_update_storage(storage_data.clone());
1167
1168 let max_slots = 5;
1169 let handle = std::thread::spawn(move || {
1170 for _ in 1..max_slots {
1171 for (address, info) in &storage_data {
1172 for (index, value) in info {
1173 let result_storage = backend.do_get_storage(*address, *index);
1174 match result_storage {
1175 Ok(stg_db) => {
1176 assert_eq!(
1177 stg_db, *value,
1178 "Storage in slot number {index} in address {address} do not have the same value"
1179 );
1180
1181 let db_result = {
1182 let storage = db.storage().read();
1183 let address_storage = storage.get(address).unwrap();
1184 *address_storage.get(index).unwrap()
1185 };
1186
1187 assert_eq!(
1188 stg_db, db_result,
1189 "Storage in slot number {index} in address {address} do not have the same value"
1190 )
1191 }
1192
1193 Err(err) => {
1194 panic!("There was a database error: {err}")
1195 }
1196 }
1197 }
1198 }
1199 }
1200 });
1201 handle.join().unwrap();
1202 }
1203
1204 #[tokio::test(flavor = "multi_thread")]
1205 async fn can_modify_block_hashes() {
1206 let Some(endpoint) = ENDPOINT else { return };
1207
1208 let provider = get_http_provider(endpoint);
1209 let meta = BlockchainDbMeta::new(BlockEnv::default(), endpoint.to_string());
1210
1211 let db = BlockchainDb::new(meta, None);
1212 let backend = SharedBackend::spawn_backend(Arc::new(provider), db.clone(), None).await;
1213
1214 let mut block_hash_data = BlockHashData::default();
1218 block_hash_data.insert(U256::from(1), B256::from(U256::from(1)));
1219 block_hash_data.insert(U256::from(2), B256::from(U256::from(2)));
1220 block_hash_data.insert(U256::from(3), B256::from(U256::from(3)));
1221 block_hash_data.insert(U256::from(4), B256::from(U256::from(4)));
1222 block_hash_data.insert(U256::from(5), B256::from(U256::from(5)));
1223
1224 backend.insert_or_update_block_hashes(block_hash_data.clone());
1225
1226 let max_slots: u64 = 5;
1227 let handle = std::thread::spawn(move || {
1228 for i in 1..max_slots {
1229 let key = U256::from(i);
1230 let result_hash = backend.do_get_block_hash(i);
1231 match result_hash {
1232 Ok(hash) => {
1233 assert_eq!(
1234 hash,
1235 *block_hash_data.get(&key).unwrap(),
1236 "The hash in block {key} did not match"
1237 );
1238
1239 let db_result = {
1240 let hashes = db.block_hashes().read();
1241 *hashes.get(&key).unwrap()
1242 };
1243
1244 assert_eq!(hash, db_result, "The hash in block {key} did not match");
1245 }
1246 Err(err) => panic!("Hash not found, error: {err}"),
1247 }
1248 }
1249 });
1250 handle.join().unwrap();
1251 }
1252
1253 #[tokio::test(flavor = "multi_thread")]
1254 async fn can_modify_storage_with_cache() {
1255 let Some(endpoint) = ENDPOINT else { return };
1256
1257 let provider = get_http_provider(endpoint);
1258 let meta = BlockchainDbMeta::new(BlockEnv::default(), endpoint.to_string());
1259
1260 fs::copy("test-data/storage.json", "test-data/storage-tmp.json").unwrap();
1262
1263 let cache_path =
1264 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/storage-tmp.json");
1265
1266 let db = BlockchainDb::new(meta.clone(), Some(cache_path));
1267 let backend =
1268 SharedBackend::spawn_backend(Arc::new(provider.clone()), db.clone(), None).await;
1269
1270 let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap();
1272
1273 let mut storage_data = StorageData::default();
1274 let mut storage_info = StorageInfo::default();
1275 storage_info.insert(U256::from(1), U256::from(10));
1276 storage_info.insert(U256::from(2), U256::from(15));
1277 storage_info.insert(U256::from(3), U256::from(20));
1278 storage_info.insert(U256::from(4), U256::from(20));
1279 storage_info.insert(U256::from(5), U256::from(15));
1280 storage_info.insert(U256::from(6), U256::from(10));
1281
1282 let mut address_data = backend.basic_ref(address).unwrap().unwrap();
1283 address_data.code = None;
1284
1285 storage_data.insert(address, storage_info);
1286
1287 backend.insert_or_update_storage(storage_data.clone());
1288
1289 let mut new_acc = backend.basic_ref(address).unwrap().unwrap();
1290 new_acc.code = Some(Bytecode::new_raw(([10, 20, 30, 40]).into()));
1292
1293 let mut account_data = AddressData::default();
1294 account_data.insert(address, new_acc);
1295
1296 backend.insert_or_update_address(account_data);
1297
1298 let backend_clone = backend.clone();
1299
1300 let max_slots = 5;
1301 let handle = std::thread::spawn(move || {
1302 for _ in 1..max_slots {
1303 for (address, info) in &storage_data {
1304 for (index, value) in info {
1305 let result_storage = backend.do_get_storage(*address, *index);
1306 match result_storage {
1307 Ok(stg_db) => {
1308 assert_eq!(
1309 stg_db, *value,
1310 "Storage in slot number {index} in address {address} doesn't have the same value"
1311 );
1312
1313 let db_result = {
1314 let storage = db.storage().read();
1315 let address_storage = storage.get(address).unwrap();
1316 *address_storage.get(index).unwrap()
1317 };
1318
1319 assert_eq!(
1320 stg_db, db_result,
1321 "Storage in slot number {index} in address {address} doesn't have the same value"
1322 );
1323 }
1324
1325 Err(err) => {
1326 panic!("There was a database error: {err}")
1327 }
1328 }
1329 }
1330 }
1331 }
1332
1333 backend_clone.flush_cache();
1334 });
1335 handle.join().unwrap();
1336
1337 let cache_path =
1340 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/storage-tmp.json");
1341
1342 let json_db = BlockchainDb::new(meta, Some(cache_path));
1343
1344 let mut storage_data = StorageData::default();
1345 let mut storage_info = StorageInfo::default();
1346 storage_info.insert(U256::from(1), U256::from(10));
1347 storage_info.insert(U256::from(2), U256::from(15));
1348 storage_info.insert(U256::from(3), U256::from(20));
1349 storage_info.insert(U256::from(4), U256::from(20));
1350 storage_info.insert(U256::from(5), U256::from(15));
1351 storage_info.insert(U256::from(6), U256::from(10));
1352
1353 storage_data.insert(address, storage_info);
1354
1355 let max_slots = 5;
1357 let handle = std::thread::spawn(move || {
1358 for _ in 1..max_slots {
1359 for (address, info) in &storage_data {
1360 for (index, value) in info {
1361 let result_storage = {
1362 let storage = json_db.storage().read();
1363 let address_storage = storage.get(address).unwrap().clone();
1364 *address_storage.get(index).unwrap()
1365 };
1366
1367 assert_eq!(
1368 result_storage, *value,
1369 "Storage in slot number {index} in address {address} doesn't have the same value"
1370 );
1371 }
1372 }
1373 }
1374 });
1375
1376 handle.join().unwrap();
1377
1378 fs::remove_file("test-data/storage-tmp.json").unwrap();
1380 }
1381
1382 #[tokio::test(flavor = "multi_thread")]
1383 async fn shared_backend_any_request() {
1384 let expected_response_bytes: Bytes = vec![0xff, 0xee].into();
1385 let server = Server::http("127.0.0.1:0").expect("failed starting in-memory http server");
1386 let endpoint = format!("http://{}", server.server_addr());
1387
1388 let expected_bytes_inner = expected_response_bytes.clone();
1390 let server_handle = std::thread::spawn(move || {
1391 #[derive(Debug, Deserialize)]
1392 struct Request {
1393 method: String,
1394 }
1395 let mut request = server.recv().unwrap();
1396 let rpc_request: Request =
1397 serde_json::from_reader(request.as_reader()).expect("failed parsing request");
1398
1399 match rpc_request.method.as_str() {
1400 "foo_callCustomMethod" => request
1401 .respond(Response::from_string(format!(
1402 r#"{{"result": "{}"}}"#,
1403 alloy_primitives::hex::encode_prefixed(expected_bytes_inner),
1404 )))
1405 .unwrap(),
1406 _ => request
1407 .respond(Response::from_string(r#"{"error": "invalid request"}"#))
1408 .unwrap(),
1409 };
1410 });
1411
1412 let provider = get_http_provider(&endpoint);
1413 let meta = BlockchainDbMeta::new(BlockEnv::default(), endpoint.clone());
1414
1415 let db = BlockchainDb::new(meta, None);
1416 let provider_inner = provider.clone();
1417 let mut backend = SharedBackend::spawn_backend(Arc::new(provider), db.clone(), None).await;
1418
1419 let actual_response_bytes = backend
1420 .do_any_request(async move {
1421 let bytes: alloy_primitives::Bytes =
1422 provider_inner.raw_request("foo_callCustomMethod".into(), vec!["0001"]).await?;
1423 Ok(bytes)
1424 })
1425 .expect("failed performing any request");
1426
1427 assert_eq!(actual_response_bytes, expected_response_bytes);
1428
1429 server_handle.join().unwrap();
1430 }
1431}