1use crate::eth::{backend::db::Db, error::BlockchainError, pool::transactions::PoolTransaction};
4use alloy_consensus::{BlockHeader, TrieAccount};
5use alloy_eips::eip2930::AccessListResult;
6use alloy_network::{
7 AnyNetwork, AnyRpcBlock, BlockResponse, Network, TransactionResponse,
8 primitives::HeaderResponse,
9};
10use alloy_primitives::{
11 Address, B256, Bytes, StorageValue, U256,
12 map::{FbHashMap, HashMap, HashSet},
13};
14use alloy_provider::{
15 Provider,
16 ext::{DebugApi, TraceApi},
17};
18use alloy_rpc_types::{
19 BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse,
20 FeeHistory, Filter, Log,
21 simulate::{SimulatePayload, SimulatedBlock},
22 trace::{
23 geth::{GethDebugTracingOptions, GethTrace},
24 parity::{LocalizedTransactionTrace as Trace, TraceResultsWithTransactionHash, TraceType},
25 },
26};
27use alloy_transport::TransportError;
28use foundry_common::provider::{ProviderBuilder, RetryProvider};
29use foundry_primitives::{FoundryTxEnvelope, FoundryTxReceipt};
30use parking_lot::{
31 RawRwLock, RwLock,
32 lock_api::{RwLockReadGuard, RwLockWriteGuard},
33};
34use revm::context_interface::block::BlobExcessGasAndPrice;
35use std::{sync::Arc, time::Duration};
36use tokio::sync::RwLock as AsyncRwLock;
37
38#[derive(Clone, Debug)]
43pub struct ClientFork<N: Network = AnyNetwork> {
44 pub storage: Arc<RwLock<ForkedStorage<N>>>,
46 pub config: Arc<RwLock<ClientForkConfig<N>>>,
50 pub database: Arc<AsyncRwLock<Box<dyn Db>>>,
52}
53
54impl<N: Network> ClientFork<N> {
55 pub fn new(config: ClientForkConfig<N>, database: Arc<AsyncRwLock<Box<dyn Db>>>) -> Self {
57 Self { storage: Default::default(), config: Arc::new(RwLock::new(config)), database }
58 }
59
60 pub fn clear_cached_storage(&self) {
62 self.storage.write().clear()
63 }
64
65 pub fn predates_fork(&self, block: u64) -> bool {
67 block < self.block_number()
68 }
69
70 pub fn predates_fork_inclusive(&self, block: u64) -> bool {
72 block <= self.block_number()
73 }
74
75 pub fn timestamp(&self) -> u64 {
76 self.config.read().timestamp
77 }
78
79 pub fn block_number(&self) -> u64 {
80 self.config.read().block_number
81 }
82
83 pub fn transaction_hash(&self) -> Option<B256> {
85 self.config.read().transaction_hash
86 }
87
88 pub fn total_difficulty(&self) -> U256 {
89 self.config.read().total_difficulty
90 }
91
92 pub fn base_fee(&self) -> Option<u128> {
93 self.config.read().base_fee
94 }
95
96 pub fn block_hash(&self) -> B256 {
97 self.config.read().block_hash
98 }
99
100 pub fn eth_rpc_url(&self) -> String {
101 self.config.read().eth_rpc_url.clone()
102 }
103
104 pub fn chain_id(&self) -> u64 {
105 self.config.read().chain_id
106 }
107
108 fn provider(&self) -> Arc<RetryProvider<N>> {
109 self.config.read().provider.clone()
110 }
111
112 fn storage_read(&self) -> RwLockReadGuard<'_, RawRwLock, ForkedStorage<N>> {
113 self.storage.read()
114 }
115
116 fn storage_write(&self) -> RwLockWriteGuard<'_, RawRwLock, ForkedStorage<N>> {
117 self.storage.write()
118 }
119
120 pub async fn fee_history(
122 &self,
123 block_count: u64,
124 newest_block: BlockNumber,
125 reward_percentiles: &[f64],
126 ) -> Result<FeeHistory, TransportError> {
127 self.provider().get_fee_history(block_count, newest_block, reward_percentiles).await
128 }
129
130 pub async fn get_proof(
132 &self,
133 address: Address,
134 keys: Vec<B256>,
135 block_number: Option<BlockId>,
136 ) -> Result<EIP1186AccountProofResponse, TransportError> {
137 self.provider().get_proof(address, keys).block_id(block_number.unwrap_or_default()).await
138 }
139
140 pub async fn storage_at(
141 &self,
142 address: Address,
143 index: U256,
144 number: Option<BlockNumber>,
145 ) -> Result<StorageValue, TransportError> {
146 self.provider()
147 .get_storage_at(address, index)
148 .block_id(number.unwrap_or_default().into())
149 .await
150 }
151
152 pub async fn logs(&self, filter: &Filter) -> Result<Vec<Log>, TransportError> {
153 if let Some(logs) = self.storage_read().logs.get(filter).cloned() {
154 return Ok(logs);
155 }
156
157 let logs = self.provider().get_logs(filter).await?;
158
159 let mut storage = self.storage_write();
160 storage.logs.insert(filter.clone(), logs.clone());
161 Ok(logs)
162 }
163
164 pub async fn get_code(
165 &self,
166 address: Address,
167 blocknumber: u64,
168 ) -> Result<Bytes, TransportError> {
169 trace!(target: "backend::fork", "get_code={:?}", address);
170 if let Some(code) = self.storage_read().code_at.get(&(address, blocknumber)).cloned() {
171 return Ok(code);
172 }
173
174 let block_id = BlockId::number(blocknumber);
175
176 let code = self.provider().get_code_at(address).block_id(block_id).await?;
177
178 let mut storage = self.storage_write();
179 storage.code_at.insert((address, blocknumber), code.clone());
180
181 Ok(code)
182 }
183
184 pub async fn get_balance(
185 &self,
186 address: Address,
187 blocknumber: u64,
188 ) -> Result<U256, TransportError> {
189 trace!(target: "backend::fork", "get_balance={:?}", address);
190 self.provider().get_balance(address).block_id(blocknumber.into()).await
191 }
192
193 pub async fn get_nonce(&self, address: Address, block: u64) -> Result<u64, TransportError> {
194 trace!(target: "backend::fork", "get_nonce={:?}", address);
195 self.provider().get_transaction_count(address).block_id(block.into()).await
196 }
197
198 pub async fn get_account(
199 &self,
200 address: Address,
201 blocknumber: u64,
202 ) -> Result<TrieAccount, TransportError> {
203 trace!(target: "backend::fork", "get_account={:?}", address);
204 self.provider().get_account(address).block_id(blocknumber.into()).await
205 }
206
207 pub async fn trace_transaction(&self, hash: B256) -> Result<Vec<Trace>, TransportError> {
208 if let Some(traces) = self.storage_read().transaction_traces.get(&hash).cloned() {
209 return Ok(traces);
210 }
211
212 let traces = self.provider().trace_transaction(hash).await?.into_iter().collect::<Vec<_>>();
213
214 let mut storage = self.storage_write();
215 storage.transaction_traces.insert(hash, traces.clone());
216
217 Ok(traces)
218 }
219
220 pub async fn debug_trace_transaction(
221 &self,
222 hash: B256,
223 opts: GethDebugTracingOptions,
224 ) -> Result<GethTrace, TransportError> {
225 if let Some(traces) = self.storage_read().geth_transaction_traces.get(&hash).cloned() {
226 return Ok(traces);
227 }
228
229 let trace = self.provider().debug_trace_transaction(hash, opts).await?;
230
231 let mut storage = self.storage_write();
232 storage.geth_transaction_traces.insert(hash, trace.clone());
233
234 Ok(trace)
235 }
236
237 pub async fn debug_code_by_hash(
238 &self,
239 code_hash: B256,
240 block_id: Option<BlockId>,
241 ) -> Result<Option<Bytes>, TransportError> {
242 self.provider().debug_code_by_hash(code_hash, block_id).await
243 }
244
245 pub async fn trace_block(&self, number: u64) -> Result<Vec<Trace>, TransportError> {
246 if let Some(traces) = self.storage_read().block_traces.get(&number).cloned() {
247 return Ok(traces);
248 }
249
250 let traces =
251 self.provider().trace_block(number.into()).await?.into_iter().collect::<Vec<_>>();
252
253 let mut storage = self.storage_write();
254 storage.block_traces.insert(number, traces.clone());
255
256 Ok(traces)
257 }
258
259 pub async fn trace_replay_block_transactions(
260 &self,
261 number: u64,
262 trace_types: HashSet<TraceType>,
263 ) -> Result<Vec<TraceResultsWithTransactionHash>, TransportError> {
264 let params = (number, trace_types.iter().map(|t| format!("{t:?}")).collect::<Vec<_>>());
266 self.provider().raw_request("trace_replayBlockTransactions".into(), params).await
267 }
268
269 pub async fn reset(
271 &self,
272 url: Option<String>,
273 block_number: impl Into<BlockId>,
274 ) -> Result<(), BlockchainError> {
275 let block_number = block_number.into();
276 {
277 self.database
278 .write()
279 .await
280 .maybe_reset(url.clone(), block_number)
281 .map_err(BlockchainError::Internal)?;
282 }
283
284 if let Some(url) = url {
285 self.config.write().update_url(url)?;
286 let override_chain_id = self.config.read().override_chain_id;
287 let chain_id = if let Some(chain_id) = override_chain_id {
288 chain_id
289 } else {
290 self.provider().get_chain_id().await?
291 };
292 self.config.write().chain_id = chain_id;
293 }
294
295 let provider = self.provider();
296 let block =
297 provider.get_block(block_number).await?.ok_or(BlockchainError::BlockNotFound)?;
298 let block_hash = block.header().hash();
299 let timestamp = block.header().timestamp();
300 let base_fee = block.header().base_fee_per_gas();
301 let total_difficulty = block.header().difficulty();
302
303 let number = block.header().number();
304 self.config.write().update_block(
305 number,
306 block_hash,
307 timestamp,
308 base_fee.map(|g| g as u128),
309 total_difficulty,
310 );
311
312 self.clear_cached_storage();
313
314 self.database.write().await.insert_block_hash(U256::from(number), block_hash);
315
316 Ok(())
317 }
318
319 pub async fn call(
321 &self,
322 request: &N::TransactionRequest,
323 block: Option<BlockNumber>,
324 ) -> Result<Bytes, TransportError> {
325 let block = block.unwrap_or(BlockNumber::Latest);
326 let res = self.provider().call(request.clone()).block(block.into()).await?;
327
328 Ok(res)
329 }
330
331 pub async fn simulate_v1(
333 &self,
334 request: &SimulatePayload,
335 block: Option<BlockNumber>,
336 ) -> Result<Vec<SimulatedBlock<N::BlockResponse>>, TransportError> {
337 let mut simulate_call = self.provider().simulate(request);
338 if let Some(n) = block {
339 simulate_call = simulate_call.number(n.as_number().unwrap());
340 }
341
342 let res = simulate_call.await?;
343
344 Ok(res)
345 }
346
347 pub async fn estimate_gas(
349 &self,
350 request: &N::TransactionRequest,
351 block: Option<BlockNumber>,
352 ) -> Result<u128, TransportError> {
353 let block = block.unwrap_or_default();
354 let res = self.provider().estimate_gas(request.clone()).block(block.into()).await?;
355
356 Ok(res as u128)
357 }
358
359 pub async fn create_access_list(
361 &self,
362 request: &N::TransactionRequest,
363 block: Option<BlockNumber>,
364 ) -> Result<AccessListResult, TransportError> {
365 self.provider().create_access_list(request).block_id(block.unwrap_or_default().into()).await
366 }
367
368 pub async fn transaction_by_block_number_and_index(
369 &self,
370 number: u64,
371 index: usize,
372 ) -> Result<Option<N::TransactionResponse>, TransportError> {
373 if let Some(block) = self.block_by_number(number).await? {
374 #[allow(clippy::collapsible_match)]
375 match block.transactions() {
376 BlockTransactions::Full(txs) => {
377 if let Some(tx) = txs.get(index) {
378 return Ok(Some(tx.clone()));
379 }
380 }
381 BlockTransactions::Hashes(hashes) => {
382 if let Some(tx_hash) = hashes.get(index) {
383 return self.transaction_by_hash(*tx_hash).await;
384 }
385 }
386 BlockTransactions::Uncle => panic!("Uncles not supported"),
388 }
389 }
390 Ok(None)
391 }
392
393 pub async fn transaction_by_block_hash_and_index(
394 &self,
395 hash: B256,
396 index: usize,
397 ) -> Result<Option<N::TransactionResponse>, TransportError> {
398 if let Some(block) = self.block_by_hash(hash).await? {
399 #[allow(clippy::collapsible_match)]
400 match block.transactions() {
401 BlockTransactions::Full(txs) => {
402 if let Some(tx) = txs.get(index) {
403 return Ok(Some(tx.clone()));
404 }
405 }
406 BlockTransactions::Hashes(hashes) => {
407 if let Some(tx_hash) = hashes.get(index) {
408 return self.transaction_by_hash(*tx_hash).await;
409 }
410 }
411 BlockTransactions::Uncle => panic!("Uncles not supported"),
413 }
414 }
415 Ok(None)
416 }
417
418 pub async fn transaction_by_hash(
419 &self,
420 hash: B256,
421 ) -> Result<Option<N::TransactionResponse>, TransportError> {
422 trace!(target: "backend::fork", "transaction_by_hash={:?}", hash);
423 if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() {
424 return Ok(tx);
425 }
426
427 let tx = self.provider().get_transaction_by_hash(hash).await?;
428 if let Some(tx) = tx.clone() {
429 let mut storage = self.storage_write();
430 storage.transactions.insert(hash, tx);
431 }
432 Ok(tx)
433 }
434
435 pub async fn block_by_hash(
436 &self,
437 hash: B256,
438 ) -> Result<Option<N::BlockResponse>, TransportError> {
439 if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() {
440 block.transactions_mut().convert_to_hashes();
441 return Ok(Some(block));
442 }
443
444 Ok(self.fetch_full_block(hash).await?.map(|mut b| {
445 b.transactions_mut().convert_to_hashes();
446 b
447 }))
448 }
449
450 pub async fn block_by_hash_full(
451 &self,
452 hash: B256,
453 ) -> Result<Option<N::BlockResponse>, TransportError> {
454 if let Some(block) = self.storage_read().blocks.get(&hash).cloned() {
455 return Ok(Some(self.convert_to_full_block(block)));
456 }
457 self.fetch_full_block(hash).await
458 }
459
460 pub async fn block_by_number(
461 &self,
462 block_number: u64,
463 ) -> Result<Option<N::BlockResponse>, TransportError> {
464 if let Some(mut block) = self
465 .storage_read()
466 .hashes
467 .get(&block_number)
468 .and_then(|hash| self.storage_read().blocks.get(hash).cloned())
469 {
470 block.transactions_mut().convert_to_hashes();
471 return Ok(Some(block));
472 }
473
474 let mut block = self.fetch_full_block(block_number).await?;
475 if let Some(block) = &mut block {
476 block.transactions_mut().convert_to_hashes();
477 }
478 Ok(block)
479 }
480
481 pub async fn block_by_number_full(
482 &self,
483 block_number: u64,
484 ) -> Result<Option<N::BlockResponse>, TransportError> {
485 if let Some(block) = self
486 .storage_read()
487 .hashes
488 .get(&block_number)
489 .copied()
490 .and_then(|hash| self.storage_read().blocks.get(&hash).cloned())
491 {
492 return Ok(Some(self.convert_to_full_block(block)));
493 }
494
495 self.fetch_full_block(block_number).await
496 }
497
498 async fn fetch_full_block(
499 &self,
500 block_id: impl Into<BlockId>,
501 ) -> Result<Option<N::BlockResponse>, TransportError> {
502 if let Some(block) = self.provider().get_block(block_id.into()).full().await? {
503 let hash = block.header().hash();
504 let block_number = block.header().number();
505 let mut storage = self.storage_write();
506 let block_txs = match block.transactions() {
508 BlockTransactions::Full(txs) => txs.to_owned(),
509 _ => vec![],
510 };
511 storage.transactions.extend(block_txs.iter().map(|tx| (tx.tx_hash(), tx.clone())));
512 storage.hashes.insert(block_number, hash);
513 storage.blocks.insert(hash, block.clone());
514 return Ok(Some(block));
515 }
516
517 Ok(None)
518 }
519
520 fn convert_to_full_block(&self, mut block: N::BlockResponse) -> N::BlockResponse {
522 let storage = self.storage.read();
523 let transactions = block
524 .transactions()
525 .hashes()
526 .filter_map(|hash| storage.transactions.get(&hash).cloned())
527 .collect();
528 *block.transactions_mut() = BlockTransactions::Full(transactions);
529 block
530 }
531}
532
533impl ClientFork {
534 pub async fn transaction_receipt(
535 &self,
536 hash: B256,
537 ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
538 if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() {
539 return Ok(Some(receipt));
540 }
541
542 if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? {
543 let receipt = FoundryTxReceipt::try_from(receipt)
544 .map_err(|_| BlockchainError::FailedToDecodeReceipt)?;
545 let mut storage = self.storage_write();
546 storage.transaction_receipts.insert(hash, receipt.clone());
547 return Ok(Some(receipt));
548 }
549
550 Ok(None)
551 }
552
553 pub async fn block_receipts(
554 &self,
555 number: u64,
556 ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
557 if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() {
558 return Ok(receipts);
559 }
560
561 if self.predates_fork_inclusive(number) {
565 let receipts = self.provider().get_block_receipts(BlockId::from(number)).await?;
566 let receipts = receipts
567 .map(|r| {
568 r.into_iter()
569 .map(|r| {
570 FoundryTxReceipt::try_from(r)
571 .map_err(|_| BlockchainError::FailedToDecodeReceipt)
572 })
573 .collect::<Result<Vec<_>, _>>()
574 })
575 .transpose()?;
576
577 if let Some(receipts) = receipts.clone() {
578 let mut storage = self.storage_write();
579 storage.block_receipts.insert(number, receipts);
580 }
581
582 return Ok(receipts);
583 }
584
585 Ok(None)
586 }
587
588 pub async fn uncle_by_block_hash_and_index(
589 &self,
590 hash: B256,
591 index: usize,
592 ) -> Result<Option<AnyRpcBlock>, TransportError> {
593 if let Some(block) = self.block_by_hash(hash).await? {
594 return self.uncles_by_block_and_index(block, index).await;
595 }
596 Ok(None)
597 }
598
599 pub async fn uncle_by_block_number_and_index(
600 &self,
601 number: u64,
602 index: usize,
603 ) -> Result<Option<AnyRpcBlock>, TransportError> {
604 if let Some(block) = self.block_by_number(number).await? {
605 return self.uncles_by_block_and_index(block, index).await;
606 }
607 Ok(None)
608 }
609
610 async fn uncles_by_block_and_index(
611 &self,
612 block: AnyRpcBlock,
613 index: usize,
614 ) -> Result<Option<AnyRpcBlock>, TransportError> {
615 let block_hash = block.header().hash();
616 let block_number = block.header().number();
617 if let Some(uncles) = self.storage_read().uncles.get(&block_hash) {
618 return Ok(uncles.get(index).cloned());
619 }
620
621 let mut uncles = Vec::with_capacity(block.uncles.len());
622 for (uncle_idx, _) in block.uncles.iter().enumerate() {
623 let uncle =
624 match self.provider().get_uncle(block_number.into(), uncle_idx as u64).await? {
625 Some(u) => u,
626 None => return Ok(None),
627 };
628 uncles.push(uncle);
629 }
630 self.storage_write().uncles.insert(block_hash, uncles.clone());
631 Ok(uncles.get(index).cloned())
632 }
633}
634
635#[derive(Clone, Debug)]
637pub struct ClientForkConfig<N: Network = AnyNetwork> {
638 pub eth_rpc_url: String,
639 pub block_number: u64,
641 pub block_hash: B256,
643 pub transaction_hash: Option<B256>,
645 pub provider: Arc<RetryProvider<N>>,
646 pub chain_id: u64,
647 pub override_chain_id: Option<u64>,
648 pub timestamp: u64,
650 pub base_fee: Option<u128>,
652 pub blob_gas_used: Option<u128>,
654 pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
656 pub timeout: Duration,
658 pub retries: u32,
660 pub backoff: Duration,
662 pub compute_units_per_second: u64,
664 pub total_difficulty: U256,
666 pub force_transactions: Option<Vec<PoolTransaction<FoundryTxEnvelope>>>,
668}
669
670impl<N: Network> ClientForkConfig<N> {
671 fn update_url(&mut self, url: String) -> Result<(), BlockchainError> {
677 self.provider = Arc::new(
679 ProviderBuilder::<N>::new(url.as_str())
680 .timeout(self.timeout)
681 .max_retry(self.retries)
683 .initial_backoff(self.backoff.as_millis() as u64)
684 .compute_units_per_second(self.compute_units_per_second)
685 .build()
686 .map_err(|e| BlockchainError::InvalidUrl(format!("{url}: {e}")))?, );
688 trace!(target: "fork", "Updated rpc url {}", url);
689 self.eth_rpc_url = url;
690 Ok(())
691 }
692 pub fn update_block(
694 &mut self,
695 block_number: u64,
696 block_hash: B256,
697 timestamp: u64,
698 base_fee: Option<u128>,
699 total_difficulty: U256,
700 ) {
701 self.block_number = block_number;
702 self.block_hash = block_hash;
703 self.timestamp = timestamp;
704 self.base_fee = base_fee;
705 self.total_difficulty = total_difficulty;
706 trace!(target: "fork", "Updated block number={} hash={:?}", block_number, block_hash);
707 }
708}
709
710#[derive(Clone, Debug)]
714pub struct ForkedStorage<N: Network = AnyNetwork> {
715 pub uncles: FbHashMap<32, Vec<N::BlockResponse>>,
716 pub blocks: FbHashMap<32, N::BlockResponse>,
717 pub hashes: HashMap<u64, B256>,
718 pub transactions: FbHashMap<32, N::TransactionResponse>,
719 pub transaction_receipts: FbHashMap<32, FoundryTxReceipt>,
720 pub transaction_traces: FbHashMap<32, Vec<Trace>>,
721 pub logs: HashMap<Filter, Vec<Log>>,
722 pub geth_transaction_traces: FbHashMap<32, GethTrace>,
723 pub block_traces: HashMap<u64, Vec<Trace>>,
724 pub block_receipts: HashMap<u64, Vec<FoundryTxReceipt>>,
725 pub code_at: HashMap<(Address, u64), Bytes>,
726}
727
728impl<N: Network> Default for ForkedStorage<N> {
729 fn default() -> Self {
730 Self {
731 uncles: Default::default(),
732 blocks: Default::default(),
733 hashes: Default::default(),
734 transactions: Default::default(),
735 transaction_receipts: Default::default(),
736 transaction_traces: Default::default(),
737 logs: Default::default(),
738 geth_transaction_traces: Default::default(),
739 block_traces: Default::default(),
740 block_receipts: Default::default(),
741 code_at: Default::default(),
742 }
743 }
744}
745
746impl<N: Network> ForkedStorage<N> {
747 pub fn clear(&mut self) {
749 *self = Self::default()
751 }
752}