1use 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 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 pub(crate) const fn is_authoritative(self) -> bool {
71 self.hardfork.is_some()
72 }
73
74 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
87pub(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#[derive(Clone, Debug)]
106pub struct ClientFork<N: Network = AnyNetwork> {
107 pub storage: Arc<RwLock<ForkedStorage<N>>>,
109 pub config: Arc<RwLock<ClientForkConfig<N>>>,
113 pub database: Arc<AsyncRwLock<Box<dyn Db>>>,
115}
116
117impl<N: Network> ClientFork<N> {
118 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 pub fn predates_fork(&self, block: u64) -> bool {
125 block < self.block_number()
126 }
127
128 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let Some(block) = provider.get_block(BlockId::hash(hash)).full().await? else {
909 return Ok(None);
910 };
911 block
912 };
913
914 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#[derive(Clone, Debug)]
933pub struct ClientForkConfig<N: Network = AnyNetwork> {
934 pub fork_urls: Vec<String>,
938 pub block_number: u64,
940 pub evm_block_number: u64,
942 pub block_hash: B256,
944 pub transaction_hash: Option<B256>,
946 pub provider: Arc<RetryProvider<N>>,
947 pub chain_id: u64,
949 pub execution_chain_id: u64,
951 pub override_chain_id: Option<u64>,
953 pub fork_chain_id: Option<u64>,
955 pub hardfork: Option<FoundryHardfork>,
957 pub(crate) endpoint_identity: ForkEndpointIdentity,
959 pub(crate) state_is_mutable: bool,
961 pub timestamp: u64,
963 pub base_fee: Option<u128>,
965 pub blob_gas_used: Option<u128>,
967 pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
969 pub timeout: Duration,
971 pub retries: u32,
973 pub backoff: Duration,
975 pub compute_units_per_second: u64,
977 pub headers: Vec<String>,
979 pub total_difficulty: U256,
981}
982
983impl<N: Network> ClientForkConfig<N> {
984 pub fn eth_rpc_url(&self) -> Option<&str> {
986 self.fork_urls.first().map(|s| s.as_str())
987 }
988
989 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 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 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 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 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#[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 pub fn clear(&mut self) {
1114 *self = Self::default()
1116 }
1117}
1118
1119#[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}