1use crate::{
4 FoundryBlock, FoundryInspectorExt, FoundryTransaction, FromAnyRpcTransaction,
5 constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS},
6 evm::{
7 BlockEnvFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, FoundryEvmFactory,
8 FoundryEvmNetwork, HaltReasonFor, PrecompilesFor, SpecFor, TxEnvFor,
9 },
10 fork::{CreateFork, ForkId, MultiFork},
11 state_snapshot::StateSnapshots,
12 utils::get_blob_base_fee_update_fraction,
13};
14use alloy_consensus::{BlockHeader, Typed2718};
15use alloy_evm::{Evm, EvmEnv, EvmFactory};
16use alloy_genesis::GenesisAccount;
17use alloy_network::{
18 AnyNetwork, AnyRpcBlock, AnyRpcTransaction, BlockResponse, Network, TransactionResponse,
19};
20use alloy_primitives::{Address, B256, TxKind, U256, keccak256, uint};
21use alloy_rpc_types::BlockNumberOrTag;
22use eyre::Context;
23use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender};
24pub use foundry_fork_db::{BlockchainDb, ForkBlockEnv, SharedBackend, cache::BlockchainDbMeta};
25use itertools::Itertools;
26use revm::{
27 Database, DatabaseCommit, JournalEntry,
28 bytecode::Bytecode,
29 context::{Block, BlockEnv, CfgEnv, ContextTr, JournalInner, Transaction},
30 context_interface::{journaled_state::account::JournaledAccountTr, result::ResultAndState},
31 database::{CacheDB, DatabaseRef, EmptyDB},
32 primitives::{AddressMap, HashMap as Map, KECCAK_EMPTY, Log},
33 state::{Account, AccountInfo, EvmState, EvmStorageSlot},
34};
35use std::{
36 collections::{BTreeMap, HashMap, HashSet},
37 fmt::Debug,
38 time::Instant,
39};
40
41mod diagnostic;
42pub use diagnostic::RevertDiagnostic;
43
44mod error;
45pub use error::{BackendError, BackendResult, DatabaseError, DatabaseResult};
46
47mod cow;
48pub use cow::CowBackend;
49
50mod in_memory_db;
51pub use in_memory_db::{EmptyDBWrapper, FoundryEvmInMemoryDB, MemDb};
52
53mod snapshot;
54pub use snapshot::{BackendStateSnapshot, RevertStateSnapshotAction, StateSnapshot};
55
56type ForkDB<N, B> = CacheDB<SharedBackend<N, B>>;
58
59pub type LocalForkId = U256;
64
65type ForkLookupIndex = usize;
68
69const DEFAULT_PERSISTENT_ACCOUNTS: [Address; 3] =
71 [CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, CALLER];
72
73pub const GLOBAL_FAIL_SLOT: U256 =
78 uint!(0x6661696c65640000000000000000000000000000000000000000000000000000_U256);
79
80pub type JournaledState = JournalInner<JournalEntry>;
81
82#[auto_impl::auto_impl(&mut)]
84pub trait DatabaseExt<F: FoundryEvmFactory>:
85 Database<Error = DatabaseError> + DatabaseCommit + Debug
86{
87 fn snapshot_state(
93 &mut self,
94 journaled_state: &JournaledState,
95 evm_env: &EvmEnv<F::Spec, F::BlockEnv>,
96 ) -> U256;
97
98 fn revert_state(
111 &mut self,
112 id: U256,
113 journaled_state: &JournaledState,
114 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
115 caller: Address,
116 action: RevertStateSnapshotAction,
117 ) -> Option<JournaledState>;
118
119 fn delete_state_snapshot(&mut self, id: U256) -> bool;
124
125 fn delete_state_snapshots(&mut self);
127
128 fn create_select_fork(
132 &mut self,
133 fork: CreateFork,
134 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
135 tx_env: &mut F::Tx,
136 journaled_state: &mut JournaledState,
137 ) -> eyre::Result<LocalForkId> {
138 let id = self.create_fork(fork)?;
139 self.select_fork(id, evm_env, tx_env, journaled_state)?;
140 Ok(id)
141 }
142
143 fn create_select_fork_at_transaction(
147 &mut self,
148 fork: CreateFork,
149 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
150 tx_env: &mut F::Tx,
151 journaled_state: &mut JournaledState,
152 transaction: B256,
153 ) -> eyre::Result<LocalForkId> {
154 let id = self.create_fork_at_transaction(fork, transaction)?;
155 self.select_fork(id, evm_env, tx_env, journaled_state)?;
156 Ok(id)
157 }
158
159 fn create_fork(&mut self, fork: CreateFork) -> eyre::Result<LocalForkId>;
161
162 fn create_fork_at_transaction(
164 &mut self,
165 fork: CreateFork,
166 transaction: B256,
167 ) -> eyre::Result<LocalForkId>;
168
169 fn select_fork(
179 &mut self,
180 id: LocalForkId,
181 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
182 tx_env: &mut F::Tx,
183 journaled_state: &mut JournaledState,
184 ) -> eyre::Result<()>;
185
186 fn roll_fork(
194 &mut self,
195 id: Option<LocalForkId>,
196 block_number: u64,
197 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
198 journaled_state: &mut JournaledState,
199 ) -> eyre::Result<()>;
200
201 fn roll_fork_to_transaction(
210 &mut self,
211 id: Option<LocalForkId>,
212 transaction: B256,
213 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
214 journaled_state: &mut JournaledState,
215 ) -> eyre::Result<()>;
216
217 fn transact(
219 &mut self,
220 id: Option<LocalForkId>,
221 transaction: B256,
222 evm_env: EvmEnv<F::Spec, F::BlockEnv>,
223 journaled_state: &mut JournaledState,
224 inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
225 ) -> eyre::Result<()>;
226
227 fn transact_from_tx(
229 &mut self,
230 tx_env: F::Tx,
231 evm_env: EvmEnv<F::Spec, F::BlockEnv>,
232 journaled_state: &mut JournaledState,
233 inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
234 ) -> eyre::Result<()>;
235
236 fn active_fork_id(&self) -> Option<LocalForkId>;
238
239 fn active_fork_url(&self) -> Option<String>;
241
242 fn is_forked_mode(&self) -> bool {
244 self.active_fork_id().is_some()
245 }
246
247 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId>;
258
259 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId>;
261
262 fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic>;
289
290 fn load_allocs(
294 &mut self,
295 allocs: &BTreeMap<Address, GenesisAccount>,
296 journaled_state: &mut JournaledState,
297 ) -> Result<(), BackendError>;
298
299 fn clone_account(
304 &mut self,
305 source: &GenesisAccount,
306 target: &Address,
307 journaled_state: &mut JournaledState,
308 ) -> Result<(), BackendError>;
309
310 fn is_persistent(&self, acc: &Address) -> bool;
312
313 fn remove_persistent_account(&mut self, account: &Address) -> bool;
315
316 fn add_persistent_account(&mut self, account: Address) -> bool;
318
319 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
321 fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
322 where
323 Self: Sized,
324 {
325 for acc in accounts {
326 self.remove_persistent_account(&acc);
327 }
328 }
329
330 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
332 fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
333 where
334 Self: Sized,
335 {
336 for acc in accounts {
337 self.add_persistent_account(acc);
338 }
339 }
340
341 fn allow_cheatcode_access(&mut self, account: Address) -> bool;
345
346 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool;
350
351 fn has_cheatcode_access(&self, account: &Address) -> bool;
353
354 fn ensure_cheatcode_access(&self, account: &Address) -> Result<(), BackendError> {
358 if !self.has_cheatcode_access(account) {
359 return Err(BackendError::NoCheats(*account));
360 }
361 Ok(())
362 }
363
364 fn ensure_cheatcode_access_forking_mode(&self, account: &Address) -> Result<(), BackendError> {
367 if self.is_forked_mode() {
368 return self.ensure_cheatcode_access(account);
369 }
370 Ok(())
371 }
372
373 fn set_blockhash(&mut self, block_number: U256, block_hash: B256);
388}
389
390#[must_use]
443pub struct Backend<FEN: FoundryEvmNetwork = EthEvmNetwork> {
444 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
446 mem_db: FoundryEvmInMemoryDB,
448 fork_init_journaled_state: JournaledState,
465 active_fork_ids: Option<(LocalForkId, ForkLookupIndex)>,
469 inner: BackendInner<FEN>,
471}
472
473impl<FEN: FoundryEvmNetwork> Clone for Backend<FEN> {
474 fn clone(&self) -> Self {
475 Self {
476 forks: self.forks.clone(),
477 mem_db: self.mem_db.clone(),
478 fork_init_journaled_state: self.fork_init_journaled_state.clone(),
479 active_fork_ids: self.active_fork_ids,
480 inner: self.inner.clone(),
481 }
482 }
483}
484
485impl<FEN: FoundryEvmNetwork> Debug for Backend<FEN> {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487 f.debug_struct("Backend")
488 .field("forks", &self.forks)
489 .field("mem_db", &self.mem_db)
490 .field("fork_init_journaled_state", &self.fork_init_journaled_state)
491 .field("active_fork_ids", &self.active_fork_ids)
492 .field("inner", &self.inner)
493 .finish()
494 }
495}
496
497impl<FEN: FoundryEvmNetwork> Backend<FEN> {
498 pub fn spawn(fork: Option<CreateFork>) -> eyre::Result<Self> {
503 Self::new(MultiFork::<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>::spawn(), fork)
504 }
505
506 pub fn new(
513 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
514 fork: Option<CreateFork>,
515 ) -> eyre::Result<Self> {
516 trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend");
517 let inner = BackendInner {
519 persistent_accounts: HashSet::from(DEFAULT_PERSISTENT_ACCOUNTS),
520 ..Default::default()
521 };
522
523 let mut backend = Self {
524 forks,
525 mem_db: CacheDB::new(Default::default()),
526 fork_init_journaled_state: inner.new_journaled_state(),
527 active_fork_ids: None,
528 inner,
529 };
530
531 if let Some(fork) = fork {
532 let (fork_id, fork, _) = backend.forks.create_fork(fork)?;
533 let fork_db = ForkDB::new(fork);
534 let fork_ids = backend.inner.insert_new_fork(
535 fork_id.clone(),
536 fork_db,
537 backend.inner.new_journaled_state(),
538 );
539 backend.inner.launched_with_fork = Some((fork_id, fork_ids.0, fork_ids.1));
540 backend.active_fork_ids = Some(fork_ids);
541 }
542
543 trace!(target: "backend", forking_mode=? backend.active_fork_ids.is_some(), "created executor backend");
544
545 Ok(backend)
546 }
547
548 pub(crate) fn new_with_fork(
551 id: &ForkId,
552 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
553 journaled_state: JournaledState,
554 ) -> eyre::Result<Self> {
555 let mut backend = Self::spawn(None)?;
556 let fork_ids = backend.inner.insert_new_fork(id.clone(), fork.db, journaled_state);
557 backend.inner.launched_with_fork = Some((id.clone(), fork_ids.0, fork_ids.1));
558 backend.active_fork_ids = Some(fork_ids);
559 Ok(backend)
560 }
561
562 pub fn clone_empty(&self) -> Self {
564 Self {
565 forks: self.forks.clone(),
566 mem_db: CacheDB::new(Default::default()),
567 fork_init_journaled_state: self.inner.new_journaled_state(),
568 active_fork_ids: None,
569 inner: Default::default(),
570 }
571 }
572
573 pub fn insert_account_info(&mut self, address: Address, account: AccountInfo) {
574 if let Some(db) = self.active_fork_db_mut() {
575 db.insert_account_info(address, account)
576 } else {
577 self.mem_db.insert_account_info(address, account)
578 }
579 }
580
581 pub fn insert_account_storage(
583 &mut self,
584 address: Address,
585 slot: U256,
586 value: U256,
587 ) -> Result<(), DatabaseError> {
588 if let Some(db) = self.active_fork_db_mut() {
589 db.insert_account_storage(address, slot, value)
590 } else {
591 self.mem_db.insert_account_storage(address, slot, value)
592 }
593 }
594
595 pub fn replace_account_storage(
600 &mut self,
601 address: Address,
602 storage: Map<U256, U256>,
603 ) -> Result<(), DatabaseError> {
604 if let Some(db) = self.active_fork_db_mut() {
605 db.replace_account_storage(address, storage.into_iter().collect())
606 } else {
607 self.mem_db.replace_account_storage(address, storage.into_iter().collect())
608 }
609 }
610
611 #[allow(clippy::type_complexity)]
613 pub const fn state_snapshots(
614 &self,
615 ) -> &StateSnapshots<
616 BackendStateSnapshot<
617 BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
618 SpecFor<FEN>,
619 BlockEnvFor<FEN>,
620 >,
621 > {
622 &self.inner.state_snapshots
623 }
624
625 pub fn set_test_contract(&mut self, acc: Address) -> &mut Self {
632 trace!(?acc, "setting test account");
633 self.inner.persistent_accounts.insert(acc);
634 self.inner.cheatcode_access_accounts.insert(acc);
635 self
636 }
637
638 pub fn set_caller(&mut self, acc: Address) -> &mut Self {
640 trace!(?acc, "setting caller account");
641 self.inner.caller = Some(acc);
642 self.inner.cheatcode_access_accounts.insert(acc);
643 self
644 }
645
646 pub fn set_spec_id(&mut self, spec_id: impl Into<SpecFor<FEN>>) -> &mut Self {
648 self.inner.spec_id = spec_id.into();
649 self
650 }
651
652 pub const fn caller_address(&self) -> Option<Address> {
654 self.inner.caller
655 }
656
657 pub const fn has_state_snapshot_failure(&self) -> bool {
663 self.inner.has_state_snapshot_failure
664 }
665
666 pub const fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) {
668 self.inner.has_state_snapshot_failure = has_state_snapshot_failure
669 }
670
671 pub(crate) fn update_fork_db(
673 &self,
674 active_journaled_state: &mut JournaledState,
675 target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
676 ) {
677 self.update_fork_db_contracts(
678 self.inner.persistent_accounts.iter().copied(),
679 active_journaled_state,
680 target_fork,
681 )
682 }
683
684 pub(crate) fn update_fork_db_contracts(
686 &self,
687 accounts: impl IntoIterator<Item = Address>,
688 active_journaled_state: &mut JournaledState,
689 target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
690 ) {
691 if let Some(db) = self.active_fork_db() {
692 merge_account_data(accounts, db, active_journaled_state, target_fork)
693 } else {
694 merge_account_data(accounts, &self.mem_db, active_journaled_state, target_fork)
695 }
696 }
697
698 pub const fn mem_db(&self) -> &FoundryEvmInMemoryDB {
700 &self.mem_db
701 }
702
703 pub fn is_active_fork(&self, id: LocalForkId) -> bool {
705 self.active_fork_ids.map(|(i, _)| i == id).unwrap_or_default()
706 }
707
708 pub fn is_in_forking_mode(&self) -> bool {
710 self.active_fork().is_some()
711 }
712
713 pub fn active_fork(&self) -> Option<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
715 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx))
716 }
717
718 pub fn active_fork_mut(&mut self) -> Option<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
720 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx))
721 }
722
723 pub fn active_fork_db(&self) -> Option<&ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
725 self.active_fork().map(|f| &f.db)
726 }
727
728 pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
730 self.active_fork_mut().map(|f| &mut f.db)
731 }
732
733 pub fn db(&self) -> &dyn Database<Error = DatabaseError> {
735 match self.active_fork_db() {
736 Some(fork_db) => fork_db,
737 None => &self.mem_db,
738 }
739 }
740
741 pub fn db_mut(&mut self) -> &mut dyn Database<Error = DatabaseError> {
743 match self.active_fork_ids.map(|(_, idx)| &mut self.inner.get_fork_mut(idx).db) {
744 Some(fork_db) => fork_db,
745 None => &mut self.mem_db,
746 }
747 }
748
749 pub(crate) fn create_db_snapshot(
751 &self,
752 ) -> BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>> {
753 if let Some((id, idx)) = self.active_fork_ids {
754 let fork = self.inner.get_fork(idx).clone();
755 let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed");
756 BackendDatabaseSnapshot::Forked(id, fork_id, idx, Box::new(fork))
757 } else {
758 BackendDatabaseSnapshot::InMemory(self.mem_db.clone())
759 }
760 }
761
762 pub fn merged_logs(&self, mut logs: Vec<Log>) -> Vec<Log> {
764 if let Some((_, active)) = self.active_fork_ids {
765 let mut all_logs = Vec::with_capacity(logs.len());
766
767 self.inner
768 .forks
769 .iter()
770 .enumerate()
771 .filter_map(|(idx, f)| f.as_ref().map(|f| (idx, f)))
772 .for_each(|(idx, f)| {
773 if idx == active {
774 all_logs.append(&mut logs);
775 } else {
776 all_logs.extend(f.journaled_state.logs.clone())
777 }
778 });
779 return all_logs;
780 }
781
782 logs
783 }
784
785 pub(crate) fn initialize(
789 &mut self,
790 spec_id: impl Into<SpecFor<FEN>>,
791 caller: Address,
792 tx_kind: TxKind,
793 ) {
794 self.set_caller(caller);
795 self.set_spec_id(spec_id);
796
797 let test_contract = match tx_kind {
798 TxKind::Call(to) => to,
799 TxKind::Create => {
800 let nonce =
801 self.basic_ref(caller).map(|b| b.unwrap_or_default().nonce).unwrap_or_default();
802 caller.create(nonce)
803 }
804 };
805 self.set_test_contract(test_contract);
806 }
807
808 #[instrument(name = "inspect", level = "debug", skip_all)]
813 pub fn inspect<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
814 &mut self,
815 evm_env: &mut EvmEnvFor<FEN>,
816 tx_env: &mut TxEnvFor<FEN>,
817 inspector: I,
818 ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
819 self.initialize(evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind());
820 let mut evm = FEN::EvmFactory::default().create_foundry_evm_with_inspector(
821 self,
822 evm_env.to_owned(),
823 inspector,
824 );
825 let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?;
826
827 *tx_env = evm.tx().clone();
828 *evm_env = evm.finish().1;
829
830 Ok(res)
831 }
832
833 pub fn is_existing_precompile(&self, addr: &Address) -> bool {
835 self.inner.precompiles().addresses().contains(addr)
836 }
837
838 #[inline]
840 fn set_init_journaled_state(&mut self, journaled_state: JournaledState) {
841 trace!("recording fork init journaled_state");
842 self.fork_init_journaled_state = journaled_state;
843 }
844
845 fn prepare_init_journal_state(&mut self) -> Result<(), BackendError> {
855 let loaded_accounts = self
856 .fork_init_journaled_state
857 .state
858 .iter()
859 .filter(|(addr, _)| {
860 !self.is_existing_precompile(addr)
861 && !self.inner.persistent_accounts.contains(*addr)
862 })
863 .map(|(addr, _)| addr)
864 .copied()
865 .collect::<Vec<_>>();
866
867 for fork in self.inner.forks_iter_mut() {
868 let mut journaled_state = self.fork_init_journaled_state.clone();
869 for loaded_account in loaded_accounts.iter().copied() {
870 trace!(?loaded_account, "replacing account on init");
871 let init_account =
872 journaled_state.state.get_mut(&loaded_account).expect("exists; qed");
873
874 if init_account.is_created() {
878 trace!(?loaded_account, "skipping created account");
879 continue;
880 }
881
882 let fork_account = Database::basic(&mut fork.db, loaded_account)?
885 .ok_or(BackendError::MissingAccount(loaded_account))?;
886 init_account.info = fork_account;
887 }
888 fork.journaled_state = journaled_state;
889 }
890 Ok(())
891 }
892
893 fn get_block_number_and_block_for_transaction(
895 &self,
896 id: LocalForkId,
897 transaction: B256,
898 ) -> eyre::Result<(u64, AnyRpcBlock)> {
899 let fork = self.inner.get_fork_by_id(id)?;
900 let tx = fork.backend().get_transaction(transaction)?;
901
902 if let Some(tx_block) = tx.block_number() {
904 let block = fork.backend().get_full_block(tx_block)?;
905
906 let fork_block = tx_block - 1;
909 Ok((fork_block, block))
910 } else {
911 let block = fork.backend().get_full_block(BlockNumberOrTag::Latest)?;
912
913 let number = block.header().number();
914
915 Ok((number, block))
916 }
917 }
918
919 pub fn replay_until(
923 &mut self,
924 id: LocalForkId,
925 evm_env: EvmEnvFor<FEN>,
926 tx_hash: B256,
927 journaled_state: &mut JournaledState,
928 ) -> eyre::Result<Option<AnyRpcTransaction>> {
929 trace!(?id, ?tx_hash, "replay until transaction");
930
931 let persistent_accounts = self.inner.persistent_accounts.clone();
932
933 let fork = self.inner.get_fork_by_id_mut(id)?;
934 let full_block =
935 fork.backend().get_full_block(evm_env.block_env.number().saturating_to::<u64>())?;
936
937 let txs = full_block
939 .transactions()
940 .txns()
941 .filter(|tx| !is_known_system_sender(tx.from()) && tx.ty() != SYSTEM_TRANSACTION_TYPE);
942
943 let mut txs_to_replay = Vec::new();
944 let mut target_tx = None;
945 for tx in txs {
946 if tx.tx_hash() == tx_hash {
947 target_tx = Some(tx.clone());
948 break;
949 }
950 txs_to_replay.push(tx.clone());
951 }
952
953 if !txs_to_replay.is_empty() {
955 let now = Instant::now();
956
957 let replay_db = fork.db.clone();
960 let mut evm = FEN::EvmFactory::default().create_evm(replay_db, evm_env);
961
962 for tx in &txs_to_replay {
963 let tx_env = TxEnvFor::<FEN>::from_any_rpc_transaction(tx)?;
964 trace!(tx=?tx.tx_hash(), "committing transaction");
965 evm.transact_commit(tx_env).wrap_err("backend: failed committing transaction")?;
966 }
967
968 fork.db = evm.into_db();
970
971 fork.refresh_journaled_states(journaled_state, &persistent_accounts)?;
974
975 trace!(elapsed=?now.elapsed(), count=txs_to_replay.len(), "replayed transactions");
976 }
977
978 Ok(target_tx)
979 }
980}
981
982impl<FEN: FoundryEvmNetwork> DatabaseExt<FEN::EvmFactory> for Backend<FEN> {
983 fn snapshot_state(
984 &mut self,
985 journaled_state: &JournaledState,
986 evm_env: &EvmEnvFor<FEN>,
987 ) -> U256 {
988 trace!("create snapshot");
989 let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new(
990 self.create_db_snapshot(),
991 journaled_state.clone(),
992 evm_env.clone(),
993 ));
994 trace!(target: "backend", "Created new snapshot {}", id);
995 id
996 }
997
998 fn revert_state(
999 &mut self,
1000 id: U256,
1001 current_state: &JournaledState,
1002 evm_env: &mut EvmEnvFor<FEN>,
1003 caller: Address,
1004 action: RevertStateSnapshotAction,
1005 ) -> Option<JournaledState> {
1006 trace!(?id, "revert snapshot");
1007 if let Some(mut snapshot) = self.inner.state_snapshots.remove_at(id) {
1008 if action.is_keep() {
1010 self.inner.state_snapshots.insert_at(snapshot.clone(), id);
1011 }
1012
1013 if let Some(account) = current_state.state.get(&CHEATCODE_ADDRESS)
1018 && let Some(slot) = account.storage.get(&GLOBAL_FAIL_SLOT)
1019 && !slot.present_value.is_zero()
1020 {
1021 self.set_state_snapshot_failure(true);
1022 }
1023
1024 snapshot.merge(current_state);
1026 let BackendStateSnapshot { db, mut journaled_state, snap_evm_env } = snapshot;
1027 match db {
1028 BackendDatabaseSnapshot::InMemory(mem_db) => {
1029 self.mem_db = mem_db;
1030 }
1031 BackendDatabaseSnapshot::Forked(id, fork_id, idx, mut fork) => {
1032 journaled_state.state.entry(caller).or_insert_with(|| {
1036 let caller_account = current_state
1037 .state
1038 .get(&caller)
1039 .map(|acc| acc.info.clone())
1040 .unwrap_or_default();
1041
1042 if !fork.db.cache.accounts.contains_key(&caller) {
1043 fork.db.insert_account_info(caller, caller_account.clone());
1045 }
1046 caller_account.into()
1047 });
1048 self.inner.revert_state_snapshot(id, fork_id, idx, *fork);
1049 self.active_fork_ids = Some((id, idx))
1050 }
1051 }
1052
1053 *evm_env = snap_evm_env;
1054 trace!(target: "backend", "Reverted snapshot {}", id);
1055
1056 Some(journaled_state)
1057 } else {
1058 warn!(target: "backend", "No snapshot to revert for {}", id);
1059 None
1060 }
1061 }
1062
1063 fn delete_state_snapshot(&mut self, id: U256) -> bool {
1064 self.inner.state_snapshots.remove_at(id).is_some()
1065 }
1066
1067 fn delete_state_snapshots(&mut self) {
1068 self.inner.state_snapshots.clear()
1069 }
1070
1071 fn create_fork(&mut self, create_fork: CreateFork) -> eyre::Result<LocalForkId> {
1072 trace!("create fork");
1073 let (fork_id, fork, _) = self.forks.create_fork(create_fork)?;
1074
1075 let fork_db = ForkDB::new(fork);
1076 let (id, _) =
1077 self.inner.insert_new_fork(fork_id, fork_db, self.fork_init_journaled_state.clone());
1078 Ok(id)
1079 }
1080
1081 fn create_fork_at_transaction(
1082 &mut self,
1083 fork: CreateFork,
1084 transaction: B256,
1085 ) -> eyre::Result<LocalForkId> {
1086 trace!(?transaction, "create fork at transaction");
1087 let id = self.create_fork(fork)?;
1088 let fork_id = self.ensure_fork_id(id).cloned()?;
1089 let mut evm_env = self
1090 .forks
1091 .get_evm_env(fork_id)?
1092 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
1093
1094 self.roll_fork_to_transaction(
1097 Some(id),
1098 transaction,
1099 &mut evm_env,
1100 &mut self.inner.new_journaled_state(),
1101 )?;
1102
1103 Ok(id)
1104 }
1105
1106 fn select_fork(
1109 &mut self,
1110 id: LocalForkId,
1111 evm_env: &mut EvmEnvFor<FEN>,
1112 tx_env: &mut TxEnvFor<FEN>,
1113 active_journaled_state: &mut JournaledState,
1114 ) -> eyre::Result<()> {
1115 trace!(?id, "select fork");
1116 if self.is_active_fork(id) {
1117 return Ok(());
1119 }
1120
1121 if let Some(active_fork_id) = self.active_fork_id() {
1124 self.forks.update_block(
1125 self.ensure_fork_id(active_fork_id).cloned()?,
1126 evm_env.block_env.number(),
1127 evm_env.block_env.timestamp(),
1128 )?;
1129 }
1130
1131 let fork_id = self.ensure_fork_id(id).cloned()?;
1132 let idx = self.inner.ensure_fork_index(&fork_id)?;
1133 let fork_evm_env = self
1134 .forks
1135 .get_evm_env(fork_id)?
1136 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
1137
1138 if let Some(active) = self.active_fork_mut() {
1141 active.journaled_state = active_journaled_state.clone();
1142
1143 let caller = tx_env.caller();
1144 let caller_account = active.journaled_state.state.get(&caller).cloned();
1145 let target_fork = self.inner.get_fork_mut(idx);
1146
1147 if target_fork.journaled_state.depth == 0 {
1149 if let Some(mut acc) = caller_account {
1151 let fork_account = Database::basic(&mut target_fork.db, caller)?
1152 .ok_or(BackendError::MissingAccount(caller))?;
1153
1154 acc.info = fork_account;
1155 target_fork.journaled_state.state.insert(caller, acc);
1156 }
1157 }
1158 } else {
1159 self.set_init_journaled_state(active_journaled_state.clone());
1166 self.prepare_init_journal_state()?;
1167
1168 self.fork_init_journaled_state.depth = 0;
1170 }
1171
1172 {
1173 let mut fork = self.inner.take_fork(idx);
1175
1176 let persistent_accounts = self.inner.persistent_accounts.clone();
1184 if let Some(db) = self.active_fork_db_mut() {
1185 for addr in persistent_accounts {
1186 let Ok(db_account) = db.load_account(addr) else { continue };
1187
1188 let Some(fork_account) = fork.journaled_state.state.get_mut(&addr) else {
1189 continue;
1190 };
1191
1192 for (key, val) in &db_account.storage {
1193 if let Some(fork_storage) = fork_account.storage.get_mut(key) {
1194 fork_storage.present_value = *val;
1195 }
1196 }
1197 }
1198 }
1199
1200 fork.journaled_state.depth = active_journaled_state.depth;
1205
1206 let caller = tx_env.caller();
1210 fork.journaled_state.state.entry(caller).or_insert_with(|| {
1211 let caller_account = active_journaled_state
1212 .state
1213 .get(&caller)
1214 .map(|acc| acc.info.clone())
1215 .unwrap_or_default();
1216
1217 if !fork.db.cache.accounts.contains_key(&caller) {
1218 fork.db.insert_account_info(caller, caller_account.clone());
1220 }
1221 caller_account.into()
1222 });
1223
1224 self.update_fork_db(active_journaled_state, &mut fork);
1225
1226 self.inner.set_fork(idx, fork);
1228 }
1229
1230 self.active_fork_ids = Some((id, idx));
1231 let preserved_spec = evm_env.cfg_env.spec;
1235 tx_env.set_chain_id(Some(fork_evm_env.cfg_env.chain_id));
1236 *evm_env = fork_evm_env;
1237 evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1238
1239 Ok(())
1240 }
1241
1242 fn roll_fork(
1245 &mut self,
1246 id: Option<LocalForkId>,
1247 block_number: u64,
1248 evm_env: &mut EvmEnvFor<FEN>,
1249 journaled_state: &mut JournaledState,
1250 ) -> eyre::Result<()> {
1251 trace!(?id, ?block_number, "roll fork");
1252 let id = self.ensure_fork(id)?;
1253 let (fork_id, backend, fork_env) =
1254 self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?;
1255 self.inner.roll_fork(id, fork_id, backend)?;
1257
1258 if let Some((active_id, active_idx)) = self.active_fork_ids {
1259 if active_id == id {
1261 let preserved_spec = evm_env.cfg_env.spec;
1264 *evm_env = fork_env;
1265 evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1266
1267 let mut persistent_addrs = self.inner.persistent_accounts.clone();
1272 persistent_addrs.extend(self.caller_address());
1274
1275 let active = self.inner.get_fork_mut(active_idx);
1276 active.journaled_state = self.fork_init_journaled_state.clone();
1277 active.journaled_state.depth = journaled_state.depth;
1278
1279 for addr in persistent_addrs {
1280 merge_journaled_state_data(addr, journaled_state, &mut active.journaled_state);
1281 }
1282
1283 for (addr, acc) in &journaled_state.state {
1291 if acc.is_created() {
1292 if acc.is_touched() {
1293 merge_journaled_state_data(
1294 *addr,
1295 journaled_state,
1296 &mut active.journaled_state,
1297 );
1298 }
1299 } else {
1300 let _ = active.journaled_state.load_account(&mut active.db, *addr);
1301 }
1302 }
1303
1304 *journaled_state = active.journaled_state.clone();
1305 }
1306 }
1307 Ok(())
1308 }
1309
1310 fn roll_fork_to_transaction(
1311 &mut self,
1312 id: Option<LocalForkId>,
1313 transaction: B256,
1314 evm_env: &mut EvmEnvFor<FEN>,
1315 journaled_state: &mut JournaledState,
1316 ) -> eyre::Result<()> {
1317 trace!(?id, ?transaction, "roll fork to transaction");
1318 let id = self.ensure_fork(id)?;
1319
1320 let (fork_block, block) =
1321 self.get_block_number_and_block_for_transaction(id, transaction)?;
1322
1323 self.roll_fork(Some(id), fork_block, evm_env, journaled_state)?;
1327
1328 update_env_block(evm_env, block.header());
1330
1331 let _ = self
1334 .forks
1335 .update_block_env(self.inner.ensure_fork_id(id).cloned()?, evm_env.block_env.clone());
1336
1337 self.replay_until(id, evm_env.clone(), transaction, journaled_state)?;
1339
1340 Ok(())
1341 }
1342
1343 fn transact(
1344 &mut self,
1345 maybe_id: Option<LocalForkId>,
1346 transaction: B256,
1347 mut evm_env: EvmEnvFor<FEN>,
1348 journaled_state: &mut JournaledState,
1349 inspector: &mut dyn for<'db> FoundryInspectorExt<
1350 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
1351 >,
1352 ) -> eyre::Result<()> {
1353 trace!(?maybe_id, ?transaction, "execute transaction");
1354 let persistent_accounts = self.inner.persistent_accounts.clone();
1355 let id = self.ensure_fork(maybe_id)?;
1356 let fork_id = self.ensure_fork_id(id).cloned()?;
1357
1358 let tx = {
1359 let fork = self.inner.get_fork_by_id_mut(id)?;
1360 fork.backend().get_transaction(transaction)?
1361 };
1362 let tx_env = TxEnvFor::<FEN>::from_any_rpc_transaction(&tx)?;
1363
1364 let (_fork_block, block) =
1371 self.get_block_number_and_block_for_transaction(id, transaction)?;
1372 update_env_block(&mut evm_env, block.header());
1373
1374 let fork = self.inner.get_fork_by_id_mut(id)?;
1375 commit_transaction::<FEN>(
1376 evm_env,
1377 tx_env,
1378 journaled_state,
1379 fork,
1380 &fork_id,
1381 &persistent_accounts,
1382 inspector,
1383 )
1384 }
1385
1386 fn transact_from_tx(
1387 &mut self,
1388 tx_env: TxEnvFor<FEN>,
1389 evm_env: EvmEnvFor<FEN>,
1390 journaled_state: &mut JournaledState,
1391 inspector: &mut dyn for<'db> FoundryInspectorExt<
1392 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
1393 >,
1394 ) -> eyre::Result<()> {
1395 trace!("execute signed transaction");
1396
1397 self.commit(journaled_state.state.clone());
1398
1399 let res = {
1400 let mut db = self.clone();
1401 let depth = journaled_state.depth + 1;
1402 let mut evm =
1403 FEN::EvmFactory::default().create_foundry_nested_evm(&mut db, evm_env, inspector);
1404 evm.journal_inner_mut().depth = depth;
1405 evm.transact_raw(tx_env)?
1406 };
1407
1408 self.commit(res.state);
1409 update_state(&mut journaled_state.state, self, None)?;
1410
1411 Ok(())
1412 }
1413
1414 fn active_fork_id(&self) -> Option<LocalForkId> {
1415 self.active_fork_ids.map(|(id, _)| id)
1416 }
1417
1418 fn active_fork_url(&self) -> Option<String> {
1419 let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
1420 self.forks.get_fork_url(fork.clone()).ok()?
1421 }
1422
1423 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
1424 if let Some(id) = id {
1425 if self.inner.issued_local_fork_ids.contains_key(&id) {
1426 return Ok(id);
1427 }
1428 eyre::bail!("Requested fork `{}` does not exist", id)
1429 }
1430 if let Some(id) = self.active_fork_id() { Ok(id) } else { eyre::bail!("No fork active") }
1431 }
1432
1433 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
1434 self.inner.ensure_fork_id(id)
1435 }
1436
1437 fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic> {
1438 let active_id = self.active_fork_id()?;
1439 let active_fork = self.active_fork()?;
1440
1441 if self.inner.forks.len() == 1 {
1442 return None;
1445 }
1446
1447 if !active_fork.is_contract(callee) && !is_contract_in_state(evm_state, callee) {
1448 let mut available_on = Vec::new();
1450 for (id, fork) in self.inner.forks_iter().filter(|(id, _)| *id != active_id) {
1451 trace!(?id, address=?callee, "checking if account exists");
1452 if fork.is_contract(callee) {
1453 available_on.push(id);
1454 }
1455 }
1456
1457 return if available_on.is_empty() {
1458 Some(RevertDiagnostic::ContractDoesNotExist {
1459 contract: callee,
1460 active: active_id,
1461 persistent: self.is_persistent(&callee),
1462 })
1463 } else {
1464 Some(RevertDiagnostic::ContractExistsOnOtherForks {
1467 contract: callee,
1468 active: active_id,
1469 available_on,
1470 })
1471 };
1472 }
1473 None
1474 }
1475
1476 fn load_allocs(
1480 &mut self,
1481 allocs: &BTreeMap<Address, GenesisAccount>,
1482 journaled_state: &mut JournaledState,
1483 ) -> Result<(), BackendError> {
1484 for (addr, acc) in allocs {
1486 self.clone_account(acc, addr, journaled_state)?;
1487 }
1488
1489 Ok(())
1490 }
1491
1492 fn clone_account(
1497 &mut self,
1498 source: &GenesisAccount,
1499 target: &Address,
1500 journaled_state: &mut JournaledState,
1501 ) -> Result<(), BackendError> {
1502 let mut state_acc = journaled_state.load_account_mut(self, *target)?;
1505
1506 if let Some(bytecode) = source.code.as_ref() {
1508 let bytecode_hash = keccak256(bytecode);
1509 let bytecode = Bytecode::new_raw(bytecode.0.clone().into());
1510 state_acc.set_code(bytecode_hash, bytecode);
1511 }
1512
1513 state_acc.set_balance(source.balance);
1515
1516 if let Some(acc) = journaled_state.state.get_mut(target) {
1518 if let Some(storage) = source.storage.as_ref() {
1519 for (slot, value) in storage {
1520 let slot = U256::from_be_bytes(slot.0);
1521 acc.storage.insert(
1522 slot,
1523 EvmStorageSlot::new_changed(
1524 acc.storage.get(&slot).map(|s| s.present_value).unwrap_or_default(),
1525 U256::from_be_bytes(value.0),
1526 0,
1527 ),
1528 );
1529 }
1530 }
1531
1532 acc.info.nonce = source.nonce.unwrap_or_default();
1534 };
1535
1536 journaled_state.touch(*target);
1538
1539 Ok(())
1540 }
1541
1542 fn add_persistent_account(&mut self, account: Address) -> bool {
1543 trace!(?account, "add persistent account");
1544 self.inner.persistent_accounts.insert(account)
1545 }
1546
1547 fn remove_persistent_account(&mut self, account: &Address) -> bool {
1548 trace!(?account, "remove persistent account");
1549 self.inner.persistent_accounts.remove(account)
1550 }
1551
1552 fn is_persistent(&self, acc: &Address) -> bool {
1553 self.inner.persistent_accounts.contains(acc)
1554 }
1555
1556 fn allow_cheatcode_access(&mut self, account: Address) -> bool {
1557 trace!(?account, "allow cheatcode access");
1558 self.inner.cheatcode_access_accounts.insert(account)
1559 }
1560
1561 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
1562 trace!(?account, "revoke cheatcode access");
1563 self.inner.cheatcode_access_accounts.remove(account)
1564 }
1565
1566 fn has_cheatcode_access(&self, account: &Address) -> bool {
1567 self.inner.cheatcode_access_accounts.contains(account)
1568 }
1569
1570 fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
1571 if let Some(db) = self.active_fork_db_mut() {
1572 db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
1573 } else {
1574 self.mem_db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
1575 }
1576 }
1577}
1578
1579impl<FEN: FoundryEvmNetwork> DatabaseRef for Backend<FEN> {
1580 type Error = DatabaseError;
1581
1582 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
1583 if let Some(db) = self.active_fork_db() {
1584 db.basic_ref(address)
1585 } else {
1586 Ok(self.mem_db.basic_ref(address)?)
1587 }
1588 }
1589
1590 fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
1591 if let Some(db) = self.active_fork_db() {
1592 db.code_by_hash_ref(code_hash)
1593 } else {
1594 Ok(self.mem_db.code_by_hash_ref(code_hash)?)
1595 }
1596 }
1597
1598 fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
1599 if let Some(db) = self.active_fork_db() {
1600 DatabaseRef::storage_ref(db, address, index)
1601 } else {
1602 Ok(DatabaseRef::storage_ref(&self.mem_db, address, index)?)
1603 }
1604 }
1605
1606 fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
1607 if let Some(db) = self.active_fork_db() {
1608 db.block_hash_ref(number)
1609 } else {
1610 Ok(self.mem_db.block_hash_ref(number)?)
1611 }
1612 }
1613}
1614
1615impl<FEN: FoundryEvmNetwork> DatabaseCommit for Backend<FEN> {
1616 fn commit(&mut self, changes: AddressMap<Account>) {
1617 if let Some(db) = self.active_fork_db_mut() {
1618 db.commit(changes)
1619 } else {
1620 self.mem_db.commit(changes)
1621 }
1622 }
1623}
1624
1625impl<FEN: FoundryEvmNetwork> Database for Backend<FEN> {
1626 type Error = DatabaseError;
1627 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
1628 if let Some(db) = self.active_fork_db_mut() {
1629 Ok(db.basic(address)?)
1630 } else {
1631 Ok(self.mem_db.basic(address)?)
1632 }
1633 }
1634
1635 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
1636 if let Some(db) = self.active_fork_db_mut() {
1637 Ok(db.code_by_hash(code_hash)?)
1638 } else {
1639 Ok(self.mem_db.code_by_hash(code_hash)?)
1640 }
1641 }
1642
1643 fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
1644 if let Some(db) = self.active_fork_db_mut() {
1645 Ok(Database::storage(db, address, index)?)
1646 } else {
1647 Ok(Database::storage(&mut self.mem_db, address, index)?)
1648 }
1649 }
1650
1651 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
1652 if let Some(db) = self.active_fork_db_mut() {
1653 Ok(db.block_hash(number)?)
1654 } else {
1655 Ok(self.mem_db.block_hash(number)?)
1656 }
1657 }
1658}
1659
1660#[derive(Clone, Debug)]
1662pub enum BackendDatabaseSnapshot<N: Network, B: ForkBlockEnv = BlockEnv> {
1663 InMemory(FoundryEvmInMemoryDB),
1665 Forked(LocalForkId, ForkId, ForkLookupIndex, Box<Fork<N, B>>),
1667}
1668
1669#[derive(Clone, Debug)]
1671pub struct Fork<N: Network, B: ForkBlockEnv = BlockEnv> {
1672 db: ForkDB<N, B>,
1673 journaled_state: JournaledState,
1674}
1675
1676impl<N: Network, B: ForkBlockEnv> Fork<N, B> {
1677 pub const fn backend(&self) -> &SharedBackend<N, B> {
1679 &self.db.db
1680 }
1681
1682 pub fn is_contract(&self, acc: Address) -> bool {
1684 if let Ok(Some(acc)) = self.db.basic_ref(acc)
1685 && acc.code_hash != KECCAK_EMPTY
1686 {
1687 return true;
1688 }
1689 is_contract_in_state(&self.journaled_state.state, acc)
1690 }
1691
1692 fn refresh_journaled_states(
1695 &mut self,
1696 journaled_state: &mut JournaledState,
1697 persistent_accounts: &HashSet<Address>,
1698 ) -> Result<(), BackendError> {
1699 update_state(&mut journaled_state.state, &mut self.db, Some(persistent_accounts))?;
1700 update_state(&mut self.journaled_state.state, &mut self.db, Some(persistent_accounts))?;
1701 Ok(())
1702 }
1703}
1704
1705pub struct BackendInner<FEN: FoundryEvmNetwork> {
1707 pub launched_with_fork: Option<(ForkId, LocalForkId, ForkLookupIndex)>,
1712 pub issued_local_fork_ids: HashMap<LocalForkId, ForkId>,
1724 pub created_forks: HashMap<ForkId, ForkLookupIndex>,
1727 pub forks: Vec<Option<Fork<AnyNetwork, BlockEnvFor<FEN>>>>,
1730 #[allow(clippy::type_complexity)]
1732 pub state_snapshots: StateSnapshots<
1733 BackendStateSnapshot<
1734 BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
1735 SpecFor<FEN>,
1736 BlockEnvFor<FEN>,
1737 >,
1738 >,
1739 pub has_state_snapshot_failure: bool,
1749 pub caller: Option<Address>,
1751 pub next_fork_id: LocalForkId,
1753 pub persistent_accounts: HashSet<Address>,
1757 pub spec_id: SpecFor<FEN>,
1759 pub cheatcode_access_accounts: HashSet<Address>,
1761}
1762
1763impl<FEN: FoundryEvmNetwork> Clone for BackendInner<FEN> {
1764 fn clone(&self) -> Self {
1765 Self {
1766 launched_with_fork: self.launched_with_fork.clone(),
1767 issued_local_fork_ids: self.issued_local_fork_ids.clone(),
1768 created_forks: self.created_forks.clone(),
1769 forks: self.forks.clone(),
1770 state_snapshots: self.state_snapshots.clone(),
1771 has_state_snapshot_failure: self.has_state_snapshot_failure,
1772 caller: self.caller,
1773 next_fork_id: self.next_fork_id,
1774 persistent_accounts: self.persistent_accounts.clone(),
1775 spec_id: self.spec_id,
1776 cheatcode_access_accounts: self.cheatcode_access_accounts.clone(),
1777 }
1778 }
1779}
1780
1781impl<FEN: FoundryEvmNetwork> Debug for BackendInner<FEN> {
1782 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1783 f.debug_struct("BackendInner")
1784 .field("launched_with_fork", &self.launched_with_fork)
1785 .field("issued_local_fork_ids", &self.issued_local_fork_ids)
1786 .field("created_forks", &self.created_forks)
1787 .field("forks", &self.forks)
1788 .field("state_snapshots", &self.state_snapshots)
1789 .field("has_state_snapshot_failure", &self.has_state_snapshot_failure)
1790 .field("caller", &self.caller)
1791 .field("next_fork_id", &self.next_fork_id)
1792 .field("persistent_accounts", &self.persistent_accounts)
1793 .field("spec_id", &self.spec_id)
1794 .field("cheatcode_access_accounts", &self.cheatcode_access_accounts)
1795 .finish()
1796 }
1797}
1798
1799impl<FEN: FoundryEvmNetwork> BackendInner<FEN> {
1800 pub fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
1801 self.issued_local_fork_ids
1802 .get(&id)
1803 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
1804 }
1805
1806 pub fn ensure_fork_index(&self, id: &ForkId) -> eyre::Result<ForkLookupIndex> {
1807 self.created_forks
1808 .get(id)
1809 .copied()
1810 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
1811 }
1812
1813 pub fn ensure_fork_index_by_local_id(&self, id: LocalForkId) -> eyre::Result<ForkLookupIndex> {
1814 self.ensure_fork_index(self.ensure_fork_id(id)?)
1815 }
1816
1817 #[track_caller]
1819 fn get_fork(&self, idx: ForkLookupIndex) -> &Fork<AnyNetwork, BlockEnvFor<FEN>> {
1820 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1821 self.forks[idx].as_ref().unwrap()
1822 }
1823
1824 #[track_caller]
1826 fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork<AnyNetwork, BlockEnvFor<FEN>> {
1827 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1828 self.forks[idx].as_mut().unwrap()
1829 }
1830
1831 #[track_caller]
1833 fn get_fork_by_id_mut(
1834 &mut self,
1835 id: LocalForkId,
1836 ) -> eyre::Result<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
1837 let idx = self.ensure_fork_index_by_local_id(id)?;
1838 Ok(self.get_fork_mut(idx))
1839 }
1840
1841 #[track_caller]
1843 fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
1844 let idx = self.ensure_fork_index_by_local_id(id)?;
1845 Ok(self.get_fork(idx))
1846 }
1847
1848 fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork<AnyNetwork, BlockEnvFor<FEN>> {
1850 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1851 self.forks[idx].take().unwrap()
1852 }
1853
1854 fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork<AnyNetwork, BlockEnvFor<FEN>>) {
1855 self.forks[idx] = Some(fork)
1856 }
1857
1858 pub fn forks_iter(
1860 &self,
1861 ) -> impl Iterator<Item = (LocalForkId, &Fork<AnyNetwork, BlockEnvFor<FEN>>)> + '_ {
1862 self.issued_local_fork_ids
1863 .iter()
1864 .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id])))
1865 }
1866
1867 pub fn forks_iter_mut(
1869 &mut self,
1870 ) -> impl Iterator<Item = &mut Fork<AnyNetwork, BlockEnvFor<FEN>>> + '_ {
1871 self.forks.iter_mut().filter_map(|f| f.as_mut())
1872 }
1873
1874 pub fn revert_state_snapshot(
1876 &mut self,
1877 id: LocalForkId,
1878 fork_id: ForkId,
1879 idx: ForkLookupIndex,
1880 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
1881 ) {
1882 self.created_forks.insert(fork_id.clone(), idx);
1883 self.issued_local_fork_ids.insert(id, fork_id);
1884 self.set_fork(idx, fork)
1885 }
1886
1887 pub fn update_fork_mapping(
1889 &mut self,
1890 id: LocalForkId,
1891 fork_id: ForkId,
1892 db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
1893 journaled_state: JournaledState,
1894 ) -> ForkLookupIndex {
1895 let idx = self.forks.len();
1896 self.issued_local_fork_ids.insert(id, fork_id.clone());
1897 self.created_forks.insert(fork_id, idx);
1898
1899 let fork = Fork { db, journaled_state };
1900 self.forks.push(Some(fork));
1901 idx
1902 }
1903
1904 pub fn roll_fork(
1905 &mut self,
1906 id: LocalForkId,
1907 new_fork_id: ForkId,
1908 backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
1909 ) -> eyre::Result<ForkLookupIndex> {
1910 let fork_id = self.ensure_fork_id(id)?;
1911 let idx = self.ensure_fork_index(fork_id)?;
1912
1913 if let Some(active) = self.forks[idx].as_mut() {
1914 let mut new_db = ForkDB::new(backend);
1916 for addr in self.persistent_accounts.iter().copied() {
1917 merge_db_account_data(addr, &active.db, &mut new_db);
1918 }
1919 active.db = new_db;
1920 }
1921 self.issued_local_fork_ids.insert(id, new_fork_id.clone());
1923 self.created_forks.insert(new_fork_id, idx);
1924 Ok(idx)
1925 }
1926
1927 pub fn insert_new_fork(
1931 &mut self,
1932 fork_id: ForkId,
1933 db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
1934 journaled_state: JournaledState,
1935 ) -> (LocalForkId, ForkLookupIndex) {
1936 let idx = self.forks.len();
1937 self.created_forks.insert(fork_id.clone(), idx);
1938 let id = self.next_id();
1939 self.issued_local_fork_ids.insert(id, fork_id);
1940 let fork = Fork { db, journaled_state };
1941 self.forks.push(Some(fork));
1942 (id, idx)
1943 }
1944
1945 fn next_id(&mut self) -> U256 {
1946 let id = self.next_fork_id;
1947 self.next_fork_id += U256::from(1);
1948 id
1949 }
1950
1951 pub fn len(&self) -> usize {
1953 self.issued_local_fork_ids.len()
1954 }
1955
1956 pub fn is_empty(&self) -> bool {
1958 self.issued_local_fork_ids.is_empty()
1959 }
1960
1961 pub fn precompiles(&self) -> PrecompilesFor<FEN> {
1962 let evm = FEN::EvmFactory::default().create_evm(
1963 EmptyDB::default(),
1964 EvmEnv::new(CfgEnv::new_with_spec(self.spec_id), Default::default()),
1965 );
1966 evm.precompiles().clone()
1967 }
1968
1969 pub fn new_journaled_state(&self) -> JournaledState {
1971 let mut journal = {
1972 let mut journal_inner = JournalInner::new();
1973 journal_inner.set_spec_id(self.spec_id.into());
1974 journal_inner
1975 };
1976 journal
1977 .warm_addresses
1978 .set_precompile_addresses(self.precompiles().addresses().copied().collect());
1979 journal
1980 }
1981}
1982
1983impl<FEN: FoundryEvmNetwork> Default for BackendInner<FEN> {
1984 fn default() -> Self {
1985 Self {
1986 launched_with_fork: None,
1987 issued_local_fork_ids: Default::default(),
1988 created_forks: Default::default(),
1989 forks: vec![],
1990 state_snapshots: Default::default(),
1991 has_state_snapshot_failure: false,
1992 caller: None,
1993 next_fork_id: Default::default(),
1994 persistent_accounts: Default::default(),
1995 spec_id: SpecFor::<FEN>::default(),
1996 cheatcode_access_accounts: HashSet::from([
1999 CHEATCODE_ADDRESS,
2000 TEST_CONTRACT_ADDRESS,
2001 CALLER,
2002 ]),
2003 }
2004 }
2005}
2006
2007pub(crate) fn merge_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
2010 accounts: impl IntoIterator<Item = Address>,
2011 active: &CacheDB<ExtDB>,
2012 active_journaled_state: &mut JournaledState,
2013 target_fork: &mut Fork<N, B>,
2014) {
2015 for addr in accounts {
2016 merge_db_account_data(addr, active, &mut target_fork.db);
2017 merge_journaled_state_data(addr, active_journaled_state, &mut target_fork.journaled_state);
2018 }
2019
2020 *active_journaled_state = target_fork.journaled_state.clone();
2021}
2022
2023fn merge_journaled_state_data(
2025 addr: Address,
2026 active_journaled_state: &JournaledState,
2027 fork_journaled_state: &mut JournaledState,
2028) {
2029 if let Some(mut acc) = active_journaled_state.state.get(&addr).cloned() {
2030 trace!(?addr, "updating journaled_state account data");
2031 if let Some(fork_account) = fork_journaled_state.state.get_mut(&addr) {
2032 fork_account.storage.extend(std::mem::take(&mut acc.storage));
2034 std::mem::swap(&mut fork_account.storage, &mut acc.storage);
2036 }
2037 fork_journaled_state.state.insert(addr, acc);
2038 }
2039}
2040
2041fn merge_db_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
2043 addr: Address,
2044 active: &CacheDB<ExtDB>,
2045 fork_db: &mut ForkDB<N, B>,
2046) {
2047 trace!(?addr, "merging database data");
2048
2049 let Some(acc) = active.cache.accounts.get(&addr) else { return };
2050
2051 if let Some(code) = active.cache.contracts.get(&acc.info.code_hash) {
2053 trace!("merging contract cache");
2054 fork_db.cache.contracts.insert(acc.info.code_hash, code.clone());
2055 }
2056
2057 use std::collections::hash_map::Entry;
2059 match fork_db.cache.accounts.entry(addr) {
2060 Entry::Vacant(vacant) => {
2061 trace!("target account not present - inserting from active");
2062 vacant.insert(acc.clone());
2065 }
2066 Entry::Occupied(mut occupied) => {
2067 trace!("target account present - merging storage slots");
2068 let fork_account = occupied.get_mut();
2071 fork_account.storage.extend(&acc.storage);
2072 }
2073 }
2074}
2075
2076fn is_contract_in_state(evm_state: &EvmState, acc: Address) -> bool {
2078 evm_state.get(&acc).map(|acc| acc.info.code_hash != KECCAK_EMPTY).unwrap_or_default()
2079}
2080
2081fn update_env_block<SPEC, BLOCK: FoundryBlock>(
2083 evm_env: &mut EvmEnv<SPEC, BLOCK>,
2084 header: &impl BlockHeader,
2085) {
2086 let block_env = &mut evm_env.block_env;
2087 block_env.set_timestamp(U256::from(header.timestamp()));
2088 block_env.set_beneficiary(header.beneficiary());
2089 block_env.set_difficulty(header.difficulty());
2090 block_env.set_prevrandao(header.mix_hash());
2091 block_env.set_basefee(header.base_fee_per_gas().unwrap_or_default());
2092 block_env.set_gas_limit(header.gas_limit());
2093 block_env.set_number(U256::from(header.number()));
2094
2095 if let Some(excess_blob_gas) = header.excess_blob_gas() {
2096 evm_env.block_env.set_blob_excess_gas_and_price(
2097 excess_blob_gas,
2098 get_blob_base_fee_update_fraction(evm_env.cfg_env.chain_id, header.timestamp()),
2099 );
2100 }
2101}
2102
2103fn commit_transaction<FEN: FoundryEvmNetwork>(
2106 evm_env: EvmEnvFor<FEN>,
2107 tx_env: TxEnvFor<FEN>,
2108 journaled_state: &mut JournaledState,
2109 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
2110 fork_id: &ForkId,
2111 persistent_accounts: &HashSet<Address>,
2112 inspector: &mut dyn for<'db> FoundryInspectorExt<
2113 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2114 >,
2115) -> eyre::Result<()> {
2116 let now = Instant::now();
2117 let res = {
2118 let fork = fork.clone();
2119 let journaled_state = journaled_state.clone();
2120 let depth = journaled_state.depth;
2121 let mut db: Backend<FEN> = Backend::new_with_fork(fork_id, fork, journaled_state)?;
2122
2123 let mut evm =
2124 FEN::EvmFactory::default().create_foundry_nested_evm(&mut db, evm_env, inspector);
2125 evm.journal_inner_mut().depth = depth + 1;
2126 evm.transact_raw(tx_env).wrap_err("backend: failed committing transaction")?
2127 };
2128 trace!(elapsed = ?now.elapsed(), "transacted transaction");
2129
2130 apply_state_changeset(res.state, journaled_state, fork, persistent_accounts)?;
2131 Ok(())
2132}
2133
2134pub fn update_state<DB: Database>(
2137 state: &mut EvmState,
2138 db: &mut DB,
2139 persistent_accounts: Option<&HashSet<Address>>,
2140) -> Result<(), DB::Error> {
2141 for (addr, acc) in state.iter_mut() {
2142 if persistent_accounts.is_none_or(|accounts| !accounts.contains(addr)) {
2143 acc.info = db.basic(*addr)?.unwrap_or_default();
2144 for (key, val) in &mut acc.storage {
2145 val.present_value = db.storage(*addr, *key)?;
2146 }
2147 }
2148 }
2149
2150 Ok(())
2151}
2152
2153fn apply_state_changeset<N: Network, B: ForkBlockEnv>(
2156 state: EvmState,
2157 journaled_state: &mut JournaledState,
2158 fork: &mut Fork<N, B>,
2159 persistent_accounts: &HashSet<Address>,
2160) -> Result<(), BackendError> {
2161 fork.db.commit(state);
2163 fork.refresh_journaled_states(journaled_state, persistent_accounts)
2164}
2165
2166#[cfg(test)]
2167mod tests {
2168 use crate::{backend::Backend, evm::EthEvmNetwork, opts::EvmOpts};
2169 use alloy_primitives::{U256, address};
2170 use alloy_provider::Provider;
2171 use foundry_common::provider::get_http_provider;
2172 use foundry_config::{Config, NamedChain};
2173 use foundry_fork_db::cache::{BlockchainDb, BlockchainDbMeta};
2174 use revm::{
2175 context::{BlockEnv, TxEnv},
2176 database::DatabaseRef,
2177 primitives::hardfork::SpecId,
2178 };
2179
2180 #[tokio::test(flavor = "multi_thread")]
2181 async fn can_read_write_cache() {
2182 let endpoint = &*foundry_test_utils::rpc::next_http_rpc_endpoint();
2183 let provider = get_http_provider(endpoint);
2184
2185 let block_num = provider.get_block_number().await.unwrap();
2186
2187 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
2188 evm_opts.fork_url = Some(endpoint.to_string());
2189 evm_opts.fork_block_number = Some(block_num);
2190
2191 let (evm_env, _, fork_block) = evm_opts.env::<SpecId, BlockEnv, TxEnv>().await.unwrap();
2192
2193 let fork =
2194 evm_opts.get_fork(&Config::default(), evm_env.cfg_env.chain_id, fork_block).unwrap();
2195
2196 let backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
2197
2198 let address = address!("0x63091244180ae240c87d1f528f5f269134cb07b3");
2200
2201 let num_slots = 5;
2202 let _account = backend.basic_ref(address);
2203 for idx in 0..num_slots {
2204 let _ = backend.storage_ref(address, U256::from(idx));
2205 }
2206 drop(backend);
2207
2208 let meta = BlockchainDbMeta {
2209 chain: None,
2210 block_env: evm_env.block_env,
2211 hosts: Default::default(),
2212 };
2213
2214 let db = BlockchainDb::new(
2215 meta,
2216 Some(Config::foundry_block_cache_dir(NamedChain::Mainnet, block_num).unwrap()),
2217 );
2218 assert!(db.accounts().read().contains_key(&address));
2219 assert!(db.storage().read().contains_key(&address));
2220 assert_eq!(db.storage().read().get(&address).unwrap().len(), num_slots as usize);
2221 }
2222}