Skip to main content

foundry_fork_db/
backend.rs

1//! Smart caching and deduplication of requests when using a forking provider.
2
3use 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
44/// Logged when an error is indicative that the user is trying to fork from a non-archive node.
45pub 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
49// Various future/request type aliases
50
51type 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
89/// States for tracking which account endpoints should be used when account info
90const ACCOUNT_FETCH_UNCHECKED: u8 = 0;
91/// Endpoints supports the non standard eth_getAccountInfo which is more efficient than sending 3
92/// separate requests
93const ACCOUNT_FETCH_SUPPORTS_ACC_INFO: u8 = 1;
94/// Use regular individual getCode, getNonce, getBalance calls
95const 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
112/// @dev Implements `WrappedAnyRequest` for `AnyRequestFuture`.
113///
114/// - `poll_inner` is similar to `Future` polling but intentionally consumes the Future<Output=T>
115///   and return Future<Output=()>
116/// - This design avoids storing `Future<Output = T>` directly, as its type may not be known at
117///   compile time.
118/// - Instead, the result (`Result<T, Err>`) is sent via the `sender` channel, which enforces type
119///   safety.
120impl<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
136/// Request variants that are executed by the provider
137enum 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/// The Request type the Backend listens for
147#[derive(Debug)]
148enum BackendRequest<N: Network = AnyNetwork> {
149    /// Fetch the account info
150    Basic(Address, AccountInfoSender),
151    /// Fetch a storage slot
152    Storage(Address, U256, StorageSender),
153    /// Fetch a block hash
154    BlockHash(u64, BlockHashSender),
155    /// Fetch an entire block with transactions
156    FullBlock(BlockId, FullBlockSender<N>),
157    /// Fetch a transaction
158    Transaction(B256, TransactionSender<N>),
159    /// Sets the pinned block to fetch data from
160    SetPinnedBlock(BlockId),
161
162    /// Update Address data
163    UpdateAddress(AddressData),
164    /// Update Storage data
165    UpdateStorage(StorageData),
166    /// Update Block Hashes
167    UpdateBlockHash(BlockHashData),
168    /// Any other request
169    AnyRequest(Box<dyn WrappedAnyRequest>),
170}
171
172/// Handles an internal provider and listens for requests.
173///
174/// This handler will remain active as long as it is reachable (request channel still open) and
175/// requests are in progress.
176#[must_use = "futures do nothing unless polled"]
177pub struct BackendHandler<N: Network = AnyNetwork, B = BlockEnv> {
178    provider: DynProvider<N>,
179    /// Stores all the data.
180    db: BlockchainDb<B>,
181    /// Requests currently in progress
182    pending_requests: Vec<ProviderRequest<eyre::Report, N>>,
183    /// Listeners that wait for a `get_account` related response
184    account_requests: HashMap<Address, Vec<AccountInfoSender>>,
185    /// Listeners that wait for a `get_storage_at` response
186    storage_requests: HashMap<(Address, U256), Vec<StorageSender>>,
187    /// Listeners that wait for a `get_block` response
188    block_requests: HashMap<u64, Vec<BlockHashSender>>,
189    /// Incoming commands.
190    incoming: UnboundedReceiver<BackendRequest<N>>,
191    /// unprocessed queued requests
192    queued_requests: VecDeque<BackendRequest<N>>,
193    /// The block to fetch data from.
194    // This is an `Option` so that we can have less code churn in the functions below
195    block_id: Option<BlockId>,
196    /// The mode for fetching account data
197    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    /// handle the request in queue in the future.
222    ///
223    /// We always check:
224    ///  1. if the requested value is already stored in the cache, then answer the sender
225    ///  2. otherwise, fetch it via the provider but check if a request for that value is already in
226    ///     progress (e.g. another Sender just requested the same account)
227    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                // account is already stored in the cache
254                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                    // account present but not storage -> fetch storage
260                    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    /// process a request for account's storage
288    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    /// returns the future that fetches the account data
312    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            // depending on the tracked mode we can dispatch requests.
320            let initial_mode = mode.load(Ordering::Relaxed);
321            match initial_mode {
322                ACCOUNT_FETCH_UNCHECKED => {
323                    // single request for accountinfo object
324                    let acc_info_fut =
325                        provider.get_account_info(address).block_id(block_id).into_future();
326
327                    // tri request for account info
328                    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                    // it's possible that the configured endpoint load balances requests to multiple
372                    // instances and not all support that endpoint so we should reset here
373                    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    /// process a request for an account
411    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    /// process a request for an entire block
424    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    /// process a request for a transactions
439    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    /// process a request for a block hash
456    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                            // if no block was returned then the block does not exist, in which case
477                            // we return empty hash
478                            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            // Drain queued requests first.
500            while let Some(req) = pin.queued_requests.pop_front() {
501                pin.on_request(req)
502            }
503
504            // receive new requests to delegate to the underlying provider
505            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            // poll all requests in progress
519            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                            // get the response
525                            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                            // convert it to revm-style types
542                            let (code, code_hash) = if code.is_empty() {
543                                (Bytes::default(), KECCAK_EMPTY)
544                            } else {
545                                (code.clone(), keccak256(&code))
546                            };
547
548                            // update the cache
549                            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                            // notify all listeners
559                            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                                    // notify all listeners
573                                    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                            // update the cache
590                            pin.db.storage().write().entry(addr).or_default().insert(idx, value);
591
592                            // notify all listeners
593                            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                                    // notify all listeners
608                                    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                            // update the cache
621                            pin.db.block_hashes().write().insert(U256::from(number), value);
622
623                            // notify all listeners
624                            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                // not ready, insert and poll again
666                pin.pending_requests.push(request);
667            }
668
669            // If no new requests have been queued, break to
670            // be polled again later.
671            if pin.queued_requests.is_empty() {
672                return Poll::Pending;
673            }
674        }
675    }
676}
677
678/// Mode for the `SharedBackend` how to block in the non-async [`DatabaseRef`] when interacting with
679/// [`BackendHandler`].
680#[derive(Default, Clone, Debug, PartialEq, Eq)]
681pub enum BlockingMode {
682    /// This mode use `tokio::task::block_in_place()` to block in place.
683    ///
684    /// This should be used when blocking on the call site is disallowed.
685    #[default]
686    BlockInPlace,
687    /// The mode blocks the current task
688    ///
689    /// This can be used if blocking on the call site is allowed, e.g. on a tokio blocking task.
690    Block,
691}
692
693impl BlockingMode {
694    /// run process logic with the blocking mode
695    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/// A cloneable backend type that shares access to the backend data with all its clones.
707///
708/// This backend type is connected to the `BackendHandler` via a mpsc unbounded channel. The
709/// `BackendHandler` is spawned on a tokio task and listens for incoming commands on the receiver
710/// half of the channel. A `SharedBackend` holds a sender for that channel, which is `Clone`, so
711/// there can be multiple `SharedBackend`s communicating with the same `BackendHandler`, hence this
712/// `Backend` type is thread safe.
713///
714/// All `Backend` trait functions are delegated as a `BackendRequest` via the channel to the
715/// `BackendHandler`. All `BackendRequest` variants include a sender half of an additional channel
716/// that is used by the `BackendHandler` to send the result of an executed `BackendRequest` back to
717/// `SharedBackend`.
718///
719/// The `BackendHandler` holds a `Provider` to look up missing accounts or storage slots
720/// from remote (e.g. infura). It detects duplicate requests from multiple `SharedBackend`s and
721/// bundles them together, so that always only one provider request is executed. For example, there
722/// are two `SharedBackend`s, `A` and `B`, both request the basic account info of account
723/// `0xasd9sa7d...` at the same time. After the `BackendHandler` receives the request from `A`, it
724/// sends a new provider request to the provider's endpoint, then it reads the identical request
725/// from `B` and simply adds it as an additional listener for the request already in progress,
726/// instead of sending another one. So that after the provider returns the response all listeners
727/// (`A` and `B`) get notified.
728// **Note**: the implementation makes use of [tokio::task::block_in_place()] when interacting with
729// the underlying [BackendHandler] which runs on a separate spawned tokio task.
730// [tokio::task::block_in_place()]
731// > Runs the provided blocking function on the current thread without blocking the executor.
732// This prevents issues (hangs) we ran into were the [SharedBackend] itself is called from a spawned
733// task.
734#[derive(Clone, Debug)]
735pub struct SharedBackend<N: Network = AnyNetwork, B: Serialize + Clone = BlockEnv> {
736    /// channel used for sending commands related to database operations
737    backend: UnboundedSender<BackendRequest<N>>,
738    /// Ensures that the underlying cache gets flushed once the last `SharedBackend` is dropped.
739    ///
740    /// There is only one instance of the type, so as soon as the last `SharedBackend` is deleted,
741    /// `FlushJsonBlockCacheDB<B>` is also deleted and the cache is flushed.
742    cache: Arc<FlushJsonBlockCacheDB<B>>,
743
744    /// The mode for the `SharedBackend` to block in place or not
745    blocking_mode: BlockingMode,
746}
747
748impl<N: Network, B: ForkBlockEnv> SharedBackend<N, B> {
749    /// _Spawns_ a new `BackendHandler` on a `tokio::task` that listens for requests from any
750    /// `SharedBackend`. Missing values get inserted in the `db`.
751    ///
752    /// The spawned `BackendHandler` finishes once the last `SharedBackend` connected to it is
753    /// dropped.
754    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        // spawn the provider handler to a task
761        trace!(target: "backendhandler", "spawning Backendhandler task");
762        tokio::spawn(handler);
763        shared
764    }
765
766    /// Same as `Self::spawn_backend` but spawns the `BackendHandler` on a separate `std::thread` in
767    /// its own `tokio::Runtime`
768    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        // spawn a light-weight thread with a thread-local async runtime just for
776        // sending and receiving data from the remote client
777        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    /// Returns a new `SharedBackend` and the `BackendHandler`
794    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    /// Returns a new `SharedBackend` and the `BackendHandler` with a specific blocking mode
806    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    /// Updates the pinned block to fetch data from
811    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    /// Returns the full block for the given block identifier
817    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    /// Returns the transaction for the hash
827    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    /// Inserts or updates data for multiple addresses
864    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    /// Inserts or updates data for multiple storage slots
876    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    /// Inserts or updates data for multiple block hashes
888    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    /// Returns any arbitrary request on the provider
900    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    /// Flushes the DB to disk if caching is enabled
917    pub fn flush_cache(&self) {
918        self.cache.0.flush();
919    }
920
921    /// Flushes the DB to a specific file
922    pub fn flush_cache_to(&self, cache_path: &Path) {
923        self.cache.0.flush_to(cache_path);
924    }
925
926    /// Returns the DB
927    pub fn data(&self) -> Arc<MemDb> {
928        self.cache.0.db().clone()
929    }
930
931    /// Returns the DB accounts
932    pub fn accounts(&self) -> AddressData {
933        self.cache.0.db().accounts.read().clone()
934    }
935
936    /// Returns the DB accounts length
937    pub fn accounts_len(&self) -> usize {
938        self.cache.0.db().accounts.read().len()
939    }
940
941    /// Returns the DB storage
942    pub fn storage(&self) -> StorageData {
943        self.cache.0.db().storage.read().clone()
944    }
945
946    /// Returns the DB storage length
947    pub fn storage_len(&self) -> usize {
948        self.cache.0.db().storage.read().len()
949    }
950
951    /// Returns the DB block_hashes
952    pub fn block_hashes(&self) -> BlockHashData {
953        self.cache.0.db().block_hashes.read().clone()
954    }
955
956    /// Returns the DB block_hashes length
957    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        // some rng contract from etherscan
1043        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        // some rng contract from etherscan
1091        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                        // comparing with db
1124                        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        // some rng contract from etherscan
1156        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        // some rng contract from etherscan
1215        // let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap();
1216
1217        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        // create a temporary file
1261        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        // some rng contract from etherscan
1271        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        // nullify the code
1291        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        // read json and confirm the changes to the data
1338
1339        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        // redo the checks with the data extracted from the json file
1356        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        // erase the temporary file
1379        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        // Spin an in-memory server that responds to "foo_callCustomMethod" rpc call.
1389        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}