Skip to main content

anvil/eth/backend/
fork.rs

1//! Support for forking off another client
2
3use crate::eth::{backend::db::Db, error::BlockchainError, pool::transactions::PoolTransaction};
4use alloy_consensus::{BlockHeader, TrieAccount};
5use alloy_eips::eip2930::AccessListResult;
6use alloy_network::{
7    AnyNetwork, AnyRpcBlock, BlockResponse, Network, TransactionResponse,
8    primitives::HeaderResponse,
9};
10use alloy_primitives::{
11    Address, B256, Bytes, StorageValue, U256,
12    map::{FbHashMap, HashMap, HashSet},
13};
14use alloy_provider::{
15    Provider,
16    ext::{DebugApi, TraceApi},
17};
18use alloy_rpc_types::{
19    BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse,
20    FeeHistory, Filter, Log,
21    simulate::{SimulatePayload, SimulatedBlock},
22    trace::{
23        geth::{GethDebugTracingOptions, GethTrace, TraceResult},
24        parity::{LocalizedTransactionTrace as Trace, TraceResultsWithTransactionHash, TraceType},
25    },
26};
27use alloy_transport::TransportError;
28use foundry_common::provider::{ProviderBuilder, RetryProvider};
29use foundry_evm::hardfork::FoundryHardfork;
30use foundry_primitives::{FoundryTxEnvelope, FoundryTxReceipt};
31use parking_lot::{
32    RawRwLock, RwLock,
33    lock_api::{RwLockReadGuard, RwLockWriteGuard},
34};
35use revm::context_interface::block::BlobExcessGasAndPrice;
36use std::{sync::Arc, time::Duration};
37use tokio::sync::RwLock as AsyncRwLock;
38
39/// Represents a fork of a remote client
40///
41/// This type contains a subset of the [`EthApi`](crate::eth::EthApi) functions but will exclusively
42/// fetch the requested data from the remote client, if it wasn't already fetched.
43#[derive(Clone, Debug)]
44pub struct ClientFork<N: Network = AnyNetwork> {
45    /// Contains the cached data
46    pub storage: Arc<RwLock<ForkedStorage<N>>>,
47    /// contains the info how the fork is configured
48    // Wrapping this in a lock, ensures we can update this on the fly via additional custom RPC
49    // endpoints
50    pub config: Arc<RwLock<ClientForkConfig<N>>>,
51    /// This also holds a handle to the underlying database
52    pub database: Arc<AsyncRwLock<Box<dyn Db>>>,
53}
54
55impl<N: Network> ClientFork<N> {
56    /// Creates a new instance of the fork
57    pub fn new(config: ClientForkConfig<N>, database: Arc<AsyncRwLock<Box<dyn Db>>>) -> Self {
58        Self { storage: Default::default(), config: Arc::new(RwLock::new(config)), database }
59    }
60
61    /// Removes all data cached from previous responses
62    pub fn clear_cached_storage(&self) {
63        self.storage.write().clear()
64    }
65
66    /// Returns true whether the block predates the fork
67    pub fn predates_fork(&self, block: u64) -> bool {
68        block < self.block_number()
69    }
70
71    /// Returns true whether the block predates the fork _or_ is the same block as the fork
72    pub fn predates_fork_inclusive(&self, block: u64) -> bool {
73        block <= self.block_number()
74    }
75
76    pub fn timestamp(&self) -> u64 {
77        self.config.read().timestamp
78    }
79
80    pub fn block_number(&self) -> u64 {
81        self.config.read().block_number
82    }
83
84    /// Returns the transaction hash we forked off of, if any.
85    pub fn transaction_hash(&self) -> Option<B256> {
86        self.config.read().transaction_hash
87    }
88
89    pub fn total_difficulty(&self) -> U256 {
90        self.config.read().total_difficulty
91    }
92
93    pub fn base_fee(&self) -> Option<u128> {
94        self.config.read().base_fee
95    }
96
97    pub fn block_hash(&self) -> B256 {
98        self.config.read().block_hash
99    }
100
101    pub fn eth_rpc_url(&self) -> Option<String> {
102        self.config.read().eth_rpc_url().map(|s| s.to_string())
103    }
104
105    pub fn chain_id(&self) -> u64 {
106        self.config.read().chain_id
107    }
108
109    fn provider(&self) -> Arc<RetryProvider<N>> {
110        self.config.read().provider.clone()
111    }
112
113    fn storage_read(&self) -> RwLockReadGuard<'_, RawRwLock, ForkedStorage<N>> {
114        self.storage.read()
115    }
116
117    fn storage_write(&self) -> RwLockWriteGuard<'_, RawRwLock, ForkedStorage<N>> {
118        self.storage.write()
119    }
120
121    /// Returns the fee history  `eth_feeHistory`
122    pub async fn fee_history(
123        &self,
124        block_count: u64,
125        newest_block: BlockNumber,
126        reward_percentiles: &[f64],
127    ) -> Result<FeeHistory, TransportError> {
128        self.provider().get_fee_history(block_count, newest_block, reward_percentiles).await
129    }
130
131    /// Sends `eth_getProof`
132    pub async fn get_proof(
133        &self,
134        address: Address,
135        keys: Vec<B256>,
136        block_number: Option<BlockId>,
137    ) -> Result<EIP1186AccountProofResponse, TransportError> {
138        self.provider().get_proof(address, keys).block_id(block_number.unwrap_or_default()).await
139    }
140
141    pub async fn storage_at(
142        &self,
143        address: Address,
144        index: U256,
145        number: Option<BlockNumber>,
146    ) -> Result<StorageValue, TransportError> {
147        self.provider()
148            .get_storage_at(address, index)
149            .block_id(number.unwrap_or_default().into())
150            .await
151    }
152
153    pub async fn logs(&self, filter: &Filter) -> Result<Vec<Log>, TransportError> {
154        if let Some(logs) = self.storage_read().logs.get(filter).cloned() {
155            return Ok(logs);
156        }
157
158        let logs = self.provider().get_logs(filter).await?;
159
160        let mut storage = self.storage_write();
161        storage.logs.insert(filter.clone(), logs.clone());
162        Ok(logs)
163    }
164
165    pub async fn get_code(
166        &self,
167        address: Address,
168        blocknumber: u64,
169    ) -> Result<Bytes, TransportError> {
170        trace!(target: "backend::fork", "get_code={:?}", address);
171        if let Some(code) = self.storage_read().code_at.get(&(address, blocknumber)).cloned() {
172            return Ok(code);
173        }
174
175        let block_id = BlockId::number(blocknumber);
176
177        let code = self.provider().get_code_at(address).block_id(block_id).await?;
178
179        let mut storage = self.storage_write();
180        storage.code_at.insert((address, blocknumber), code.clone());
181
182        Ok(code)
183    }
184
185    pub async fn get_balance(
186        &self,
187        address: Address,
188        blocknumber: u64,
189    ) -> Result<U256, TransportError> {
190        trace!(target: "backend::fork", "get_balance={:?}", address);
191        self.provider().get_balance(address).block_id(blocknumber.into()).await
192    }
193
194    pub async fn get_nonce(&self, address: Address, block: u64) -> Result<u64, TransportError> {
195        trace!(target: "backend::fork", "get_nonce={:?}", address);
196        self.provider().get_transaction_count(address).block_id(block.into()).await
197    }
198
199    pub async fn get_account(
200        &self,
201        address: Address,
202        blocknumber: u64,
203    ) -> Result<TrieAccount, TransportError> {
204        trace!(target: "backend::fork", "get_account={:?}", address);
205        self.provider().get_account(address).block_id(blocknumber.into()).await
206    }
207
208    pub async fn trace_transaction(&self, hash: B256) -> Result<Vec<Trace>, TransportError> {
209        if let Some(traces) = self.storage_read().transaction_traces.get(&hash).cloned() {
210            return Ok(traces);
211        }
212
213        let traces = self.provider().trace_transaction(hash).await?.into_iter().collect::<Vec<_>>();
214
215        let mut storage = self.storage_write();
216        storage.transaction_traces.insert(hash, traces.clone());
217
218        Ok(traces)
219    }
220
221    pub async fn debug_trace_transaction(
222        &self,
223        hash: B256,
224        opts: GethDebugTracingOptions,
225    ) -> Result<GethTrace, TransportError> {
226        if let Some(traces) = self.storage_read().geth_transaction_traces.get(&hash).cloned() {
227            return Ok(traces);
228        }
229
230        let trace = self.provider().debug_trace_transaction(hash, opts).await?;
231
232        let mut storage = self.storage_write();
233        storage.geth_transaction_traces.insert(hash, trace.clone());
234
235        Ok(trace)
236    }
237
238    pub async fn debug_code_by_hash(
239        &self,
240        code_hash: B256,
241        block_id: Option<BlockId>,
242    ) -> Result<Option<Bytes>, TransportError> {
243        self.provider().debug_code_by_hash(code_hash, block_id).await
244    }
245
246    pub async fn debug_trace_block_by_hash(
247        &self,
248        block_hash: B256,
249        opts: GethDebugTracingOptions,
250    ) -> Result<Vec<TraceResult>, TransportError> {
251        if let Some(traces) = self.storage_read().geth_block_traces.get(&block_hash).cloned() {
252            return Ok(traces);
253        }
254
255        let trace_results = self.provider().debug_trace_block_by_hash(block_hash, opts).await?;
256
257        let mut storage = self.storage_write();
258        storage.geth_block_traces.insert(block_hash, trace_results.clone());
259
260        Ok(trace_results)
261    }
262
263    pub async fn debug_trace_block_by_number(
264        &self,
265        number: u64,
266        opts: GethDebugTracingOptions,
267    ) -> Result<Vec<TraceResult>, TransportError> {
268        if let Ok(Some(block)) = self.provider().get_block_by_number(number.into()).await {
269            let block_hash = block.header().hash();
270            return self.debug_trace_block_by_hash(block_hash, opts).await;
271        }
272
273        self.provider().debug_trace_block_by_number(number.into(), opts).await
274    }
275
276    pub async fn trace_block(&self, number: u64) -> Result<Vec<Trace>, TransportError> {
277        if let Some(traces) = self.storage_read().block_traces.get(&number).cloned() {
278            return Ok(traces);
279        }
280
281        let traces =
282            self.provider().trace_block(number.into()).await?.into_iter().collect::<Vec<_>>();
283
284        let mut storage = self.storage_write();
285        storage.block_traces.insert(number, traces.clone());
286
287        Ok(traces)
288    }
289
290    pub async fn trace_replay_block_transactions(
291        &self,
292        number: u64,
293        trace_types: HashSet<TraceType>,
294    ) -> Result<Vec<TraceResultsWithTransactionHash>, TransportError> {
295        // Forward to upstream provider for historical blocks
296        let params = (number, trace_types.iter().map(|t| format!("{t:?}")).collect::<Vec<_>>());
297        self.provider().raw_request("trace_replayBlockTransactions".into(), params).await
298    }
299
300    /// Reset the fork to a fresh forked state, and optionally update the fork config
301    pub async fn reset(
302        &self,
303        urls: Vec<String>,
304        block_number: impl Into<BlockId>,
305    ) -> Result<(), BlockchainError> {
306        let block_number = block_number.into();
307        {
308            self.database
309                .write()
310                .await
311                .maybe_reset(urls.clone(), block_number)
312                .map_err(BlockchainError::Internal)?;
313        }
314
315        if !urls.is_empty() {
316            self.config.write().update_urls(urls)?;
317            let override_chain_id = self.config.read().override_chain_id;
318            let chain_id = if let Some(chain_id) = override_chain_id {
319                chain_id
320            } else {
321                self.provider().get_chain_id().await?
322            };
323            self.config.write().chain_id = chain_id;
324        }
325
326        let provider = self.provider();
327        let block =
328            provider.get_block(block_number).await?.ok_or(BlockchainError::BlockNotFound)?;
329        let block_hash = block.header().hash();
330        let timestamp = block.header().timestamp();
331        let base_fee = block.header().base_fee_per_gas();
332        let total_difficulty = block.header().difficulty();
333
334        let number = block.header().number();
335        self.config.write().update_block(
336            number,
337            block_hash,
338            timestamp,
339            base_fee.map(|g| g as u128),
340            total_difficulty,
341        );
342
343        self.clear_cached_storage();
344
345        self.database.write().await.insert_block_hash(U256::from(number), block_hash);
346
347        Ok(())
348    }
349
350    /// Sends `eth_call`
351    pub async fn call(
352        &self,
353        request: &N::TransactionRequest,
354        block: Option<BlockNumber>,
355    ) -> Result<Bytes, TransportError> {
356        let block = block.unwrap_or(BlockNumber::Latest);
357        let res = self.provider().call(request.clone()).block(block.into()).await?;
358
359        Ok(res)
360    }
361
362    /// Sends `eth_simulateV1`
363    pub async fn simulate_v1(
364        &self,
365        request: &SimulatePayload,
366        block: Option<BlockNumber>,
367    ) -> Result<Vec<SimulatedBlock<N::BlockResponse>>, TransportError> {
368        let mut simulate_call = self.provider().simulate(request);
369        if let Some(n) = block {
370            simulate_call = simulate_call.number(n.as_number().unwrap());
371        }
372
373        let res = simulate_call.await?;
374
375        Ok(res)
376    }
377
378    /// Sends `eth_estimateGas`
379    pub async fn estimate_gas(
380        &self,
381        request: &N::TransactionRequest,
382        block: Option<BlockNumber>,
383    ) -> Result<u128, TransportError> {
384        let block = block.unwrap_or_default();
385        let res = self.provider().estimate_gas(request.clone()).block(block.into()).await?;
386
387        Ok(res as u128)
388    }
389
390    /// Sends `eth_createAccessList`
391    pub async fn create_access_list(
392        &self,
393        request: &N::TransactionRequest,
394        block: Option<BlockNumber>,
395    ) -> Result<AccessListResult, TransportError> {
396        self.provider().create_access_list(request).block_id(block.unwrap_or_default().into()).await
397    }
398
399    pub async fn transaction_by_block_number_and_index(
400        &self,
401        number: u64,
402        index: usize,
403    ) -> Result<Option<N::TransactionResponse>, TransportError> {
404        let block = self.block_by_number(number).await?;
405        self.transaction_at_block_index(block, index).await
406    }
407
408    pub async fn transaction_by_block_hash_and_index(
409        &self,
410        hash: B256,
411        index: usize,
412    ) -> Result<Option<N::TransactionResponse>, TransportError> {
413        let block = self.block_by_hash(hash).await?;
414        self.transaction_at_block_index(block, index).await
415    }
416
417    async fn transaction_at_block_index(
418        &self,
419        block: Option<N::BlockResponse>,
420        index: usize,
421    ) -> Result<Option<N::TransactionResponse>, TransportError> {
422        if let Some(block) = block {
423            match block.transactions() {
424                BlockTransactions::Full(txs) => {
425                    if let Some(tx) = txs.get(index) {
426                        return Ok(Some(tx.clone()));
427                    }
428                }
429                BlockTransactions::Hashes(hashes) => {
430                    if let Some(tx_hash) = hashes.get(index) {
431                        return self.transaction_by_hash(*tx_hash).await;
432                    }
433                }
434                BlockTransactions::Uncle => {}
435            }
436        }
437        Ok(None)
438    }
439
440    pub async fn transaction_by_hash(
441        &self,
442        hash: B256,
443    ) -> Result<Option<N::TransactionResponse>, TransportError> {
444        trace!(target: "backend::fork", "transaction_by_hash={:?}", hash);
445        if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() {
446            return Ok(tx);
447        }
448
449        let tx = self.provider().get_transaction_by_hash(hash).await?;
450        if let Some(tx) = tx.clone() {
451            let mut storage = self.storage_write();
452            storage.transactions.insert(hash, tx);
453        }
454        Ok(tx)
455    }
456
457    pub async fn block_by_hash(
458        &self,
459        hash: B256,
460    ) -> Result<Option<N::BlockResponse>, TransportError> {
461        if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() {
462            block.transactions_mut().convert_to_hashes();
463            return Ok(Some(block));
464        }
465
466        Ok(self.fetch_full_block(hash).await?.map(|mut b| {
467            b.transactions_mut().convert_to_hashes();
468            b
469        }))
470    }
471
472    pub async fn block_by_hash_full(
473        &self,
474        hash: B256,
475    ) -> Result<Option<N::BlockResponse>, TransportError> {
476        if let Some(block) = self.storage_read().blocks.get(&hash).cloned()
477            && let Some(block) = self.convert_to_full_block(block)
478        {
479            return Ok(Some(block));
480        }
481        self.fetch_full_block(hash).await
482    }
483
484    pub async fn block_by_number(
485        &self,
486        block_number: u64,
487    ) -> Result<Option<N::BlockResponse>, TransportError> {
488        if let Some(mut block) = self
489            .storage_read()
490            .hashes
491            .get(&block_number)
492            .and_then(|hash| self.storage_read().blocks.get(hash).cloned())
493        {
494            block.transactions_mut().convert_to_hashes();
495            return Ok(Some(block));
496        }
497
498        let mut block = self.fetch_full_block(block_number).await?;
499        if let Some(block) = &mut block {
500            block.transactions_mut().convert_to_hashes();
501        }
502        Ok(block)
503    }
504
505    pub async fn block_by_number_full(
506        &self,
507        block_number: u64,
508    ) -> Result<Option<N::BlockResponse>, TransportError> {
509        if let Some(block) = self
510            .storage_read()
511            .hashes
512            .get(&block_number)
513            .copied()
514            .and_then(|hash| self.storage_read().blocks.get(&hash).cloned())
515            && let Some(block) = self.convert_to_full_block(block)
516        {
517            return Ok(Some(block));
518        }
519
520        self.fetch_full_block(block_number).await
521    }
522
523    async fn fetch_full_block(
524        &self,
525        block_id: impl Into<BlockId>,
526    ) -> Result<Option<N::BlockResponse>, TransportError> {
527        if let Some(block) = self.provider().get_block(block_id.into()).full().await? {
528            let hash = block.header().hash();
529            let block_number = block.header().number();
530            let mut storage = self.storage_write();
531            // also insert all transactions
532            let block_txs = match block.transactions() {
533                BlockTransactions::Full(txs) => txs.to_owned(),
534                _ => vec![],
535            };
536            storage.transactions.extend(block_txs.iter().map(|tx| (tx.tx_hash(), tx.clone())));
537            storage.hashes.insert(block_number, hash);
538            storage.blocks.insert(hash, block.clone());
539            return Ok(Some(block));
540        }
541
542        Ok(None)
543    }
544
545    /// Converts a block of hashes into a full block
546    fn convert_to_full_block(&self, mut block: N::BlockResponse) -> Option<N::BlockResponse> {
547        let storage = self.storage.read();
548        let transactions = block
549            .transactions()
550            .hashes()
551            .map(|hash| storage.transactions.get(&hash).cloned())
552            .collect::<Option<Vec<_>>>()?;
553        *block.transactions_mut() = BlockTransactions::Full(transactions);
554        Some(block)
555    }
556}
557
558impl ClientFork {
559    pub async fn transaction_receipt(
560        &self,
561        hash: B256,
562    ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
563        if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() {
564            return Ok(Some(receipt));
565        }
566
567        if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? {
568            let receipt = FoundryTxReceipt::try_from(receipt)
569                .map_err(|_| BlockchainError::FailedToDecodeReceipt)?;
570            let mut storage = self.storage_write();
571            storage.transaction_receipts.insert(hash, receipt.clone());
572            return Ok(Some(receipt));
573        }
574
575        Ok(None)
576    }
577
578    pub async fn block_receipts(
579        &self,
580        number: u64,
581    ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
582        if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() {
583            return Ok(receipts);
584        }
585
586        // TODO Needs to be removed.
587        // Since alloy doesn't indicate in the result whether the block exists,
588        // this is being temporarily implemented in anvil.
589        if self.predates_fork_inclusive(number) {
590            let receipts = self.provider().get_block_receipts(BlockId::from(number)).await?;
591            let receipts = receipts
592                .map(|r| {
593                    r.into_iter()
594                        .map(|r| {
595                            FoundryTxReceipt::try_from(r)
596                                .map_err(|_| BlockchainError::FailedToDecodeReceipt)
597                        })
598                        .collect::<Result<Vec<_>, _>>()
599                })
600                .transpose()?;
601
602            if let Some(receipts) = receipts.clone() {
603                let mut storage = self.storage_write();
604                storage.block_receipts.insert(number, receipts);
605            }
606
607            return Ok(receipts);
608        }
609
610        Ok(None)
611    }
612
613    pub async fn uncle_by_block_hash_and_index(
614        &self,
615        hash: B256,
616        index: usize,
617    ) -> Result<Option<AnyRpcBlock>, TransportError> {
618        if let Some(block) = self.block_by_hash(hash).await? {
619            return self.uncles_by_block_and_index(block, index).await;
620        }
621        Ok(None)
622    }
623
624    pub async fn uncle_by_block_number_and_index(
625        &self,
626        number: u64,
627        index: usize,
628    ) -> Result<Option<AnyRpcBlock>, TransportError> {
629        if let Some(block) = self.block_by_number(number).await? {
630            return self.uncles_by_block_and_index(block, index).await;
631        }
632        Ok(None)
633    }
634
635    async fn uncles_by_block_and_index(
636        &self,
637        block: AnyRpcBlock,
638        index: usize,
639    ) -> Result<Option<AnyRpcBlock>, TransportError> {
640        let block_hash = block.header().hash();
641        let block_number = block.header().number();
642        if let Some(uncles) = self.storage_read().uncles.get(&block_hash) {
643            return Ok(uncles.get(index).cloned());
644        }
645
646        let mut uncles = Vec::with_capacity(block.uncles.len());
647        for (uncle_idx, _) in block.uncles.iter().enumerate() {
648            let uncle =
649                match self.provider().get_uncle(block_number.into(), uncle_idx as u64).await? {
650                    Some(u) => u,
651                    None => return Ok(None),
652                };
653            uncles.push(uncle);
654        }
655        self.storage_write().uncles.insert(block_hash, uncles.clone());
656        Ok(uncles.get(index).cloned())
657    }
658}
659
660/// Contains all fork metadata
661#[derive(Clone, Debug)]
662pub struct ClientForkConfig<N: Network = AnyNetwork> {
663    /// All fork URLs. The first entry is the primary endpoint.
664    /// When multiple URLs are present, requests are distributed using
665    /// round-robin load balancing with retry-based failover.
666    pub fork_urls: Vec<String>,
667    /// The block number of the forked block
668    pub block_number: u64,
669    /// The hash of the forked block
670    pub block_hash: B256,
671    /// The transaction hash we forked off of, if any.
672    pub transaction_hash: Option<B256>,
673    pub provider: Arc<RetryProvider<N>>,
674    pub chain_id: u64,
675    pub override_chain_id: Option<u64>,
676    /// The hardfork resolved for the forked block, if known.
677    pub hardfork: Option<FoundryHardfork>,
678    /// The timestamp for the forked block
679    pub timestamp: u64,
680    /// The basefee of the forked block
681    pub base_fee: Option<u128>,
682    /// Blob gas used of the forked block
683    pub blob_gas_used: Option<u128>,
684    /// Blob excess gas and price of the forked block
685    pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
686    /// request timeout
687    pub timeout: Duration,
688    /// request retries for spurious networks
689    pub retries: u32,
690    /// request retries for spurious networks
691    pub backoff: Duration,
692    /// available CUPS
693    pub compute_units_per_second: u64,
694    /// Headers to include with RPC requests
695    pub headers: Vec<String>,
696    /// total difficulty of the chain until this block
697    pub total_difficulty: U256,
698    /// Transactions to force include in the forked chain
699    pub force_transactions: Option<Vec<PoolTransaction<FoundryTxEnvelope>>>,
700}
701
702impl<N: Network> ClientForkConfig<N> {
703    /// Returns the primary RPC URL (first entry in `fork_urls`).
704    pub fn eth_rpc_url(&self) -> Option<&str> {
705        self.fork_urls.first().map(|s| s.as_str())
706    }
707
708    /// Updates the provider URLs
709    ///
710    /// # Errors
711    ///
712    /// This will fail if no new provider could be established (erroneous URL)
713    fn update_urls(&mut self, urls: Vec<String>) -> Result<(), BlockchainError> {
714        let primary = urls.first().ok_or_else(|| {
715            BlockchainError::InvalidUrl("at least one fork URL required".to_string())
716        })?;
717
718        let builder = ProviderBuilder::<N>::new(primary.as_str())
719            .timeout(self.timeout)
720            .max_retry(self.retries)
721            .initial_backoff(self.backoff.as_millis() as u64)
722            .compute_units_per_second(self.compute_units_per_second)
723            .headers(self.headers.clone());
724
725        self.provider = Arc::new(if urls.len() > 1 {
726            builder
727                .build_fallback(urls.clone())
728                .map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))?
729        } else {
730            builder.build().map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))?
731        });
732        trace!(target: "fork", "Updated fork urls: {:?}", urls);
733        self.fork_urls = urls;
734        Ok(())
735    }
736
737    /// Updates the block forked off `(block number, block hash, timestamp)`
738    pub fn update_block(
739        &mut self,
740        block_number: u64,
741        block_hash: B256,
742        timestamp: u64,
743        base_fee: Option<u128>,
744        total_difficulty: U256,
745    ) {
746        self.block_number = block_number;
747        self.block_hash = block_hash;
748        self.timestamp = timestamp;
749        self.base_fee = base_fee;
750        self.total_difficulty = total_difficulty;
751        trace!(target: "fork", "Updated block number={} hash={:?}", block_number, block_hash);
752    }
753}
754
755/// Contains cached state fetched to serve EthApi requests
756///
757/// This is used as a cache so repeated requests to the same data are not sent to the remote client
758#[derive(Clone, Debug)]
759pub struct ForkedStorage<N: Network = AnyNetwork> {
760    pub uncles: FbHashMap<32, Vec<N::BlockResponse>>,
761    pub blocks: FbHashMap<32, N::BlockResponse>,
762    pub hashes: HashMap<u64, B256>,
763    pub transactions: FbHashMap<32, N::TransactionResponse>,
764    pub transaction_receipts: FbHashMap<32, FoundryTxReceipt>,
765    pub transaction_traces: FbHashMap<32, Vec<Trace>>,
766    pub logs: HashMap<Filter, Vec<Log>>,
767    pub geth_transaction_traces: FbHashMap<32, GethTrace>,
768    pub geth_block_traces: FbHashMap<32, Vec<TraceResult>>,
769    pub block_traces: HashMap<u64, Vec<Trace>>,
770    pub block_receipts: HashMap<u64, Vec<FoundryTxReceipt>>,
771    pub code_at: HashMap<(Address, u64), Bytes>,
772}
773
774impl<N: Network> Default for ForkedStorage<N> {
775    fn default() -> Self {
776        Self {
777            uncles: Default::default(),
778            blocks: Default::default(),
779            hashes: Default::default(),
780            transactions: Default::default(),
781            transaction_receipts: Default::default(),
782            transaction_traces: Default::default(),
783            logs: Default::default(),
784            geth_transaction_traces: Default::default(),
785            geth_block_traces: Default::default(),
786            block_traces: Default::default(),
787            block_receipts: Default::default(),
788            code_at: Default::default(),
789        }
790    }
791}
792
793impl<N: Network> ForkedStorage<N> {
794    /// Clears all data
795    pub fn clear(&mut self) {
796        // simply replace with a completely new, empty instance
797        *self = Self::default()
798    }
799}