1use crate::{
4 AsEnvMut, Env, EnvMut, InspectorExt,
5 constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS},
6 fork::{CreateFork, ForkId, MultiFork},
7 state_snapshot::StateSnapshots,
8 utils::{configure_tx_env, get_blob_base_fee_update_fraction_by_spec_id},
9};
10use alloy_consensus::Typed2718;
11use alloy_evm::Evm;
12use alloy_genesis::GenesisAccount;
13use alloy_network::{AnyRpcBlock, AnyTxEnvelope, TransactionResponse};
14use alloy_primitives::{Address, B256, TxKind, U256, keccak256, uint};
15use alloy_rpc_types::{BlockNumberOrTag, Transaction, TransactionRequest};
16use eyre::Context;
17use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender};
18pub use foundry_fork_db::{BlockchainDb, SharedBackend, cache::BlockchainDbMeta};
19use revm::{
20 Database, DatabaseCommit, JournalEntry,
21 bytecode::Bytecode,
22 context::JournalInner,
23 context_interface::{block::BlobExcessGasAndPrice, result::ResultAndState},
24 database::{CacheDB, DatabaseRef},
25 inspector::NoOpInspector,
26 precompile::{PrecompileSpecId, Precompiles},
27 primitives::{HashMap as Map, KECCAK_EMPTY, Log, hardfork::SpecId},
28 state::{Account, AccountInfo, EvmState, EvmStorageSlot},
29};
30use std::{
31 any::Any,
32 collections::{BTreeMap, HashMap, HashSet},
33 fmt::Debug,
34 time::Instant,
35};
36
37mod diagnostic;
38pub use diagnostic::RevertDiagnostic;
39
40mod error;
41pub use error::{BackendError, BackendResult, DatabaseError, DatabaseResult};
42
43mod cow;
44pub use cow::CowBackend;
45
46mod in_memory_db;
47pub use in_memory_db::{EmptyDBWrapper, FoundryEvmInMemoryDB, MemDb};
48
49mod snapshot;
50pub use snapshot::{BackendStateSnapshot, RevertStateSnapshotAction, StateSnapshot};
51
52mod strategy;
53pub use strategy::{
54 BackendStrategy, BackendStrategyContext, BackendStrategyRunner, EvmBackendStrategyRunner,
55};
56
57pub type ForkDB = CacheDB<SharedBackend>;
59
60pub type LocalForkId = U256;
65
66type ForkLookupIndex = usize;
69
70const DEFAULT_PERSISTENT_ACCOUNTS: [Address; 3] =
72 [CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, CALLER];
73
74pub const GLOBAL_FAIL_SLOT: U256 =
79 uint!(0x6661696c65640000000000000000000000000000000000000000000000000000_U256);
80
81pub type JournaledState = JournalInner<JournalEntry>;
82
83#[auto_impl::auto_impl(&mut)]
85pub trait DatabaseExt: Database<Error = DatabaseError> + DatabaseCommit + Debug {
86 fn get_strategy(&mut self) -> &mut BackendStrategy;
88
89 fn snapshot_state(&mut self, journaled_state: &JournaledState, env: &mut EnvMut<'_>) -> U256;
95
96 fn revert_state(
109 &mut self,
110 id: U256,
111 journaled_state: &JournaledState,
112 env: &mut EnvMut<'_>,
113 action: RevertStateSnapshotAction,
114 ) -> Option<JournaledState>;
115
116 fn delete_state_snapshot(&mut self, id: U256) -> bool;
121
122 fn delete_state_snapshots(&mut self);
124
125 fn create_select_fork(
129 &mut self,
130 fork: CreateFork,
131 env: &mut EnvMut<'_>,
132 journaled_state: &mut JournaledState,
133 ) -> eyre::Result<LocalForkId> {
134 let id = self.create_fork(fork)?;
135 self.select_fork(id, env, journaled_state)?;
136 Ok(id)
137 }
138
139 fn create_select_fork_at_transaction(
143 &mut self,
144 fork: CreateFork,
145 env: &mut EnvMut<'_>,
146 journaled_state: &mut JournaledState,
147 transaction: B256,
148 ) -> eyre::Result<LocalForkId> {
149 let id = self.create_fork_at_transaction(fork, transaction)?;
150 self.select_fork(id, env, journaled_state)?;
151 Ok(id)
152 }
153
154 fn create_fork(&mut self, fork: CreateFork) -> eyre::Result<LocalForkId>;
156
157 fn create_fork_at_transaction(
159 &mut self,
160 fork: CreateFork,
161 transaction: B256,
162 ) -> eyre::Result<LocalForkId>;
163
164 fn select_fork(
174 &mut self,
175 id: LocalForkId,
176 env: &mut EnvMut<'_>,
177 journaled_state: &mut JournaledState,
178 ) -> eyre::Result<()>;
179
180 fn roll_fork(
188 &mut self,
189 id: Option<LocalForkId>,
190 block_number: u64,
191 env: &mut EnvMut<'_>,
192 journaled_state: &mut JournaledState,
193 ) -> eyre::Result<()>;
194
195 fn roll_fork_to_transaction(
204 &mut self,
205 id: Option<LocalForkId>,
206 transaction: B256,
207 env: &mut EnvMut<'_>,
208 journaled_state: &mut JournaledState,
209 ) -> eyre::Result<()>;
210
211 fn transact(
213 &mut self,
214 id: Option<LocalForkId>,
215 transaction: B256,
216 env: Env,
217 journaled_state: &mut JournaledState,
218 inspector: &mut dyn InspectorExt,
219 ) -> eyre::Result<()>;
220
221 fn transact_from_tx(
223 &mut self,
224 transaction: &TransactionRequest,
225 env: Env,
226 journaled_state: &mut JournaledState,
227 inspector: &mut dyn InspectorExt,
228 inspect_ctx: Box<dyn Any>,
229 ) -> eyre::Result<()>;
230
231 fn active_fork_id(&self) -> Option<LocalForkId>;
233
234 fn active_fork_url(&self) -> Option<String>;
236
237 fn is_forked_mode(&self) -> bool {
239 self.active_fork_id().is_some()
240 }
241
242 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId>;
253
254 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId>;
256
257 fn diagnose_revert(
284 &self,
285 callee: Address,
286 journaled_state: &JournaledState,
287 ) -> Option<RevertDiagnostic>;
288
289 fn load_allocs(
293 &mut self,
294 allocs: &BTreeMap<Address, GenesisAccount>,
295 journaled_state: &mut JournaledState,
296 ) -> Result<(), BackendError>;
297
298 fn clone_account(
303 &mut self,
304 source: &GenesisAccount,
305 target: &Address,
306 journaled_state: &mut JournaledState,
307 ) -> Result<(), BackendError>;
308
309 fn is_persistent(&self, acc: &Address) -> bool;
311
312 fn remove_persistent_account(&mut self, account: &Address) -> bool;
314
315 fn add_persistent_account(&mut self, account: Address) -> bool;
317
318 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
320 fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
321 where
322 Self: Sized,
323 {
324 for acc in accounts {
325 self.remove_persistent_account(&acc);
326 }
327 }
328
329 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
331 fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
332 where
333 Self: Sized,
334 {
335 for acc in accounts {
336 self.add_persistent_account(acc);
337 }
338 }
339
340 fn persistent_accounts(&self) -> &HashSet<Address>;
342
343 fn allow_cheatcode_access(&mut self, account: Address) -> bool;
347
348 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool;
352
353 fn has_cheatcode_access(&self, account: &Address) -> bool;
355
356 fn cached_accounts(&self) -> Vec<Address>;
358
359 fn cached_storage(&self, address: Address) -> Option<Map<U256, U256>>;
363
364 fn ensure_cheatcode_access(&self, account: &Address) -> Result<(), BackendError> {
368 if !self.has_cheatcode_access(account) {
369 return Err(BackendError::NoCheats(*account));
370 }
371 Ok(())
372 }
373
374 fn ensure_cheatcode_access_forking_mode(&self, account: &Address) -> Result<(), BackendError> {
377 if self.is_forked_mode() {
378 return self.ensure_cheatcode_access(account);
379 }
380 Ok(())
381 }
382
383 fn set_blockhash(&mut self, block_number: U256, block_hash: B256);
398
399 fn get_test_contract_address(&self) -> Option<Address>;
401}
402
403struct _ObjectSafe(dyn DatabaseExt);
404
405#[derive(Clone, Debug)]
458#[must_use]
459pub struct Backend {
460 forks: MultiFork,
462 mem_db: FoundryEvmInMemoryDB,
464 fork_init_journaled_state: JournaledState,
481 active_fork_ids: Option<(LocalForkId, ForkLookupIndex)>,
485 inner: BackendInner,
487 pub strategy: BackendStrategy,
489}
490
491impl Backend {
492 pub fn spawn(fork: Option<CreateFork>, strategy: BackendStrategy) -> eyre::Result<Self> {
497 Self::new(strategy, MultiFork::spawn(), fork)
498 }
499
500 pub fn new(
507 strategy: BackendStrategy,
508 forks: MultiFork,
509 fork: Option<CreateFork>,
510 ) -> eyre::Result<Self> {
511 trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend");
512 let inner = BackendInner {
514 persistent_accounts: HashSet::from(DEFAULT_PERSISTENT_ACCOUNTS),
515 ..Default::default()
516 };
517
518 let mut backend = Self {
519 forks,
520 mem_db: CacheDB::new(Default::default()),
521 fork_init_journaled_state: inner.new_journaled_state(),
522 active_fork_ids: None,
523 inner,
524 strategy,
525 };
526
527 if let Some(fork) = fork {
528 let (fork_id, fork, _) = backend.forks.create_fork(fork)?;
529 let fork_db = ForkDB::new(fork);
530 let fork_ids = backend.inner.insert_new_fork(
531 fork_id.clone(),
532 fork_db,
533 backend.inner.new_journaled_state(),
534 );
535 backend.inner.launched_with_fork = Some((fork_id, fork_ids.0, fork_ids.1));
536 backend.active_fork_ids = Some(fork_ids);
537 }
538
539 trace!(target: "backend", forking_mode=? backend.active_fork_ids.is_some(), "created executor backend");
540
541 Ok(backend)
542 }
543
544 pub(crate) fn new_with_fork(
547 strategy: BackendStrategy,
548 id: &ForkId,
549 fork: Fork,
550 journaled_state: JournaledState,
551 ) -> eyre::Result<Self> {
552 let mut backend = Self::spawn(None, strategy)?;
553 let fork_ids = backend.inner.insert_new_fork(id.clone(), fork.db, journaled_state);
554 backend.inner.launched_with_fork = Some((id.clone(), fork_ids.0, fork_ids.1));
555 backend.active_fork_ids = Some(fork_ids);
556 Ok(backend)
557 }
558
559 pub fn clone_empty(&self) -> Self {
561 Self {
562 forks: self.forks.clone(),
563 mem_db: CacheDB::new(Default::default()),
564 fork_init_journaled_state: self.inner.new_journaled_state(),
565 active_fork_ids: None,
566 inner: Default::default(),
567 strategy: self.strategy.clone(),
568 }
569 }
570
571 pub fn insert_account_info(&mut self, address: Address, account: AccountInfo) {
572 if let Some(db) = self.active_fork_db_mut() {
573 db.insert_account_info(address, account)
574 } else {
575 self.mem_db.insert_account_info(address, account)
576 }
577 }
578
579 pub fn insert_account_storage(
581 &mut self,
582 address: Address,
583 slot: U256,
584 value: U256,
585 ) -> Result<(), DatabaseError> {
586 if let Some(db) = self.active_fork_db_mut() {
587 db.insert_account_storage(address, slot, value)
588 } else {
589 self.mem_db.insert_account_storage(address, slot, value)
590 }
591 }
592
593 pub fn replace_account_storage(
598 &mut self,
599 address: Address,
600 storage: Map<U256, U256>,
601 ) -> Result<(), DatabaseError> {
602 if let Some(db) = self.active_fork_db_mut() {
603 db.replace_account_storage(address, storage)
604 } else {
605 self.mem_db.replace_account_storage(address, storage)
606 }
607 }
608
609 pub fn state_snapshots(
611 &self,
612 ) -> &StateSnapshots<BackendStateSnapshot<BackendDatabaseSnapshot>> {
613 &self.inner.state_snapshots
614 }
615
616 pub fn set_test_contract(&mut self, acc: Address) -> &mut Self {
623 trace!(?acc, "setting test account");
624 self.add_persistent_account(acc);
625 self.allow_cheatcode_access(acc);
626 self.inner.test_contract = Some(acc);
627 self
628 }
629
630 pub fn get_test_contract(&self) -> Option<Address> {
632 self.inner.test_contract
633 }
634
635 pub fn set_caller(&mut self, acc: Address) -> &mut Self {
637 trace!(?acc, "setting caller account");
638 self.inner.caller = Some(acc);
639 self.allow_cheatcode_access(acc);
640 self
641 }
642
643 pub fn set_spec_id(&mut self, spec_id: SpecId) -> &mut Self {
645 trace!(?spec_id, "setting spec ID");
646 self.inner.spec_id = spec_id;
647 self
648 }
649
650 pub fn caller_address(&self) -> Option<Address> {
652 self.inner.caller
653 }
654
655 pub fn has_state_snapshot_failure(&self) -> bool {
661 self.inner.has_state_snapshot_failure
662 }
663
664 pub fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) {
666 self.inner.has_state_snapshot_failure = has_state_snapshot_failure
667 }
668
669 pub fn mem_db(&self) -> &FoundryEvmInMemoryDB {
671 &self.mem_db
672 }
673
674 pub fn is_active_fork(&self, id: LocalForkId) -> bool {
676 self.active_fork_ids.map(|(i, _)| i == id).unwrap_or_default()
677 }
678
679 pub fn is_in_forking_mode(&self) -> bool {
681 self.active_fork().is_some()
682 }
683
684 pub fn active_fork(&self) -> Option<&Fork> {
686 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx))
687 }
688
689 pub fn active_fork_mut(&mut self) -> Option<&mut Fork> {
691 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx))
692 }
693
694 pub fn active_fork_db(&self) -> Option<&ForkDB> {
696 self.active_fork().map(|f| &f.db)
697 }
698
699 pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB> {
701 self.active_fork_mut().map(|f| &mut f.db)
702 }
703
704 #[inline(always)]
706 pub fn db(&self) -> &dyn Database<Error = DatabaseError> {
707 match self.active_fork_db() {
708 Some(fork_db) => fork_db,
709 None => &self.mem_db,
710 }
711 }
712
713 #[inline(always)]
715 pub fn db_mut(&mut self) -> &mut dyn Database<Error = DatabaseError> {
716 match self.active_fork_ids.map(|(_, idx)| &mut self.inner.get_fork_mut(idx).db) {
717 Some(fork_db) => fork_db,
718 None => &mut self.mem_db,
719 }
720 }
721
722 pub(crate) fn create_db_snapshot(&self) -> BackendDatabaseSnapshot {
724 if let Some((id, idx)) = self.active_fork_ids {
725 let fork = self.inner.get_fork(idx).clone();
726 let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed");
727 BackendDatabaseSnapshot::Forked(id, fork_id, idx, Box::new(fork))
728 } else {
729 BackendDatabaseSnapshot::InMemory(self.mem_db.clone())
730 }
731 }
732
733 pub fn merged_logs(&self, mut logs: Vec<Log>) -> Vec<Log> {
735 if let Some((_, active)) = self.active_fork_ids {
736 let mut all_logs = Vec::with_capacity(logs.len());
737
738 self.inner
739 .forks
740 .iter()
741 .enumerate()
742 .filter_map(|(idx, f)| f.as_ref().map(|f| (idx, f)))
743 .for_each(|(idx, f)| {
744 if idx == active {
745 all_logs.append(&mut logs);
746 } else {
747 all_logs.extend(f.journaled_state.logs.clone())
748 }
749 });
750 return all_logs;
751 }
752
753 logs
754 }
755
756 pub(crate) fn initialize(&mut self, env: &Env) {
760 self.set_caller(env.tx.caller);
761 self.set_spec_id(env.evm_env.cfg_env.spec);
762
763 if self.inner.test_contract.is_none() {
769 let test_contract = match env.tx.kind {
770 TxKind::Call(to) => to,
771 TxKind::Create => {
772 let nonce = self
773 .basic_ref(env.tx.caller)
774 .map(|b| b.unwrap_or_default().nonce)
775 .unwrap_or_default();
776 env.tx.caller.create(nonce)
777 }
778 };
779 self.set_test_contract(test_contract);
780 }
781 }
782
783 #[instrument(name = "inspect", level = "debug", skip_all)]
788 pub fn inspect<I: InspectorExt>(
789 &mut self,
790 env: &mut Env,
791 inspector: &mut I,
792 inspect_ctx: Box<dyn Any>,
793 ) -> eyre::Result<ResultAndState> {
794 self.initialize(env);
795 self.strategy.runner.inspect(self, env, inspector, inspect_ctx)
796 }
797
798 pub fn is_existing_precompile(&self, addr: &Address) -> bool {
800 self.inner.precompiles().contains(addr)
801 }
802
803 #[inline]
805 fn set_init_journaled_state(&mut self, journaled_state: JournaledState) {
806 trace!("recording fork init journaled_state");
807 self.fork_init_journaled_state = journaled_state;
808 }
809
810 fn prepare_init_journal_state(&mut self) -> Result<(), BackendError> {
820 let loaded_accounts = self
821 .fork_init_journaled_state
822 .state
823 .iter()
824 .filter(|(addr, _)| !self.is_existing_precompile(addr) && !self.is_persistent(addr))
825 .map(|(addr, _)| addr)
826 .copied()
827 .collect::<Vec<_>>();
828
829 for fork in self.inner.forks_iter_mut() {
830 let mut journaled_state = self.fork_init_journaled_state.clone();
831 for loaded_account in loaded_accounts.iter().copied() {
832 trace!(?loaded_account, "replacing account on init");
833 let init_account =
834 journaled_state.state.get_mut(&loaded_account).expect("exists; qed");
835
836 if init_account.is_created() {
840 trace!(?loaded_account, "skipping created account");
841 continue;
842 }
843
844 let fork_account = Database::basic(&mut fork.db, loaded_account)?
847 .ok_or(BackendError::MissingAccount(loaded_account))?;
848 init_account.info = fork_account;
849 }
850 fork.journaled_state = journaled_state;
851 }
852 Ok(())
853 }
854
855 fn get_block_number_and_block_for_transaction(
857 &self,
858 id: LocalForkId,
859 transaction: B256,
860 ) -> eyre::Result<(u64, AnyRpcBlock)> {
861 let fork = self.inner.get_fork_by_id(id)?;
862 let tx = fork.db.db.get_transaction(transaction)?;
863
864 if let Some(tx_block) = tx.block_number {
866 let block = fork.db.db.get_full_block(tx_block)?;
867
868 let fork_block = tx_block - 1;
871 Ok((fork_block, block))
872 } else {
873 let block = fork.db.db.get_full_block(BlockNumberOrTag::Latest)?;
874
875 let number = block.header.number;
876
877 Ok((number, block))
878 }
879 }
880
881 pub fn replay_until(
885 &mut self,
886 id: LocalForkId,
887 mut env: Env,
888 tx_hash: B256,
889 journaled_state: &mut JournaledState,
890 ) -> eyre::Result<Option<Transaction<AnyTxEnvelope>>> {
891 trace!(?id, ?tx_hash, "replay until transaction");
892
893 let persistent_accounts = self.inner.persistent_accounts.clone();
894 let fork_id = self.ensure_fork_id(id)?.clone();
895
896 let fork = self.inner.get_fork_by_id_mut(id)?;
897 let full_block =
898 fork.db.db.get_full_block(env.evm_env.block_env.number.saturating_to::<u64>())?;
899
900 for tx in full_block.inner.transactions.txns() {
901 if is_known_system_sender(tx.inner().inner.signer())
904 || tx.ty() == SYSTEM_TRANSACTION_TYPE
905 {
906 trace!(tx=?tx.tx_hash(), "skipping system transaction");
907 continue;
908 }
909
910 if tx.tx_hash() == tx_hash {
911 return Ok(Some(tx.inner.clone()));
913 }
914 trace!(tx=?tx.tx_hash(), "committing transaction");
915
916 commit_transaction(
917 &mut self.strategy,
918 &tx.inner,
919 &mut env.as_env_mut(),
920 journaled_state,
921 fork,
922 &fork_id,
923 &persistent_accounts,
924 &mut NoOpInspector,
925 )?;
926 }
927
928 Ok(None)
929 }
930}
931
932impl DatabaseExt for Backend {
933 fn get_strategy(&mut self) -> &mut BackendStrategy {
934 &mut self.strategy
935 }
936
937 fn snapshot_state(&mut self, journaled_state: &JournaledState, env: &mut EnvMut<'_>) -> U256 {
938 trace!("create snapshot");
939 let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new(
940 self.create_db_snapshot(),
941 journaled_state.clone(),
942 env.to_owned(),
943 ));
944 trace!(target: "backend", "Created new snapshot {}", id);
945 id
946 }
947
948 fn revert_state(
949 &mut self,
950 id: U256,
951 current_state: &JournaledState,
952 current: &mut EnvMut<'_>,
953 action: RevertStateSnapshotAction,
954 ) -> Option<JournaledState> {
955 trace!(?id, "revert snapshot");
956 if let Some(mut snapshot) = self.inner.state_snapshots.remove_at(id) {
957 if action.is_keep() {
959 self.inner.state_snapshots.insert_at(snapshot.clone(), id);
960 }
961
962 if let Some(account) = current_state.state.get(&CHEATCODE_ADDRESS)
967 && let Some(slot) = account.storage.get(&GLOBAL_FAIL_SLOT)
968 && !slot.present_value.is_zero()
969 {
970 self.set_state_snapshot_failure(true);
971 }
972
973 snapshot.merge(current_state);
975 let BackendStateSnapshot { db, mut journaled_state, env } = snapshot;
976 match db {
977 BackendDatabaseSnapshot::InMemory(mem_db) => {
978 self.mem_db = mem_db;
979 }
980 BackendDatabaseSnapshot::Forked(id, fork_id, idx, mut fork) => {
981 let caller = current.tx.caller;
985 journaled_state.state.entry(caller).or_insert_with(|| {
986 let caller_account = current_state
987 .state
988 .get(&caller)
989 .map(|acc| acc.info.clone())
990 .unwrap_or_default();
991
992 if !fork.db.cache.accounts.contains_key(&caller) {
993 fork.db.insert_account_info(caller, caller_account.clone());
995 }
996 caller_account.into()
997 });
998 self.inner.revert_state_snapshot(id, fork_id, idx, *fork);
999 self.active_fork_ids = Some((id, idx))
1000 }
1001 }
1002
1003 update_current_env_with_fork_env(&mut current.as_env_mut(), env);
1004 trace!(target: "backend", "Reverted snapshot {}", id);
1005
1006 Some(journaled_state)
1007 } else {
1008 warn!(target: "backend", "No snapshot to revert for {}", id);
1009 None
1010 }
1011 }
1012
1013 fn delete_state_snapshot(&mut self, id: U256) -> bool {
1014 self.inner.state_snapshots.remove_at(id).is_some()
1015 }
1016
1017 fn delete_state_snapshots(&mut self) {
1018 self.inner.state_snapshots.clear()
1019 }
1020
1021 fn create_fork(&mut self, create_fork: CreateFork) -> eyre::Result<LocalForkId> {
1022 trace!("create fork");
1023 let (fork_id, fork, _) = self.forks.create_fork(create_fork)?;
1024
1025 let fork_db = ForkDB::new(fork);
1026 let (id, _) =
1027 self.inner.insert_new_fork(fork_id, fork_db, self.fork_init_journaled_state.clone());
1028 Ok(id)
1029 }
1030
1031 fn create_fork_at_transaction(
1032 &mut self,
1033 fork: CreateFork,
1034 transaction: B256,
1035 ) -> eyre::Result<LocalForkId> {
1036 trace!(?transaction, "create fork at transaction");
1037 let id = self.create_fork(fork)?;
1038 let fork_id = self.ensure_fork_id(id).cloned()?;
1039 let mut env = self
1040 .forks
1041 .get_env(fork_id)?
1042 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exit", id))?;
1043
1044 self.roll_fork_to_transaction(
1047 Some(id),
1048 transaction,
1049 &mut env.as_env_mut(),
1050 &mut self.inner.new_journaled_state(),
1051 )?;
1052
1053 Ok(id)
1054 }
1055
1056 fn select_fork(
1059 &mut self,
1060 id: LocalForkId,
1061 env: &mut EnvMut<'_>,
1062 active_journaled_state: &mut JournaledState,
1063 ) -> eyre::Result<()> {
1064 trace!(?id, "select fork");
1065 if self.is_active_fork(id) {
1066 return Ok(());
1068 }
1069
1070 if let Some(active_fork_id) = self.active_fork_id() {
1073 self.forks.update_block(
1074 self.ensure_fork_id(active_fork_id).cloned()?,
1075 env.block.number,
1076 env.block.timestamp,
1077 )?;
1078 }
1079
1080 let fork_id = self.ensure_fork_id(id).cloned()?;
1081 let idx = self.inner.ensure_fork_index(&fork_id)?;
1082 let fork_env = self
1083 .forks
1084 .get_env(fork_id)?
1085 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exit", id))?;
1086
1087 if let Some(active) = self.active_fork_mut() {
1090 active.journaled_state = active_journaled_state.clone();
1091
1092 let caller = env.tx.caller;
1093 let caller_account = active.journaled_state.state.get(&env.tx.caller).cloned();
1094 let target_fork = self.inner.get_fork_mut(idx);
1095
1096 if target_fork.journaled_state.depth == 0 {
1098 if let Some(mut acc) = caller_account {
1100 let fork_account = Database::basic(&mut target_fork.db, caller)?
1101 .ok_or(BackendError::MissingAccount(caller))?;
1102
1103 acc.info = fork_account;
1104 target_fork.journaled_state.state.insert(caller, acc);
1105 }
1106 }
1107 } else {
1108 self.set_init_journaled_state(active_journaled_state.clone());
1115 self.prepare_init_journal_state()?;
1116
1117 self.fork_init_journaled_state.depth = 0;
1119 }
1120
1121 {
1122 let mut fork = self.inner.take_fork(idx);
1124
1125 let persistent_accounts = self.inner.persistent_accounts.clone();
1133 if let Some(db) = self.active_fork_db_mut() {
1134 for addr in persistent_accounts {
1135 let Ok(db_account) = db.load_account(addr) else { continue };
1136
1137 let Some(fork_account) = fork.journaled_state.state.get_mut(&addr) else {
1138 continue;
1139 };
1140
1141 for (key, val) in &db_account.storage {
1142 if let Some(fork_storage) = fork_account.storage.get_mut(key) {
1143 fork_storage.present_value = *val;
1144 }
1145 }
1146 }
1147 }
1148
1149 fork.journaled_state.depth = active_journaled_state.depth;
1154
1155 let caller = env.tx.caller;
1159 fork.journaled_state.state.entry(caller).or_insert_with(|| {
1160 let caller_account = active_journaled_state
1161 .state
1162 .get(&env.tx.caller)
1163 .map(|acc| acc.info.clone())
1164 .unwrap_or_default();
1165
1166 if !fork.db.cache.accounts.contains_key(&caller) {
1167 fork.db.insert_account_info(caller, caller_account.clone());
1169 }
1170 caller_account.into()
1171 });
1172
1173 let active_fork = self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx));
1174 self.strategy.runner.update_fork_db(
1175 self.strategy.context.as_mut(),
1176 active_fork,
1177 &self.mem_db,
1178 &self.inner,
1179 active_journaled_state,
1180 &mut fork,
1181 );
1182
1183 self.inner.set_fork(idx, fork);
1185 }
1186
1187 self.active_fork_ids = Some((id, idx));
1188 update_current_env_with_fork_env(env, fork_env);
1190
1191 Ok(())
1192 }
1193
1194 fn roll_fork(
1197 &mut self,
1198 id: Option<LocalForkId>,
1199 block_number: u64,
1200 env: &mut EnvMut<'_>,
1201 journaled_state: &mut JournaledState,
1202 ) -> eyre::Result<()> {
1203 trace!(?id, ?block_number, "roll fork");
1204 let id = self.ensure_fork(id)?;
1205 let (fork_id, backend, fork_env) =
1206 self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?;
1207 self.inner.roll_fork(&mut self.strategy, id, fork_id, backend)?;
1209
1210 if let Some((active_id, active_idx)) = self.active_fork_ids {
1211 if active_id == id {
1213 update_current_env_with_fork_env(env, fork_env);
1216
1217 let mut persistent_addrs = self.inner.persistent_accounts.clone();
1222 persistent_addrs.extend(self.caller_address());
1224
1225 let active = self.inner.get_fork_mut(active_idx);
1226 active.journaled_state = self.fork_init_journaled_state.clone();
1227 active.journaled_state.depth = journaled_state.depth;
1228
1229 for addr in persistent_addrs {
1230 self.strategy.runner.merge_journaled_state_data(
1231 self.strategy.context.as_mut(),
1232 addr,
1233 journaled_state,
1234 &mut active.journaled_state,
1235 );
1236 }
1237
1238 for (addr, acc) in &journaled_state.state {
1246 if acc.is_created() {
1247 if acc.is_touched() {
1248 self.strategy.runner.merge_journaled_state_data(
1249 self.strategy.context.as_mut(),
1250 *addr,
1251 journaled_state,
1252 &mut active.journaled_state,
1253 );
1254 }
1255 } else {
1256 let _ = active.journaled_state.load_account(&mut active.db, *addr);
1257 }
1258 }
1259
1260 *journaled_state = active.journaled_state.clone();
1261 }
1262 }
1263 Ok(())
1264 }
1265
1266 fn roll_fork_to_transaction(
1267 &mut self,
1268 id: Option<LocalForkId>,
1269 transaction: B256,
1270 env: &mut EnvMut<'_>,
1271 journaled_state: &mut JournaledState,
1272 ) -> eyre::Result<()> {
1273 trace!(?id, ?transaction, "roll fork to transaction");
1274 let id = self.ensure_fork(id)?;
1275
1276 let (fork_block, block) =
1277 self.get_block_number_and_block_for_transaction(id, transaction)?;
1278
1279 self.roll_fork(Some(id), fork_block, env, journaled_state)?;
1283
1284 update_env_block(env, &block);
1286
1287 let _ =
1290 self.forks.update_block_env(self.inner.ensure_fork_id(id).cloned()?, env.block.clone());
1291
1292 let env = env.to_owned();
1293
1294 self.replay_until(id, env, transaction, journaled_state)?;
1296
1297 Ok(())
1298 }
1299
1300 fn transact(
1301 &mut self,
1302 maybe_id: Option<LocalForkId>,
1303 transaction: B256,
1304 mut env: Env,
1305 journaled_state: &mut JournaledState,
1306 inspector: &mut dyn InspectorExt,
1307 ) -> eyre::Result<()> {
1308 trace!(?maybe_id, ?transaction, "execute transaction");
1309 let persistent_accounts = self.inner.persistent_accounts.clone();
1310 let id = self.ensure_fork(maybe_id)?;
1311 let fork_id = self.ensure_fork_id(id).cloned()?;
1312
1313 let tx = {
1314 let fork = self.inner.get_fork_by_id_mut(id)?;
1315 fork.db.db.get_transaction(transaction)?
1316 };
1317
1318 let (_fork_block, block) =
1325 self.get_block_number_and_block_for_transaction(id, transaction)?;
1326 update_env_block(&mut env.as_env_mut(), &block);
1327
1328 let fork = self.inner.get_fork_by_id_mut(id)?;
1329 commit_transaction(
1330 &mut self.strategy,
1331 &tx.inner,
1332 &mut env.as_env_mut(),
1333 journaled_state,
1334 fork,
1335 &fork_id,
1336 &persistent_accounts,
1337 inspector,
1338 )
1339 }
1340
1341 fn transact_from_tx(
1342 &mut self,
1343 tx: &TransactionRequest,
1344 env: Env,
1345 journaled_state: &mut JournaledState,
1346 inspector: &mut dyn InspectorExt,
1347 inspect_ctx: Box<dyn Any>,
1348 ) -> eyre::Result<()> {
1349 trace!(?tx, "execute signed transaction");
1350
1351 self.strategy.runner.transact_from_tx(
1352 self,
1353 tx,
1354 env,
1355 journaled_state,
1356 inspector,
1357 inspect_ctx,
1358 )
1359 }
1360
1361 fn active_fork_id(&self) -> Option<LocalForkId> {
1362 self.active_fork_ids.map(|(id, _)| id)
1363 }
1364
1365 fn active_fork_url(&self) -> Option<String> {
1366 let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
1367 self.forks.get_fork_url(fork.clone()).ok()?
1368 }
1369
1370 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
1371 if let Some(id) = id {
1372 if self.inner.issued_local_fork_ids.contains_key(&id) {
1373 return Ok(id);
1374 }
1375 eyre::bail!("Requested fork `{}` does not exit", id)
1376 }
1377 if let Some(id) = self.active_fork_id() { Ok(id) } else { eyre::bail!("No fork active") }
1378 }
1379
1380 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
1381 self.inner.ensure_fork_id(id)
1382 }
1383
1384 fn diagnose_revert(
1385 &self,
1386 callee: Address,
1387 journaled_state: &JournaledState,
1388 ) -> Option<RevertDiagnostic> {
1389 let active_id = self.active_fork_id()?;
1390 let active_fork = self.active_fork()?;
1391
1392 if self.inner.forks.len() == 1 {
1393 return None;
1396 }
1397
1398 if !active_fork.is_contract(callee) && !is_contract_in_state(journaled_state, callee) {
1399 let mut available_on = Vec::new();
1401 for (id, fork) in self.inner.forks_iter().filter(|(id, _)| *id != active_id) {
1402 trace!(?id, address=?callee, "checking if account exists");
1403 if fork.is_contract(callee) {
1404 available_on.push(id);
1405 }
1406 }
1407
1408 return if available_on.is_empty() {
1409 Some(RevertDiagnostic::ContractDoesNotExist {
1410 contract: callee,
1411 active: active_id,
1412 persistent: self.is_persistent(&callee),
1413 })
1414 } else {
1415 Some(RevertDiagnostic::ContractExistsOnOtherForks {
1418 contract: callee,
1419 active: active_id,
1420 available_on,
1421 })
1422 };
1423 }
1424 None
1425 }
1426
1427 fn load_allocs(
1431 &mut self,
1432 allocs: &BTreeMap<Address, GenesisAccount>,
1433 journaled_state: &mut JournaledState,
1434 ) -> Result<(), BackendError> {
1435 for (addr, acc) in allocs {
1437 self.clone_account(acc, addr, journaled_state)?;
1438 }
1439
1440 Ok(())
1441 }
1442
1443 fn clone_account(
1448 &mut self,
1449 source: &GenesisAccount,
1450 target: &Address,
1451 journaled_state: &mut JournaledState,
1452 ) -> Result<(), BackendError> {
1453 let mut state_acc = journaled_state.load_account(self, *target)?;
1456
1457 if let Some(bytecode) = source.code.as_ref() {
1459 state_acc.info.code_hash = keccak256(bytecode);
1460 let bytecode = Bytecode::new_raw(bytecode.0.clone().into());
1461 state_acc.info.code = Some(bytecode);
1462 }
1463
1464 if let Some(storage) = source.storage.as_ref() {
1466 state_acc.storage = storage
1467 .iter()
1468 .map(|(slot, value)| {
1469 let slot = U256::from_be_bytes(slot.0);
1470 (
1471 slot,
1472 EvmStorageSlot::new_changed(
1473 state_acc
1474 .storage
1475 .get(&slot)
1476 .map(|s| s.present_value)
1477 .unwrap_or_default(),
1478 U256::from_be_bytes(value.0),
1479 0,
1480 ),
1481 )
1482 })
1483 .collect();
1484 }
1485 state_acc.info.nonce = source.nonce.unwrap_or_default();
1487 state_acc.info.balance = source.balance;
1488
1489 journaled_state.touch(*target);
1491
1492 Ok(())
1493 }
1494
1495 fn add_persistent_account(&mut self, account: Address) -> bool {
1496 trace!(?account, "add persistent account");
1497 self.inner.persistent_accounts.insert(account)
1498 }
1499
1500 fn remove_persistent_account(&mut self, account: &Address) -> bool {
1501 trace!(?account, "remove persistent account");
1502 self.inner.persistent_accounts.remove(account)
1503 }
1504
1505 fn is_persistent(&self, acc: &Address) -> bool {
1506 self.inner.persistent_accounts.contains(acc)
1507 }
1508
1509 fn persistent_accounts(&self) -> &HashSet<Address> {
1510 &self.inner.persistent_accounts
1511 }
1512
1513 fn allow_cheatcode_access(&mut self, account: Address) -> bool {
1514 trace!(?account, "allow cheatcode access");
1515 self.inner.cheatcode_access_accounts.insert(account)
1516 }
1517
1518 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
1519 trace!(?account, "revoke cheatcode access");
1520 self.inner.cheatcode_access_accounts.remove(account)
1521 }
1522
1523 fn has_cheatcode_access(&self, account: &Address) -> bool {
1524 self.inner.cheatcode_access_accounts.contains(account)
1525 }
1526
1527 fn cached_accounts(&self) -> Vec<Address> {
1528 self.mem_db
1529 .cache
1530 .accounts
1531 .iter()
1532 .filter_map(|(addr, acc)| {
1533 if acc.info.code.as_ref().is_some_and(|c| !c.is_empty()) {
1535 Some(*addr)
1536 } else {
1537 None
1538 }
1539 })
1540 .collect()
1541 }
1542
1543 fn cached_storage(&self, address: Address) -> Option<Map<U256, U256>> {
1544 self.mem_db
1545 .cache
1546 .accounts
1547 .get(&address)
1548 .map(|acc| acc.storage.iter().map(|(k, v)| (*k, *v)).collect())
1549 }
1550
1551 fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
1552 if let Some(db) = self.active_fork_db_mut() {
1553 db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
1554 } else {
1555 self.mem_db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
1556 }
1557 }
1558
1559 fn get_test_contract_address(&self) -> Option<Address> {
1560 self.get_test_contract()
1561 }
1562}
1563
1564impl DatabaseRef for Backend {
1565 type Error = DatabaseError;
1566
1567 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
1568 if let Some(db) = self.active_fork_db() {
1569 db.basic_ref(address)
1570 } else {
1571 Ok(self.mem_db.basic_ref(address)?)
1572 }
1573 }
1574
1575 fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
1576 if let Some(db) = self.active_fork_db() {
1577 db.code_by_hash_ref(code_hash)
1578 } else {
1579 Ok(self.mem_db.code_by_hash_ref(code_hash)?)
1580 }
1581 }
1582
1583 fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
1584 if let Some(db) = self.active_fork_db() {
1585 DatabaseRef::storage_ref(db, address, index)
1586 } else {
1587 Ok(DatabaseRef::storage_ref(&self.mem_db, address, index)?)
1588 }
1589 }
1590
1591 fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
1592 if let Some(db) = self.active_fork_db() {
1593 db.block_hash_ref(number)
1594 } else {
1595 Ok(self.mem_db.block_hash_ref(number)?)
1596 }
1597 }
1598}
1599
1600impl DatabaseCommit for Backend {
1601 fn commit(&mut self, changes: Map<Address, Account>) {
1602 if let Some(db) = self.active_fork_db_mut() {
1603 db.commit(changes)
1604 } else {
1605 self.mem_db.commit(changes)
1606 }
1607 }
1608}
1609
1610impl Database for Backend {
1611 type Error = DatabaseError;
1612 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
1613 if let Some(db) = self.active_fork_db_mut() {
1614 Ok(db.basic(address)?)
1615 } else {
1616 Ok(self.mem_db.basic(address)?)
1617 }
1618 }
1619
1620 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
1621 if let Some(db) = self.active_fork_db_mut() {
1622 Ok(db.code_by_hash(code_hash)?)
1623 } else {
1624 Ok(self.mem_db.code_by_hash(code_hash)?)
1625 }
1626 }
1627
1628 fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
1629 if let Some(db) = self.active_fork_db_mut() {
1630 Ok(Database::storage(db, address, index)?)
1631 } else {
1632 Ok(Database::storage(&mut self.mem_db, address, index)?)
1633 }
1634 }
1635
1636 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
1637 if let Some(db) = self.active_fork_db_mut() {
1638 Ok(db.block_hash(number)?)
1639 } else {
1640 Ok(self.mem_db.block_hash(number)?)
1641 }
1642 }
1643}
1644
1645#[derive(Clone, Debug)]
1647pub enum BackendDatabaseSnapshot {
1648 InMemory(FoundryEvmInMemoryDB),
1650 Forked(LocalForkId, ForkId, ForkLookupIndex, Box<Fork>),
1652}
1653
1654#[derive(Clone, Debug)]
1656pub struct Fork {
1657 db: ForkDB,
1658 journaled_state: JournaledState,
1659}
1660
1661impl Fork {
1662 pub fn is_contract(&self, acc: Address) -> bool {
1664 if let Ok(Some(acc)) = self.db.basic_ref(acc)
1665 && acc.code_hash != KECCAK_EMPTY
1666 {
1667 return true;
1668 }
1669 is_contract_in_state(&self.journaled_state, acc)
1670 }
1671}
1672
1673#[derive(Clone, Debug)]
1675pub struct BackendInner {
1676 pub launched_with_fork: Option<(ForkId, LocalForkId, ForkLookupIndex)>,
1681 pub issued_local_fork_ids: HashMap<LocalForkId, ForkId>,
1693 pub created_forks: HashMap<ForkId, ForkLookupIndex>,
1696 pub forks: Vec<Option<Fork>>,
1699 pub state_snapshots: StateSnapshots<BackendStateSnapshot<BackendDatabaseSnapshot>>,
1701 pub has_state_snapshot_failure: bool,
1711 pub caller: Option<Address>,
1713 pub next_fork_id: LocalForkId,
1715 pub persistent_accounts: HashSet<Address>,
1719 pub spec_id: SpecId,
1721 pub cheatcode_access_accounts: HashSet<Address>,
1723 pub test_contract: Option<Address>,
1728}
1729
1730impl BackendInner {
1731 pub fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
1732 self.issued_local_fork_ids
1733 .get(&id)
1734 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
1735 }
1736
1737 pub fn ensure_fork_index(&self, id: &ForkId) -> eyre::Result<ForkLookupIndex> {
1738 self.created_forks
1739 .get(id)
1740 .copied()
1741 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
1742 }
1743
1744 pub fn ensure_fork_index_by_local_id(&self, id: LocalForkId) -> eyre::Result<ForkLookupIndex> {
1745 self.ensure_fork_index(self.ensure_fork_id(id)?)
1746 }
1747
1748 #[track_caller]
1750 fn get_fork(&self, idx: ForkLookupIndex) -> &Fork {
1751 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1752 self.forks[idx].as_ref().unwrap()
1753 }
1754
1755 #[track_caller]
1757 fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork {
1758 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1759 self.forks[idx].as_mut().unwrap()
1760 }
1761
1762 #[track_caller]
1764 fn get_fork_by_id_mut(&mut self, id: LocalForkId) -> eyre::Result<&mut Fork> {
1765 let idx = self.ensure_fork_index_by_local_id(id)?;
1766 Ok(self.get_fork_mut(idx))
1767 }
1768
1769 #[track_caller]
1771 fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork> {
1772 let idx = self.ensure_fork_index_by_local_id(id)?;
1773 Ok(self.get_fork(idx))
1774 }
1775
1776 fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork {
1778 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1779 self.forks[idx].take().unwrap()
1780 }
1781
1782 fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork) {
1783 self.forks[idx] = Some(fork)
1784 }
1785
1786 pub fn forks_iter(&self) -> impl Iterator<Item = (LocalForkId, &Fork)> + '_ {
1788 self.issued_local_fork_ids
1789 .iter()
1790 .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id])))
1791 }
1792
1793 pub fn forks_iter_mut(&mut self) -> impl Iterator<Item = &mut Fork> + '_ {
1795 self.forks.iter_mut().filter_map(|f| f.as_mut())
1796 }
1797
1798 pub fn revert_state_snapshot(
1800 &mut self,
1801 id: LocalForkId,
1802 fork_id: ForkId,
1803 idx: ForkLookupIndex,
1804 fork: Fork,
1805 ) {
1806 self.created_forks.insert(fork_id.clone(), idx);
1807 self.issued_local_fork_ids.insert(id, fork_id);
1808 self.set_fork(idx, fork)
1809 }
1810
1811 pub fn update_fork_mapping(
1813 &mut self,
1814 id: LocalForkId,
1815 fork_id: ForkId,
1816 db: ForkDB,
1817 journaled_state: JournaledState,
1818 ) -> ForkLookupIndex {
1819 let idx = self.forks.len();
1820 self.issued_local_fork_ids.insert(id, fork_id.clone());
1821 self.created_forks.insert(fork_id, idx);
1822
1823 let fork = Fork { db, journaled_state };
1824 self.forks.push(Some(fork));
1825 idx
1826 }
1827
1828 pub fn roll_fork(
1829 &mut self,
1830 strategy: &mut BackendStrategy,
1831 id: LocalForkId,
1832 new_fork_id: ForkId,
1833 backend: SharedBackend,
1834 ) -> eyre::Result<ForkLookupIndex> {
1835 let fork_id = self.ensure_fork_id(id)?;
1836 let idx = self.ensure_fork_index(fork_id)?;
1837
1838 if let Some(active) = self.forks[idx].as_mut() {
1839 let mut new_db = ForkDB::new(backend);
1841 for addr in self.persistent_accounts.iter().copied() {
1842 strategy.runner.merge_db_account_data(
1843 strategy.context.as_mut(),
1844 addr,
1845 &active.db,
1846 &mut new_db,
1847 );
1848 }
1849 active.db = new_db;
1850 }
1851 self.issued_local_fork_ids.insert(id, new_fork_id.clone());
1853 self.created_forks.insert(new_fork_id, idx);
1854 Ok(idx)
1855 }
1856
1857 pub fn insert_new_fork(
1861 &mut self,
1862 fork_id: ForkId,
1863 db: ForkDB,
1864 journaled_state: JournaledState,
1865 ) -> (LocalForkId, ForkLookupIndex) {
1866 let idx = self.forks.len();
1867 self.created_forks.insert(fork_id.clone(), idx);
1868 let id = self.next_id();
1869 self.issued_local_fork_ids.insert(id, fork_id);
1870 let fork = Fork { db, journaled_state };
1871 self.forks.push(Some(fork));
1872 (id, idx)
1873 }
1874
1875 fn next_id(&mut self) -> U256 {
1876 let id = self.next_fork_id;
1877 self.next_fork_id += U256::from(1);
1878 id
1879 }
1880
1881 pub fn len(&self) -> usize {
1883 self.issued_local_fork_ids.len()
1884 }
1885
1886 pub fn is_empty(&self) -> bool {
1888 self.issued_local_fork_ids.is_empty()
1889 }
1890
1891 pub fn precompiles(&self) -> &'static Precompiles {
1892 Precompiles::new(PrecompileSpecId::from_spec_id(self.spec_id))
1893 }
1894
1895 pub fn new_journaled_state(&self) -> JournaledState {
1897 let mut journal = {
1898 let mut journal_inner = JournalInner::new();
1899 journal_inner.set_spec_id(self.spec_id);
1900 journal_inner
1901 };
1902 journal.precompiles.extend(self.precompiles().addresses().copied());
1903 journal
1904 }
1905}
1906
1907impl Default for BackendInner {
1908 fn default() -> Self {
1909 Self {
1910 launched_with_fork: None,
1911 issued_local_fork_ids: Default::default(),
1912 created_forks: Default::default(),
1913 forks: vec![],
1914 state_snapshots: Default::default(),
1915 has_state_snapshot_failure: false,
1916 caller: None,
1917 next_fork_id: Default::default(),
1918 persistent_accounts: Default::default(),
1919 spec_id: SpecId::default(),
1920 cheatcode_access_accounts: HashSet::from([
1923 CHEATCODE_ADDRESS,
1924 TEST_CONTRACT_ADDRESS,
1925 CALLER,
1926 ]),
1927 test_contract: None,
1928 }
1929 }
1930}
1931
1932pub(crate) fn update_current_env_with_fork_env(current: &mut EnvMut<'_>, fork: Env) {
1934 *current.block = fork.evm_env.block_env;
1935 *current.cfg = fork.evm_env.cfg_env;
1936 current.tx.chain_id = fork.tx.chain_id;
1937}
1938
1939fn is_contract_in_state(journaled_state: &JournaledState, acc: Address) -> bool {
1941 journaled_state
1942 .state
1943 .get(&acc)
1944 .map(|acc| acc.info.code_hash != KECCAK_EMPTY)
1945 .unwrap_or_default()
1946}
1947
1948fn update_env_block(env: &mut EnvMut<'_>, block: &AnyRpcBlock) {
1950 env.block.timestamp = U256::from(block.header.timestamp);
1951 env.block.beneficiary = block.header.beneficiary;
1952 env.block.difficulty = block.header.difficulty;
1953 env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default());
1954 env.block.basefee = block.header.base_fee_per_gas.unwrap_or_default();
1955 env.block.gas_limit = block.header.gas_limit;
1956 env.block.number = U256::from(block.header.number);
1957
1958 if let Some(excess_blob_gas) = block.header.excess_blob_gas {
1959 env.block.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(
1960 excess_blob_gas,
1961 get_blob_base_fee_update_fraction_by_spec_id(env.cfg.spec),
1962 ));
1963 }
1964}
1965
1966#[allow(clippy::too_many_arguments)]
1969fn commit_transaction(
1970 strategy: &mut BackendStrategy,
1971 tx: &Transaction<AnyTxEnvelope>,
1972 env: &mut EnvMut<'_>,
1973 journaled_state: &mut JournaledState,
1974 fork: &mut Fork,
1975 fork_id: &ForkId,
1976 persistent_accounts: &HashSet<Address>,
1977 inspector: &mut dyn InspectorExt,
1978) -> eyre::Result<()> {
1979 configure_tx_env(env, tx);
1980
1981 let now = Instant::now();
1982 let res = {
1983 let fork = fork.clone();
1984 let journaled_state = journaled_state.clone();
1985 let depth = journaled_state.depth;
1986 let mut db = Backend::new_with_fork(strategy.clone(), fork_id, fork, journaled_state)?;
1987
1988 let mut evm = crate::evm::new_evm_with_inspector(&mut db as _, env.to_owned(), inspector);
1989 evm.journaled_state.depth = depth + 1;
1991 evm.transact(env.tx.clone()).wrap_err("backend: failed committing transaction")?
1992 };
1993 trace!(elapsed = ?now.elapsed(), "transacted transaction");
1994
1995 apply_state_changeset(res.state, journaled_state, fork, persistent_accounts)?;
1996 Ok(())
1997}
1998
1999pub fn update_state<DB: Database>(
2002 state: &mut EvmState,
2003 db: &mut DB,
2004 persistent_accounts: Option<&HashSet<Address>>,
2005) -> Result<(), DB::Error> {
2006 for (addr, acc) in state.iter_mut() {
2007 if !persistent_accounts.is_some_and(|accounts| accounts.contains(addr)) {
2008 acc.info = db.basic(*addr)?.unwrap_or_default();
2009 for (key, val) in &mut acc.storage {
2010 val.present_value = db.storage(*addr, *key)?;
2011 }
2012 }
2013 }
2014
2015 Ok(())
2016}
2017
2018fn apply_state_changeset(
2021 state: Map<revm::primitives::Address, Account>,
2022 journaled_state: &mut JournaledState,
2023 fork: &mut Fork,
2024 persistent_accounts: &HashSet<Address>,
2025) -> Result<(), BackendError> {
2026 fork.db.commit(state);
2028
2029 update_state(&mut journaled_state.state, &mut fork.db, Some(persistent_accounts))?;
2030 update_state(&mut fork.journaled_state.state, &mut fork.db, Some(persistent_accounts))?;
2031
2032 Ok(())
2033}
2034
2035#[cfg(test)]
2036mod tests {
2037 use crate::{
2038 backend::{Backend, strategy::BackendStrategy},
2039 fork::CreateFork,
2040 opts::EvmOpts,
2041 };
2042 use alloy_primitives::{Address, U256};
2043 use alloy_provider::Provider;
2044 use foundry_common::provider::get_http_provider;
2045 use foundry_config::{Config, NamedChain};
2046 use foundry_fork_db::cache::{BlockchainDb, BlockchainDbMeta};
2047 use revm::database::DatabaseRef;
2048
2049 const ENDPOINT: Option<&str> = option_env!("ETH_RPC_URL");
2050
2051 #[tokio::test(flavor = "multi_thread")]
2052 async fn can_read_write_cache() -> eyre::Result<()> {
2053 let Some(endpoint) = ENDPOINT else { return Ok(()) };
2054
2055 let provider = get_http_provider(endpoint);
2056
2057 let block_num = provider.get_block_number().await.unwrap();
2058
2059 let config = Config::figment();
2060 let mut evm_opts = config.extract::<EvmOpts>().unwrap();
2061 evm_opts.fork_block_number = Some(block_num);
2062
2063 let (env, _block) = evm_opts.fork_evm_env(endpoint).await.unwrap();
2064
2065 let fork = CreateFork {
2066 enable_caching: true,
2067 url: endpoint.to_string(),
2068 env: env.clone(),
2069 evm_opts,
2070 };
2071
2072 let backend = Backend::spawn(Some(fork), BackendStrategy::new_evm())?;
2073
2074 let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap();
2076
2077 let idx = U256::from(0u64);
2078 let _value = backend.storage_ref(address, idx);
2079 let _account = backend.basic_ref(address);
2080
2081 let num_slots = 10u64;
2083 for idx in 1..num_slots {
2084 let _ = backend.storage_ref(address, U256::from(idx));
2085 }
2086 drop(backend);
2087
2088 let meta = BlockchainDbMeta {
2089 chain: None,
2090 block_env: env.evm_env.block_env,
2091 hosts: Default::default(),
2092 };
2093
2094 let db = BlockchainDb::new(
2095 meta,
2096 Some(Config::foundry_block_cache_dir(NamedChain::Mainnet, block_num).unwrap()),
2097 );
2098 assert!(db.accounts().read().contains_key(&address));
2099 assert!(db.storage().read().contains_key(&address));
2100 assert_eq!(db.storage().read().get(&address).unwrap().len(), num_slots as usize);
2101 Ok(())
2102 }
2103}