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