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};
4use alloy_chains::NamedChain;
5use alloy_consensus::{BlockHeader, TrieAccount};
6use alloy_eips::eip2930::AccessListResult;
7use alloy_network::{
8    AnyNetwork, AnyRpcBlock, BlockResponse, Network, TransactionResponse,
9    primitives::HeaderResponse,
10};
11use alloy_primitives::{
12    Address, B256, Bytes, StorageValue, U256,
13    map::{FbHashMap, HashMap, HashSet},
14};
15use alloy_provider::{
16    Provider,
17    ext::{DebugApi, TraceApi},
18};
19use alloy_rpc_types::{
20    BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse,
21    FeeHistory, Filter, FilterBlockOption, FilterSet, Index, Log,
22    request::TransactionRequest,
23    simulate::{SimulatePayload, SimulatedBlock},
24    state::StateOverride,
25    trace::{
26        geth::{GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult},
27        opcode::{BlockOpcodeGas, TransactionOpcodeGas},
28        parity::{
29            LocalizedTransactionTrace as Trace, TraceResults, TraceResultsWithTransactionHash,
30            TraceType,
31        },
32    },
33};
34use alloy_rpc_types_eth::{AccountInfo, Bundle, EthCallResponse, StateContext};
35use alloy_rpc_types_mev::{EthCallBundle, EthCallBundleResponse};
36use alloy_serde::WithOtherFields;
37use alloy_transport::TransportError;
38use foundry_common::provider::{RetryProvider, is_rpc_method_not_found};
39use foundry_evm::{
40    backend::{AccountFetchPolicy, BlockchainDb, account_fetch_policy_for_source},
41    fork::{cache_bal, validate_bal},
42    hardfork::FoundryHardfork,
43};
44use foundry_evm_networks::{NetworkConfigs, NetworkVariant};
45use foundry_primitives::FoundryTxReceipt;
46use parking_lot::{
47    RawRwLock, RwLock,
48    lock_api::{RwLockReadGuard, RwLockWriteGuard},
49};
50use revm::{context_interface::block::BlobExcessGasAndPrice, primitives::hardfork::SpecId};
51use std::{sync::Arc, time::Duration};
52use tokio::sync::RwLock as AsyncRwLock;
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub(crate) struct ForkEndpointIdentity {
56    pub(crate) execution_chain_id: u64,
57    pub(crate) source_chain_id: u64,
58    pub(crate) network: Option<NetworkVariant>,
59    pub(crate) network_profile: Option<NetworkConfigs>,
60    pub(crate) hardfork: Option<FoundryHardfork>,
61    pub(crate) instance_id: Option<B256>,
62    pub(crate) source_fork_block_number: Option<u64>,
63    pub(crate) source_fork_block_hash: Option<B256>,
64}
65
66impl ForkEndpointIdentity {
67    /// Returns whether this identity was reported by an Anvil endpoint.
68    pub(crate) const fn is_authoritative(self) -> bool {
69        self.hardfork.is_some()
70    }
71
72    /// Returns whether two endpoints expose the same fork execution context.
73    pub(crate) fn context_eq(self, other: Self) -> bool {
74        self.execution_chain_id == other.execution_chain_id
75            && self.source_chain_id == other.source_chain_id
76            && self.network == other.network
77            && self.network_profile == other.network_profile
78            && self.hardfork == other.hardfork
79            && self.source_fork_block_number == other.source_fork_block_number
80            && self.source_fork_block_hash == other.source_fork_block_hash
81    }
82}
83
84/// Ensures Anvil's EVM backend can execute the resolved upstream source chain.
85///
86/// Anvil's execution chain-ID override does not change the bytecode format in remote fork state.
87pub(crate) fn ensure_fork_network_supported(chain_id: u64) -> Result<(), BlockchainError> {
88    if matches!(NamedChain::try_from(chain_id), Ok(NamedChain::ZkSync | NamedChain::ZkSyncTestnet))
89    {
90        return Err(BlockchainError::UnsupportedForkNetwork {
91            chain_id,
92            reason: "Anvil's EVM backend cannot execute native EraVM bytecode; use `anvil-zksync` for zkSync Era forks",
93        });
94    }
95    Ok(())
96}
97
98/// Represents a fork of a remote client
99///
100/// This type contains a subset of the [`EthApi`](crate::eth::EthApi) functions but will exclusively
101/// fetch the requested data from the remote client, if it wasn't already fetched.
102#[derive(Clone, Debug)]
103pub struct ClientFork<N: Network = AnyNetwork> {
104    /// Contains the cached data
105    pub storage: Arc<RwLock<ForkedStorage<N>>>,
106    /// contains the info how the fork is configured
107    // Wrapping this in a lock, ensures we can update this on the fly via additional custom RPC
108    // endpoints
109    pub config: Arc<RwLock<ClientForkConfig<N>>>,
110    /// This also holds a handle to the underlying database
111    pub database: Arc<AsyncRwLock<Box<dyn Db>>>,
112}
113
114impl<N: Network> ClientFork<N> {
115    /// Creates a new instance of the fork
116    pub fn new(config: ClientForkConfig<N>, database: Arc<AsyncRwLock<Box<dyn Db>>>) -> Self {
117        Self { storage: Default::default(), config: Arc::new(RwLock::new(config)), database }
118    }
119
120    /// Removes all data cached from previous responses
121    pub fn clear_cached_storage(&self) {
122        self.storage.write().clear()
123    }
124
125    /// Returns true whether the block predates the fork
126    pub fn predates_fork(&self, block: u64) -> bool {
127        block < self.block_number()
128    }
129
130    /// Returns true whether the block predates the fork _or_ is the same block as the fork
131    pub fn predates_fork_inclusive(&self, block: u64) -> bool {
132        block <= self.block_number()
133    }
134
135    pub fn timestamp(&self) -> u64 {
136        self.config.read().timestamp
137    }
138
139    pub fn block_number(&self) -> u64 {
140        self.config.read().block_number
141    }
142
143    /// Converts a local RPC block number to its EVM-visible number.
144    ///
145    /// Local mining advances both numbers once per block, preserving the fork root's offset.
146    pub fn evm_block_number(&self, rpc_number: u64) -> U256 {
147        let config = self.config.read();
148        U256::from(rpc_number)
149            .saturating_add(U256::from(config.evm_block_number))
150            .saturating_sub(U256::from(config.block_number))
151    }
152
153    /// Converts a local EVM-visible block number to its RPC number.
154    pub fn rpc_block_number(&self, evm_number: U256) -> u64 {
155        let config = self.config.read();
156        evm_number
157            .saturating_add(U256::from(config.block_number))
158            .saturating_sub(U256::from(config.evm_block_number))
159            .saturating_to()
160    }
161
162    /// Returns the transaction hash we forked off of, if any.
163    pub fn transaction_hash(&self) -> Option<B256> {
164        self.config.read().transaction_hash
165    }
166
167    pub fn total_difficulty(&self) -> U256 {
168        self.config.read().total_difficulty
169    }
170
171    pub fn base_fee(&self) -> Option<u128> {
172        self.config.read().base_fee
173    }
174
175    pub fn block_hash(&self) -> B256 {
176        self.config.read().block_hash
177    }
178
179    pub fn eth_rpc_url(&self) -> Option<String> {
180        self.config.read().eth_rpc_url().map(|s| s.to_string())
181    }
182
183    pub fn chain_id(&self) -> u64 {
184        self.config.read().chain_id
185    }
186
187    /// Returns whether this fork source requires the combined account-info RPC.
188    pub fn requires_account_info(&self) -> bool {
189        let config = self.config.read();
190        account_fetch_policy_for_source(
191            config.chain_id,
192            config.endpoint_identity.network_profile.unwrap_or_default(),
193        ) == AccountFetchPolicy::RequireAccountInfo
194    }
195
196    /// Returns the execution chain ID exposed by the forked node.
197    pub fn execution_chain_id(&self) -> u64 {
198        self.config.read().execution_chain_id
199    }
200
201    fn provider(&self) -> Arc<RetryProvider<N>> {
202        self.config.read().provider.clone()
203    }
204
205    fn storage_read(&self) -> RwLockReadGuard<'_, RawRwLock, ForkedStorage<N>> {
206        self.storage.read()
207    }
208
209    fn storage_write(&self) -> RwLockWriteGuard<'_, RawRwLock, ForkedStorage<N>> {
210        self.storage.write()
211    }
212
213    /// Returns the fee history  `eth_feeHistory`
214    pub async fn fee_history(
215        &self,
216        block_count: u64,
217        newest_block: BlockNumber,
218        reward_percentiles: &[f64],
219    ) -> Result<FeeHistory, TransportError> {
220        self.provider().get_fee_history(block_count, newest_block, reward_percentiles).await
221    }
222
223    /// Sends `eth_getProof`
224    pub async fn get_proof(
225        &self,
226        address: Address,
227        keys: Vec<B256>,
228        block_number: Option<BlockId>,
229    ) -> Result<EIP1186AccountProofResponse, TransportError> {
230        self.provider().get_proof(address, keys).block_id(block_number.unwrap_or_default()).await
231    }
232
233    /// Sends `eth_getBlockAccessList`
234    pub async fn block_access_list(
235        &self,
236        block_id: BlockId,
237    ) -> Result<Option<serde_json::Value>, TransportError> {
238        self.provider().raw_request("eth_getBlockAccessList".into(), (block_id,)).await
239    }
240
241    /// Sends `eth_getBlockAccessListByBlockHash`
242    pub async fn block_access_list_by_hash(
243        &self,
244        block_hash: B256,
245    ) -> Result<Option<serde_json::Value>, TransportError> {
246        self.provider().raw_request("eth_getBlockAccessListByBlockHash".into(), (block_hash,)).await
247    }
248
249    /// Sends `eth_getBlockAccessListByBlockNumber`
250    pub async fn block_access_list_by_number(
251        &self,
252        block_number: BlockNumber,
253    ) -> Result<Option<serde_json::Value>, TransportError> {
254        self.provider()
255            .raw_request("eth_getBlockAccessListByBlockNumber".into(), (block_number,))
256            .await
257    }
258
259    /// Sends `eth_getBlockAccessListRaw`.
260    pub async fn block_access_list_raw(
261        &self,
262        block_id: BlockId,
263    ) -> Result<Option<Bytes>, TransportError> {
264        self.provider().raw_request("eth_getBlockAccessListRaw".into(), (block_id,)).await
265    }
266
267    pub async fn storage_at(
268        &self,
269        address: Address,
270        index: U256,
271        number: Option<BlockNumber>,
272    ) -> Result<StorageValue, TransportError> {
273        self.provider()
274            .get_storage_at(address, index)
275            .block_id(number.unwrap_or_default().into())
276            .await
277    }
278
279    pub async fn logs(&self, filter: &Filter) -> Result<Vec<Log>, TransportError> {
280        let key = LogsCacheKey::from(filter);
281        if let Some(logs) = self.storage_read().logs.get(&key).cloned() {
282            return Ok(logs);
283        }
284
285        let logs = self.provider().get_logs(filter).await?;
286
287        let mut storage = self.storage_write();
288        storage.logs.insert(key, logs.clone());
289        Ok(logs)
290    }
291
292    pub async fn get_code(
293        &self,
294        address: Address,
295        blocknumber: u64,
296    ) -> Result<Bytes, TransportError> {
297        trace!(target: "backend::fork", "get_code={:?}", address);
298        if let Some(code) = self.storage_read().code_at.get(&(address, blocknumber)).cloned() {
299            return Ok(code);
300        }
301
302        let block_id = BlockId::number(blocknumber);
303
304        let code = self.provider().get_code_at(address).block_id(block_id).await?;
305
306        let mut storage = self.storage_write();
307        storage.code_at.insert((address, blocknumber), code.clone());
308
309        Ok(code)
310    }
311
312    pub async fn get_balance(
313        &self,
314        address: Address,
315        blocknumber: u64,
316    ) -> Result<U256, TransportError> {
317        trace!(target: "backend::fork", "get_balance={:?}", address);
318        self.provider().get_balance(address).block_id(blocknumber.into()).await
319    }
320
321    pub async fn get_account_info(
322        &self,
323        address: Address,
324        blocknumber: u64,
325    ) -> Result<AccountInfo, TransportError> {
326        trace!(target: "backend::fork", "get_account_info={:?}", address);
327        self.provider().get_account_info(address).block_id(blocknumber.into()).await
328    }
329
330    pub async fn get_nonce(&self, address: Address, block: u64) -> Result<u64, TransportError> {
331        trace!(target: "backend::fork", "get_nonce={:?}", address);
332        self.provider().get_transaction_count(address).block_id(block.into()).await
333    }
334
335    pub async fn get_account(
336        &self,
337        address: Address,
338        blocknumber: u64,
339    ) -> Result<TrieAccount, TransportError> {
340        trace!(target: "backend::fork", "get_account={:?}", address);
341        self.provider().get_account(address).block_id(blocknumber.into()).await
342    }
343
344    pub async fn trace_transaction(
345        &self,
346        hash: B256,
347    ) -> Result<Option<Vec<Trace>>, TransportError> {
348        if let Some(traces) = self.storage_read().transaction_traces.get(&hash).cloned() {
349            return Ok(Some(traces));
350        }
351
352        let traces = self
353            .provider()
354            .raw_request::<_, Option<Vec<Trace>>>("trace_transaction".into(), (hash,))
355            .await?;
356
357        if let Some(traces) = &traces {
358            self.storage_write().transaction_traces.insert(hash, traces.clone());
359        }
360
361        Ok(traces)
362    }
363
364    pub async fn trace_transaction_opcode_gas(
365        &self,
366        hash: B256,
367    ) -> Result<Option<TransactionOpcodeGas>, TransportError> {
368        self.provider().raw_request("trace_transactionOpcodeGas".into(), (hash,)).await
369    }
370
371    /// Sends `trace_call`.
372    pub async fn trace_call(
373        &self,
374        request: WithOtherFields<TransactionRequest>,
375        trace_types: HashSet<TraceType>,
376        block: BlockId,
377    ) -> Result<TraceResults, TransportError> {
378        self.provider().raw_request("trace_call".into(), (request, trace_types, block)).await
379    }
380
381    /// Sends `trace_get`.
382    pub async fn trace_get(
383        &self,
384        hash: B256,
385        indices: Vec<Index>,
386    ) -> Result<Option<Trace>, TransportError> {
387        self.provider().raw_request("trace_get".into(), (hash, indices)).await
388    }
389
390    pub async fn debug_trace_transaction(
391        &self,
392        hash: B256,
393        opts: GethDebugTracingOptions,
394    ) -> Result<GethTrace, TransportError> {
395        if let Some(trace) = self
396            .storage_read()
397            .geth_transaction_traces
398            .get(&hash)
399            .and_then(|traces| traces.iter().find(|(cached_opts, _)| cached_opts == &opts))
400            .map(|(_, trace)| trace.clone())
401        {
402            return Ok(trace);
403        }
404
405        let trace = self.provider().debug_trace_transaction(hash, opts.clone()).await?;
406
407        let mut storage = self.storage_write();
408        let traces = storage.geth_transaction_traces.entry(hash).or_default();
409        if !traces.iter().any(|(cached_opts, _)| cached_opts == &opts) {
410            traces.push((opts, trace.clone()));
411        }
412
413        Ok(trace)
414    }
415
416    pub async fn debug_trace_call(
417        &self,
418        request: WithOtherFields<TransactionRequest>,
419        block_id: BlockId,
420        opts: GethDebugTracingCallOptions,
421    ) -> Result<GethTrace, TransportError> {
422        self.provider().raw_request("debug_traceCall".into(), (request, block_id, opts)).await
423    }
424
425    pub async fn debug_code_by_hash(
426        &self,
427        code_hash: B256,
428        block_id: Option<BlockId>,
429    ) -> Result<Option<Bytes>, TransportError> {
430        self.provider().debug_code_by_hash(code_hash, block_id).await
431    }
432
433    pub async fn debug_account_info_at(
434        &self,
435        block_id: BlockId,
436        tx_index: Index,
437        address: Address,
438    ) -> Result<Option<AccountInfo>, TransportError> {
439        self.provider()
440            .raw_request("debug_accountInfoAt".into(), (block_id, tx_index, address))
441            .await
442    }
443
444    pub async fn debug_trace_block_by_hash(
445        &self,
446        block_hash: B256,
447        opts: GethDebugTracingOptions,
448    ) -> Result<Vec<TraceResult>, TransportError> {
449        if let Some(traces) = self
450            .storage_read()
451            .geth_block_traces
452            .get(&block_hash)
453            .and_then(|traces| traces.iter().find(|(cached_opts, _)| cached_opts == &opts))
454            .map(|(_, traces)| traces.clone())
455        {
456            return Ok(traces);
457        }
458
459        let trace_results =
460            self.provider().debug_trace_block_by_hash(block_hash, opts.clone()).await?;
461
462        let mut storage = self.storage_write();
463        let traces = storage.geth_block_traces.entry(block_hash).or_default();
464        if !traces.iter().any(|(cached_opts, _)| cached_opts == &opts) {
465            traces.push((opts, trace_results.clone()));
466        }
467
468        Ok(trace_results)
469    }
470
471    pub async fn debug_trace_block_by_number(
472        &self,
473        number: u64,
474        opts: GethDebugTracingOptions,
475    ) -> Result<Vec<TraceResult>, TransportError> {
476        if let Ok(Some(block)) = self.provider().get_block_by_number(number.into()).await {
477            let block_hash = block.header().hash();
478            return self.debug_trace_block_by_hash(block_hash, opts).await;
479        }
480
481        self.provider().debug_trace_block_by_number(number.into(), opts).await
482    }
483
484    pub async fn trace_block(&self, number: u64) -> Result<Vec<Trace>, TransportError> {
485        if let Some(traces) = self.storage_read().block_traces.get(&number).cloned() {
486            return Ok(traces);
487        }
488
489        let traces =
490            self.provider().trace_block(number.into()).await?.into_iter().collect::<Vec<_>>();
491
492        let mut storage = self.storage_write();
493        storage.block_traces.insert(number, traces.clone());
494
495        Ok(traces)
496    }
497
498    pub async fn trace_replay_block_transactions(
499        &self,
500        number: u64,
501        trace_types: HashSet<TraceType>,
502    ) -> Result<Vec<TraceResultsWithTransactionHash>, TransportError> {
503        // Forward to upstream provider for historical blocks. Use the typed trace API so the block
504        // and trace types are serialized in the format upstream providers expect.
505        self.provider()
506            .trace_replay_block_transactions(BlockId::number(number))
507            .trace_types(trace_types)
508            .await
509    }
510
511    pub async fn trace_replay_transaction(
512        &self,
513        hash: B256,
514        trace_types: HashSet<TraceType>,
515    ) -> Result<Option<TraceResults>, TransportError> {
516        self.provider().raw_request("trace_replayTransaction".into(), (hash, trace_types)).await
517    }
518
519    pub async fn trace_block_opcode_gas(
520        &self,
521        block_id: BlockId,
522    ) -> Result<Option<BlockOpcodeGas>, TransportError> {
523        self.provider().raw_request("trace_blockOpcodeGas".into(), (block_id,)).await
524    }
525
526    /// Sends `eth_call`
527    pub async fn call(
528        &self,
529        request: &N::TransactionRequest,
530        block: Option<BlockNumber>,
531    ) -> Result<Bytes, TransportError> {
532        let block = block.unwrap_or(BlockNumber::Latest);
533        let res = self.provider().call(request.clone()).block(block.into()).await?;
534
535        Ok(res)
536    }
537
538    /// Sends `eth_call` with a network-specific request.
539    pub async fn call_raw(
540        &self,
541        request: &WithOtherFields<TransactionRequest>,
542        block: Option<BlockNumber>,
543    ) -> Result<Bytes, TransportError> {
544        self.provider()
545            .raw_request("eth_call".into(), (request, block.unwrap_or(BlockNumber::Latest)))
546            .await
547    }
548
549    /// Sends `eth_callMany`
550    pub async fn call_many(
551        &self,
552        bundles: Vec<Bundle<WithOtherFields<TransactionRequest>>>,
553        state_context: Option<StateContext>,
554        state_override: Option<StateOverride>,
555    ) -> Result<Vec<Vec<EthCallResponse>>, TransportError> {
556        self.provider()
557            .raw_request("eth_callMany".into(), (bundles, state_context, state_override))
558            .await
559    }
560
561    /// Sends `eth_callBundle`.
562    pub async fn call_bundle(
563        &self,
564        bundle: EthCallBundle,
565    ) -> Result<EthCallBundleResponse, TransportError> {
566        self.provider().raw_request("eth_callBundle".into(), (bundle,)).await
567    }
568
569    /// Sends `eth_simulateV1`
570    pub async fn simulate_v1(
571        &self,
572        request: &SimulatePayload<WithOtherFields<TransactionRequest>>,
573        block: Option<BlockId>,
574    ) -> Result<Vec<SimulatedBlock<N::BlockResponse>>, TransportError> {
575        self.provider().raw_request("eth_simulateV1".into(), (request, block)).await
576    }
577
578    /// Sends `eth_estimateGas`
579    pub async fn estimate_gas(
580        &self,
581        request: &N::TransactionRequest,
582        block: Option<BlockNumber>,
583    ) -> Result<u128, TransportError> {
584        let block = block.unwrap_or_default();
585        let res = self.provider().estimate_gas(request.clone()).block(block.into()).await?;
586
587        Ok(res as u128)
588    }
589
590    /// Sends `eth_estimateGas` with a network-specific request.
591    pub async fn estimate_gas_raw(
592        &self,
593        request: &WithOtherFields<TransactionRequest>,
594        block: Option<BlockNumber>,
595    ) -> Result<u128, TransportError> {
596        let gas: U256 = self
597            .provider()
598            .raw_request("eth_estimateGas".into(), (request, block.unwrap_or_default()))
599            .await?;
600        Ok(gas.saturating_to())
601    }
602
603    /// Sends `eth_createAccessList`
604    pub async fn create_access_list(
605        &self,
606        request: &N::TransactionRequest,
607        block: Option<BlockNumber>,
608    ) -> Result<AccessListResult, TransportError> {
609        self.provider().create_access_list(request).block_id(block.unwrap_or_default().into()).await
610    }
611
612    /// Sends `eth_createAccessList` with a network-specific request.
613    pub async fn create_access_list_raw(
614        &self,
615        request: &WithOtherFields<TransactionRequest>,
616        block: Option<BlockNumber>,
617    ) -> Result<AccessListResult, TransportError> {
618        self.provider()
619            .raw_request("eth_createAccessList".into(), (request, block.unwrap_or_default()))
620            .await
621    }
622
623    pub async fn transaction_by_block_number_and_index(
624        &self,
625        number: u64,
626        index: usize,
627    ) -> Result<Option<N::TransactionResponse>, TransportError> {
628        let block = self.block_by_number(number).await?;
629        self.transaction_at_block_index(block, index).await
630    }
631
632    pub async fn transaction_by_block_hash_and_index(
633        &self,
634        hash: B256,
635        index: usize,
636    ) -> Result<Option<N::TransactionResponse>, TransportError> {
637        let block = self.block_by_hash(hash).await?;
638        self.transaction_at_block_index(block, index).await
639    }
640
641    async fn transaction_at_block_index(
642        &self,
643        block: Option<N::BlockResponse>,
644        index: usize,
645    ) -> Result<Option<N::TransactionResponse>, TransportError> {
646        if let Some(block) = block {
647            match block.transactions() {
648                BlockTransactions::Full(txs) => {
649                    if let Some(tx) = txs.get(index) {
650                        return Ok(Some(tx.clone()));
651                    }
652                }
653                BlockTransactions::Hashes(hashes) => {
654                    if let Some(tx_hash) = hashes.get(index) {
655                        return self.transaction_by_hash(*tx_hash).await;
656                    }
657                }
658                BlockTransactions::Uncle => {}
659            }
660        }
661        Ok(None)
662    }
663
664    pub async fn transaction_by_hash(
665        &self,
666        hash: B256,
667    ) -> Result<Option<N::TransactionResponse>, TransportError> {
668        trace!(target: "backend::fork", "transaction_by_hash={:?}", hash);
669        if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() {
670            return Ok(tx);
671        }
672
673        let tx = self.provider().get_transaction_by_hash(hash).await?;
674        if let Some(tx) = tx.clone() {
675            let mut storage = self.storage_write();
676            storage.transactions.insert(hash, tx);
677        }
678        Ok(tx)
679    }
680
681    pub async fn block_by_hash(
682        &self,
683        hash: B256,
684    ) -> Result<Option<N::BlockResponse>, TransportError> {
685        if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() {
686            block.transactions_mut().convert_to_hashes();
687            return Ok(Some(block));
688        }
689
690        Ok(self.fetch_full_block(hash).await?.map(|mut b| {
691            b.transactions_mut().convert_to_hashes();
692            b
693        }))
694    }
695
696    pub async fn block_by_hash_full(
697        &self,
698        hash: B256,
699    ) -> Result<Option<N::BlockResponse>, TransportError> {
700        if let Some(block) = self.storage_read().blocks.get(&hash).cloned()
701            && let Some(block) = self.convert_to_full_block(block)
702        {
703            return Ok(Some(block));
704        }
705        self.fetch_full_block(hash).await
706    }
707
708    pub async fn block_by_number(
709        &self,
710        block_number: u64,
711    ) -> Result<Option<N::BlockResponse>, TransportError> {
712        if let Some(mut block) = self
713            .storage_read()
714            .hashes
715            .get(&block_number)
716            .and_then(|hash| self.storage_read().blocks.get(hash).cloned())
717        {
718            block.transactions_mut().convert_to_hashes();
719            return Ok(Some(block));
720        }
721
722        let mut block = self.fetch_full_block(block_number).await?;
723        if let Some(block) = &mut block {
724            block.transactions_mut().convert_to_hashes();
725        }
726        Ok(block)
727    }
728
729    pub async fn block_by_number_full(
730        &self,
731        block_number: u64,
732    ) -> Result<Option<N::BlockResponse>, TransportError> {
733        if let Some(block) = self
734            .storage_read()
735            .hashes
736            .get(&block_number)
737            .copied()
738            .and_then(|hash| self.storage_read().blocks.get(&hash).cloned())
739            && let Some(block) = self.convert_to_full_block(block)
740        {
741            return Ok(Some(block));
742        }
743
744        self.fetch_full_block(block_number).await
745    }
746
747    /// Fetches a block selected by its original identifier directly from the fork provider.
748    pub async fn fetch_block(
749        &self,
750        block_id: BlockId,
751    ) -> Result<Option<N::BlockResponse>, TransportError> {
752        self.fetch_full_block(block_id).await
753    }
754
755    async fn fetch_full_block(
756        &self,
757        block_id: impl Into<BlockId>,
758    ) -> Result<Option<N::BlockResponse>, TransportError> {
759        if let Some(block) = self.provider().get_block(block_id.into()).full().await? {
760            let hash = block.header().hash();
761            let block_number = block.header().number();
762            let mut storage = self.storage_write();
763            // also insert all transactions
764            let block_txs = match block.transactions() {
765                BlockTransactions::Full(txs) => txs.to_owned(),
766                _ => vec![],
767            };
768            storage.transactions.extend(block_txs.iter().map(|tx| (tx.tx_hash(), tx.clone())));
769            storage.hashes.insert(block_number, hash);
770            storage.blocks.insert(hash, block.clone());
771            return Ok(Some(block));
772        }
773
774        Ok(None)
775    }
776
777    /// Converts a block of hashes into a full block
778    fn convert_to_full_block(&self, mut block: N::BlockResponse) -> Option<N::BlockResponse> {
779        let storage = self.storage.read();
780        let transactions = block
781            .transactions()
782            .hashes()
783            .map(|hash| storage.transactions.get(&hash).cloned())
784            .collect::<Option<Vec<_>>>()?;
785        *block.transactions_mut() = BlockTransactions::Full(transactions);
786        Some(block)
787    }
788}
789
790impl ClientFork {
791    pub async fn transaction_receipt(
792        &self,
793        hash: B256,
794    ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
795        if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() {
796            return Ok(Some(receipt));
797        }
798
799        if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? {
800            let receipt = FoundryTxReceipt::try_from(receipt)
801                .map_err(|_| BlockchainError::FailedToDecodeReceipt)?;
802            let mut storage = self.storage_write();
803            storage.transaction_receipts.insert(hash, receipt.clone());
804            return Ok(Some(receipt));
805        }
806
807        Ok(None)
808    }
809
810    pub async fn block_receipts(
811        &self,
812        number: u64,
813    ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
814        if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() {
815            return Ok(receipts);
816        }
817
818        // TODO Needs to be removed.
819        // Since alloy doesn't indicate in the result whether the block exists,
820        // this is being temporarily implemented in anvil.
821        if self.predates_fork_inclusive(number) {
822            let receipts = self.provider().get_block_receipts(BlockId::from(number)).await?;
823            let receipts = receipts
824                .map(|r| {
825                    r.into_iter()
826                        .map(|r| {
827                            FoundryTxReceipt::try_from(r)
828                                .map_err(|_| BlockchainError::FailedToDecodeReceipt)
829                        })
830                        .collect::<Result<Vec<_>, _>>()
831                })
832                .transpose()?;
833
834            if let Some(receipts) = receipts.clone() {
835                let mut storage = self.storage_write();
836                storage.block_receipts.insert(number, receipts);
837            }
838
839            return Ok(receipts);
840        }
841
842        Ok(None)
843    }
844
845    pub async fn uncle_by_block_hash_and_index(
846        &self,
847        hash: B256,
848        index: usize,
849    ) -> Result<Option<AnyRpcBlock>, TransportError> {
850        if let Some(block) = self.block_by_hash(hash).await? {
851            return self.uncles_by_block_and_index(block, index).await;
852        }
853        Ok(None)
854    }
855
856    pub async fn uncle_by_block_number_and_index(
857        &self,
858        number: u64,
859        index: usize,
860    ) -> Result<Option<AnyRpcBlock>, TransportError> {
861        if let Some(block) = self.block_by_number(number).await? {
862            return self.uncles_by_block_and_index(block, index).await;
863        }
864        Ok(None)
865    }
866
867    async fn uncles_by_block_and_index(
868        &self,
869        block: AnyRpcBlock,
870        index: usize,
871    ) -> Result<Option<AnyRpcBlock>, TransportError> {
872        let block_hash = block.header().hash();
873        let block_number = block.header().number();
874        if let Some(uncles) = self.storage_read().uncles.get(&block_hash) {
875            return Ok(uncles.get(index).cloned());
876        }
877
878        let mut uncles = Vec::with_capacity(block.uncles.len());
879        for (uncle_idx, _) in block.uncles.iter().enumerate() {
880            let uncle =
881                match self.provider().get_uncle(block_number.into(), uncle_idx as u64).await? {
882                    Some(u) => u,
883                    None => return Ok(None),
884                };
885            uncles.push(uncle);
886        }
887        self.storage_write().uncles.insert(block_hash, uncles.clone());
888        Ok(uncles.get(index).cloned())
889    }
890}
891
892/// Contains all fork metadata
893#[derive(Clone, Debug)]
894pub struct ClientForkConfig<N: Network = AnyNetwork> {
895    /// All fork URLs. The first entry is the primary endpoint.
896    /// When multiple URLs are present, requests are distributed using
897    /// round-robin load balancing with retry-based failover.
898    pub fork_urls: Vec<String>,
899    /// The block number of the forked block
900    pub block_number: u64,
901    /// The EVM-visible block number of the fork root, which is the L1 number on Arbitrum.
902    pub evm_block_number: u64,
903    /// The hash of the forked block
904    pub block_hash: B256,
905    /// The transaction hash we forked off of, if any.
906    pub transaction_hash: Option<B256>,
907    pub provider: Arc<RetryProvider<N>>,
908    /// Chain ID of the remote fork source.
909    pub chain_id: u64,
910    /// Chain ID exposed by the fork endpoint, including inherited execution overrides.
911    pub execution_chain_id: u64,
912    /// Explicit execution chain ID exposed by the local node.
913    pub override_chain_id: Option<u64>,
914    /// User-provided source chain ID that avoids remote discovery.
915    pub fork_chain_id: Option<u64>,
916    /// The effective hardfork used to execute the forked block.
917    pub hardfork: Option<FoundryHardfork>,
918    /// Stable endpoint identity captured with the fork block.
919    pub(crate) endpoint_identity: ForkEndpointIdentity,
920    /// Discovery identified a local node or could not rule out mutable source state.
921    pub(crate) state_is_mutable: bool,
922    /// The timestamp for the forked block
923    pub timestamp: u64,
924    /// The basefee of the forked block
925    pub base_fee: Option<u128>,
926    /// Blob gas used of the forked block
927    pub blob_gas_used: Option<u128>,
928    /// Blob excess gas and price of the forked block
929    pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
930    /// request timeout
931    pub timeout: Duration,
932    /// request retries for spurious networks
933    pub retries: u32,
934    /// request retries for spurious networks
935    pub backoff: Duration,
936    /// available CUPS
937    pub compute_units_per_second: u64,
938    /// Headers to include with RPC requests
939    pub headers: Vec<String>,
940    /// total difficulty of the chain until this block
941    pub total_difficulty: U256,
942}
943
944impl<N: Network> ClientForkConfig<N> {
945    /// Returns the primary RPC URL (first entry in `fork_urls`).
946    pub fn eth_rpc_url(&self) -> Option<&str> {
947        self.fork_urls.first().map(|s| s.as_str())
948    }
949
950    /// Updates the block forked off `(block number, block hash, timestamp)`
951    pub fn update_block(
952        &mut self,
953        block_number: u64,
954        evm_block_number: u64,
955        block_hash: B256,
956        timestamp: u64,
957        base_fee: Option<u128>,
958        total_difficulty: U256,
959    ) {
960        self.block_number = block_number;
961        self.evm_block_number = evm_block_number;
962        self.block_hash = block_hash;
963        self.timestamp = timestamp;
964        self.base_fee = base_fee;
965        self.total_difficulty = total_difficulty;
966        trace!(target: "fork", "Updated block number={} hash={:?}", block_number, block_hash);
967    }
968}
969
970impl ClientForkConfig {
971    /// Accepts only a single, immutable Ethereum source under Cancun deletion rules.
972    fn bal_eligible(&self) -> bool {
973        let identity = self.endpoint_identity;
974        !self.state_is_mutable
975            && self.fork_urls.len() == 1
976            && !identity.is_authoritative()
977            && identity.network.is_none_or(|network| network.is_ethereum())
978            && matches!(
979                NamedChain::try_from(identity.source_chain_id),
980                Ok(NamedChain::Mainnet
981                    | NamedChain::Sepolia
982                    | NamedChain::Holesky
983                    | NamedChain::Hoodi)
984            )
985            && matches!(
986                FoundryHardfork::from_chain_and_timestamp(identity.source_chain_id, self.timestamp),
987                Some(hardfork @ FoundryHardfork::Ethereum(_)) if SpecId::from(hardfork) >= SpecId::CANCUN
988            )
989    }
990
991    /// Prefills the remote cache before local overrides, without making BAL support mandatory.
992    pub(crate) async fn prefill_cache(&self, db: &BlockchainDb) {
993        if !self.bal_eligible() || db.meta().read().fork_hash != Some(self.block_hash) {
994            return;
995        }
996
997        let prefill = async {
998            let Some(bal) =
999                self.provider.get_block_access_list(BlockId::hash(self.block_hash)).await?
1000            else {
1001                return Ok(());
1002            };
1003            let Some(block) = self.provider.get_block(BlockId::hash(self.block_hash)).await? else {
1004                return Ok(());
1005            };
1006            eyre::ensure!(block.header.hash == self.block_hash, "fork block hash mismatch");
1007            validate_bal(&bal, block.transactions.len(), block.header.block_access_list_hash())?;
1008
1009            // Anvil can mutate state without changing the block hash. Discard the BAL if
1010            // the source became local or its identity is now inconclusive.
1011            match self
1012                .provider
1013                .raw_request::<_, serde_json::Value>("anvil_nodeInfo".into(), ())
1014                .await
1015            {
1016                Err(error) if is_rpc_method_not_found(&error) => {}
1017                _ => return Ok(()),
1018            }
1019            cache_bal(db.db(), bal);
1020            Ok::<_, eyre::Report>(())
1021        };
1022        // Include retries and validation RPCs in the optional startup budget.
1023        match tokio::time::timeout(Duration::from_millis(500), prefill).await {
1024            Ok(Ok(())) => {}
1025            Ok(Err(_)) => debug!(target: "node", "fork BAL prefill unavailable"),
1026            Err(_) => debug!(target: "node", "fork BAL prefill timed out"),
1027        }
1028    }
1029}
1030
1031#[cfg(test)]
1032mod bal_tests;
1033
1034/// Contains cached state fetched to serve EthApi requests
1035///
1036/// This is used as a cache so repeated requests to the same data are not sent to the remote client
1037#[derive(Clone, Debug)]
1038pub struct ForkedStorage<N: Network = AnyNetwork> {
1039    pub uncles: FbHashMap<32, Vec<N::BlockResponse>>,
1040    pub blocks: FbHashMap<32, N::BlockResponse>,
1041    pub hashes: HashMap<u64, B256>,
1042    pub transactions: FbHashMap<32, N::TransactionResponse>,
1043    pub transaction_receipts: FbHashMap<32, FoundryTxReceipt>,
1044    pub transaction_traces: FbHashMap<32, Vec<Trace>>,
1045    pub logs: HashMap<LogsCacheKey, Vec<Log>>,
1046    pub geth_transaction_traces: FbHashMap<32, Vec<(GethDebugTracingOptions, GethTrace)>>,
1047    pub geth_block_traces: FbHashMap<32, Vec<(GethDebugTracingOptions, Vec<TraceResult>)>>,
1048    pub block_traces: HashMap<u64, Vec<Trace>>,
1049    pub block_receipts: HashMap<u64, Vec<FoundryTxReceipt>>,
1050    pub code_at: HashMap<(Address, u64), Bytes>,
1051}
1052
1053impl<N: Network> Default for ForkedStorage<N> {
1054    fn default() -> Self {
1055        Self {
1056            uncles: Default::default(),
1057            blocks: Default::default(),
1058            hashes: Default::default(),
1059            transactions: Default::default(),
1060            transaction_receipts: Default::default(),
1061            transaction_traces: Default::default(),
1062            logs: Default::default(),
1063            geth_transaction_traces: Default::default(),
1064            geth_block_traces: Default::default(),
1065            block_traces: Default::default(),
1066            block_receipts: Default::default(),
1067            code_at: Default::default(),
1068        }
1069    }
1070}
1071
1072impl<N: Network> ForkedStorage<N> {
1073    /// Clears all data
1074    pub fn clear(&mut self) {
1075        // simply replace with a completely new, empty instance
1076        *self = Self::default()
1077    }
1078}
1079
1080/// Cache key for a log [`Filter`].
1081///
1082/// [`Filter`] is not hashable because its address and topic sets iterate in arbitrary order, so
1083/// the key stores them sorted.
1084#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1085pub struct LogsCacheKey {
1086    block_option: FilterBlockOption,
1087    address: Vec<Address>,
1088    topics: [Vec<B256>; 4],
1089}
1090
1091impl From<&Filter> for LogsCacheKey {
1092    fn from(filter: &Filter) -> Self {
1093        Self {
1094            block_option: filter.block_option,
1095            address: sorted_filter_set(&filter.address),
1096            topics: filter.topics.each_ref().map(sorted_filter_set),
1097        }
1098    }
1099}
1100
1101fn sorted_filter_set<T: Copy + Ord + std::hash::Hash>(set: &FilterSet<T>) -> Vec<T> {
1102    let mut values = set.iter().copied().collect::<Vec<_>>();
1103    values.sort_unstable();
1104    values
1105}