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, 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, map::AddressSet, uint};
21use alloy_rpc_types::BlockNumberOrTag;
22use eyre::Context;
23use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender};
24use foundry_evm_networks::NetworkConfigs;
25pub use foundry_fork_db::{BlockchainDb, ForkBlockEnv, SharedBackend, cache::BlockchainDbMeta};
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::{AccountState, CacheDB, DatabaseRef, EmptyDB},
32 primitives::{AddressMap, HashMap as Map, KECCAK_EMPTY, Log},
33 state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
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 active_fork_block_number(&self) -> Option<u64> {
244 None
245 }
246
247 fn is_forked_mode(&self) -> bool {
249 self.active_fork_id().is_some()
250 }
251
252 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId>;
263
264 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId>;
266
267 fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic>;
294
295 fn load_allocs(
299 &mut self,
300 allocs: &BTreeMap<Address, GenesisAccount>,
301 journaled_state: &mut JournaledState,
302 ) -> Result<(), BackendError>;
303
304 fn clone_account(
309 &mut self,
310 source: &GenesisAccount,
311 target: &Address,
312 journaled_state: &mut JournaledState,
313 ) -> Result<(), BackendError>;
314
315 fn is_persistent(&self, acc: &Address) -> bool;
317
318 fn invalidate_fork_cache_account(&mut self, address: Address);
321
322 fn invalidate_fork_cache_storage(&mut self, address: Address, slot: U256);
325
326 fn remove_persistent_account(&mut self, account: &Address) -> bool;
328
329 fn add_persistent_account(&mut self, account: Address) -> bool;
331
332 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
334 fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
335 where
336 Self: Sized,
337 {
338 for acc in accounts {
339 self.remove_persistent_account(&acc);
340 }
341 }
342
343 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
345 fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
346 where
347 Self: Sized,
348 {
349 for acc in accounts {
350 self.add_persistent_account(acc);
351 }
352 }
353
354 fn allow_cheatcode_access(&mut self, account: Address) -> bool;
358
359 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool;
363
364 fn has_cheatcode_access(&self, account: &Address) -> bool;
366
367 fn ensure_cheatcode_access(&self, account: &Address) -> Result<(), BackendError> {
371 if !self.has_cheatcode_access(account) {
372 return Err(BackendError::NoCheats(*account));
373 }
374 Ok(())
375 }
376
377 fn ensure_cheatcode_access_forking_mode(&self, account: &Address) -> Result<(), BackendError> {
380 if self.is_forked_mode() {
381 return self.ensure_cheatcode_access(account);
382 }
383 Ok(())
384 }
385
386 fn set_blockhash(&mut self, block_number: U256, block_hash: B256);
401}
402
403#[must_use]
456pub struct Backend<FEN: FoundryEvmNetwork = EthEvmNetwork> {
457 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
459 mem_db: FoundryEvmInMemoryDB,
461 fork_init_journaled_state: JournaledState,
478 active_fork_ids: Option<(LocalForkId, ForkLookupIndex)>,
482 inner: BackendInner<FEN>,
484}
485
486impl<FEN: FoundryEvmNetwork> Clone for Backend<FEN> {
487 fn clone(&self) -> Self {
488 Self {
489 forks: self.forks.clone(),
490 mem_db: self.mem_db.clone(),
491 fork_init_journaled_state: self.fork_init_journaled_state.clone(),
492 active_fork_ids: self.active_fork_ids,
493 inner: self.inner.clone(),
494 }
495 }
496}
497
498impl<FEN: FoundryEvmNetwork> Debug for Backend<FEN> {
499 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
500 f.debug_struct("Backend")
501 .field("forks", &self.forks)
502 .field("mem_db", &self.mem_db)
503 .field("fork_init_journaled_state", &self.fork_init_journaled_state)
504 .field("active_fork_ids", &self.active_fork_ids)
505 .field("inner", &self.inner)
506 .finish()
507 }
508}
509
510impl<FEN: FoundryEvmNetwork> Backend<FEN> {
511 pub fn spawn(fork: Option<CreateFork>) -> eyre::Result<Self> {
516 Self::new(MultiFork::<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>::spawn(), fork)
517 }
518
519 pub fn new(
526 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
527 fork: Option<CreateFork>,
528 ) -> eyre::Result<Self> {
529 trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend");
530 let inner = BackendInner {
532 persistent_accounts: HashSet::from(DEFAULT_PERSISTENT_ACCOUNTS),
533 ..Default::default()
534 };
535
536 let mut backend = Self {
537 forks,
538 mem_db: CacheDB::new(Default::default()),
539 fork_init_journaled_state: inner.new_journaled_state(),
540 active_fork_ids: None,
541 inner,
542 };
543
544 if let Some(fork) = fork {
545 let (fork_id, fork, _) = backend.forks.create_fork(fork)?;
546 let fork_db = ForkDB::new(fork);
547 let fork_ids = backend.inner.insert_new_fork(
548 fork_id.clone(),
549 fork_db,
550 backend.inner.new_journaled_state(),
551 );
552 backend.inner.launched_with_fork = Some((fork_id, fork_ids.0, fork_ids.1));
553 backend.active_fork_ids = Some(fork_ids);
554 }
555
556 trace!(target: "backend", forking_mode=? backend.active_fork_ids.is_some(), "created executor backend");
557
558 Ok(backend)
559 }
560
561 pub(crate) fn new_with_fork(
564 id: &ForkId,
565 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
566 journaled_state: JournaledState,
567 ) -> eyre::Result<Self> {
568 let mut backend = Self::spawn(None)?;
569 let fork_ids = backend.inner.insert_new_fork(id.clone(), fork.db, journaled_state);
570 backend.inner.launched_with_fork = Some((id.clone(), fork_ids.0, fork_ids.1));
571 backend.active_fork_ids = Some(fork_ids);
572 Ok(backend)
573 }
574
575 pub fn clone_empty(&self) -> Self {
577 Self {
578 forks: self.forks.clone(),
579 mem_db: CacheDB::new(Default::default()),
580 fork_init_journaled_state: self.inner.new_journaled_state(),
581 active_fork_ids: None,
582 inner: Default::default(),
583 }
584 }
585
586 pub fn insert_account_info(&mut self, address: Address, account: AccountInfo) {
587 if let Some(db) = self.active_fork_db_mut() {
588 db.insert_account_info(address, account)
589 } else {
590 self.mem_db.insert_account_info(address, account)
591 }
592 }
593
594 pub fn insert_account_storage(
596 &mut self,
597 address: Address,
598 slot: U256,
599 value: U256,
600 ) -> Result<(), DatabaseError> {
601 if let Some(db) = self.active_fork_db_mut() {
602 db.insert_account_storage(address, slot, value)
603 } else {
604 self.mem_db.insert_account_storage(address, slot, value)
605 }
606 }
607
608 pub fn replace_account_storage(
613 &mut self,
614 address: Address,
615 storage: Map<U256, U256>,
616 ) -> Result<(), DatabaseError> {
617 if let Some(db) = self.active_fork_db_mut() {
618 db.replace_account_storage(address, storage.into_iter().collect())
619 } else {
620 self.mem_db.replace_account_storage(address, storage.into_iter().collect())
621 }
622 }
623
624 #[allow(clippy::type_complexity)]
626 pub const fn state_snapshots(
627 &self,
628 ) -> &StateSnapshots<
629 BackendStateSnapshot<
630 BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
631 SpecFor<FEN>,
632 BlockEnvFor<FEN>,
633 >,
634 > {
635 &self.inner.state_snapshots
636 }
637
638 pub fn set_test_contract(&mut self, acc: Address) -> &mut Self {
645 trace!(?acc, "setting test account");
646 self.inner.persistent_accounts.insert(acc);
647 self.inner.cheatcode_access_accounts.insert(acc);
648 self
649 }
650
651 pub fn set_caller(&mut self, acc: Address) -> &mut Self {
653 trace!(?acc, "setting caller account");
654 self.inner.caller = Some(acc);
655 self.inner.cheatcode_access_accounts.insert(acc);
656 self
657 }
658
659 pub fn set_spec_id(&mut self, spec_id: impl Into<SpecFor<FEN>>) -> &mut Self {
661 self.inner.spec_id = spec_id.into();
662 self
663 }
664
665 pub const fn caller_address(&self) -> Option<Address> {
667 self.inner.caller
668 }
669
670 pub const fn has_state_snapshot_failure(&self) -> bool {
676 self.inner.has_state_snapshot_failure
677 }
678
679 pub const fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) {
681 self.inner.has_state_snapshot_failure = has_state_snapshot_failure
682 }
683
684 pub(crate) fn update_fork_db(
686 &self,
687 active_journaled_state: &mut JournaledState,
688 target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
689 ) {
690 self.update_fork_db_contracts(
691 self.inner.persistent_accounts.iter().copied(),
692 active_journaled_state,
693 target_fork,
694 )
695 }
696
697 pub(crate) fn update_fork_db_contracts(
699 &self,
700 accounts: impl IntoIterator<Item = Address>,
701 active_journaled_state: &mut JournaledState,
702 target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
703 ) {
704 if let Some(db) = self.active_fork_db() {
705 merge_account_data(accounts, db, active_journaled_state, target_fork)
706 } else {
707 merge_account_data(accounts, &self.mem_db, active_journaled_state, target_fork)
708 }
709 }
710
711 pub const fn mem_db(&self) -> &FoundryEvmInMemoryDB {
713 &self.mem_db
714 }
715
716 pub fn is_active_fork(&self, id: LocalForkId) -> bool {
718 self.active_fork_ids.map(|(i, _)| i == id).unwrap_or_default()
719 }
720
721 pub fn is_in_forking_mode(&self) -> bool {
723 self.active_fork().is_some()
724 }
725
726 pub fn active_fork(&self) -> Option<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
728 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx))
729 }
730
731 pub fn active_fork_mut(&mut self) -> Option<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
733 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx))
734 }
735
736 pub fn active_fork_db(&self) -> Option<&ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
738 self.active_fork().map(|f| &f.db)
739 }
740
741 pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
743 self.active_fork_mut().map(|f| &mut f.db)
744 }
745
746 pub fn db(&self) -> &dyn Database<Error = DatabaseError> {
748 match self.active_fork_db() {
749 Some(fork_db) => fork_db,
750 None => &self.mem_db,
751 }
752 }
753
754 pub fn db_mut(&mut self) -> &mut dyn Database<Error = DatabaseError> {
756 match self.active_fork_ids.map(|(_, idx)| &mut self.inner.get_fork_mut(idx).db) {
757 Some(fork_db) => fork_db,
758 None => &mut self.mem_db,
759 }
760 }
761
762 pub(crate) fn create_db_snapshot(
764 &self,
765 ) -> BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>> {
766 if let Some((id, idx)) = self.active_fork_ids {
767 let fork = self.inner.get_fork(idx).clone();
768 let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed");
769 BackendDatabaseSnapshot::Forked(id, fork_id, idx, Box::new(fork))
770 } else {
771 BackendDatabaseSnapshot::InMemory(self.mem_db.clone())
772 }
773 }
774
775 pub fn merged_logs(&self, mut logs: Vec<Log>) -> Vec<Log> {
777 if let Some((_, active)) = self.active_fork_ids {
778 let mut all_logs = Vec::with_capacity(logs.len());
779
780 self.inner
781 .forks
782 .iter()
783 .enumerate()
784 .filter_map(|(idx, f)| f.as_ref().map(|f| (idx, f)))
785 .for_each(|(idx, f)| {
786 if idx == active {
787 all_logs.append(&mut logs);
788 } else {
789 all_logs.extend(f.journaled_state.logs.clone())
790 }
791 });
792 return all_logs;
793 }
794
795 logs
796 }
797
798 pub(crate) fn initialize(
802 &mut self,
803 spec_id: impl Into<SpecFor<FEN>>,
804 caller: Address,
805 tx_kind: TxKind,
806 ) {
807 self.set_caller(caller);
808 self.set_spec_id(spec_id);
809
810 let test_contract = match tx_kind {
811 TxKind::Call(to) => to,
812 TxKind::Create => {
813 let nonce =
814 self.basic_ref(caller).map(|b| b.unwrap_or_default().nonce).unwrap_or_default();
815 caller.create(nonce)
816 }
817 };
818 self.set_test_contract(test_contract);
819 }
820
821 #[instrument(name = "inspect", level = "debug", skip_all)]
826 pub fn inspect<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
827 &mut self,
828 evm_env: &mut EvmEnvFor<FEN>,
829 tx_env: &mut TxEnvFor<FEN>,
830 inspector: I,
831 ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
832 self.initialize(evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind());
833 let mut evm = FEN::EvmFactory::default().create_foundry_evm_with_inspector(
834 self,
835 evm_env.to_owned(),
836 inspector,
837 );
838 let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?;
839
840 *tx_env = evm.tx().clone();
841 *evm_env = evm.finish().1;
842
843 Ok(res)
844 }
845
846 pub fn is_existing_precompile(&self, addr: &Address) -> bool {
848 self.inner.precompile_addresses().contains(addr)
849 }
850
851 #[inline]
853 fn set_init_journaled_state(&mut self, journaled_state: JournaledState) {
854 trace!("recording fork init journaled_state");
855 self.fork_init_journaled_state = journaled_state;
856 }
857
858 fn prepare_init_journal_state(&mut self) -> Result<(), BackendError> {
868 let loaded_accounts = self
869 .fork_init_journaled_state
870 .state
871 .iter()
872 .filter(|(addr, _)| {
873 !self.is_existing_precompile(addr)
874 && !self.inner.persistent_accounts.contains(*addr)
875 })
876 .map(|(addr, _)| addr)
877 .copied()
878 .collect::<Vec<_>>();
879
880 for fork in self.inner.forks_iter_mut() {
881 let mut journaled_state = self.fork_init_journaled_state.clone();
882 for loaded_account in loaded_accounts.iter().copied() {
883 trace!(?loaded_account, "replacing account on init");
884 let init_account =
885 journaled_state.state.get_mut(&loaded_account).expect("exists; qed");
886
887 if init_account.is_created() {
891 trace!(?loaded_account, "skipping created account");
892 continue;
893 }
894
895 let fork_account = Database::basic(&mut fork.db, loaded_account)?
898 .ok_or(BackendError::MissingAccount(loaded_account))?;
899 init_account.info = fork_account;
900 }
901 fork.journaled_state = journaled_state;
902 }
903 Ok(())
904 }
905
906 fn get_block_number_and_block_for_transaction(
908 &self,
909 id: LocalForkId,
910 transaction: B256,
911 ) -> eyre::Result<(u64, AnyRpcBlock)> {
912 let fork = self.inner.get_fork_by_id(id)?;
913 let tx = fork.backend().get_transaction(transaction)?;
914
915 if let Some(tx_block) = tx.block_number() {
917 let block = fork.backend().get_full_block(tx_block)?;
918
919 let fork_block = tx_block - 1;
922 Ok((fork_block, block))
923 } else {
924 let block = fork.backend().get_full_block(BlockNumberOrTag::Latest)?;
925
926 let number = block.header().number();
927
928 Ok((number, block))
929 }
930 }
931
932 pub fn replay_until(
936 &mut self,
937 id: LocalForkId,
938 evm_env: EvmEnvFor<FEN>,
939 tx_hash: B256,
940 journaled_state: &mut JournaledState,
941 ) -> eyre::Result<Option<AnyRpcTransaction>> {
942 trace!(?id, ?tx_hash, "replay until transaction");
943
944 let persistent_accounts = self.inner.persistent_accounts.clone();
945
946 let fork = self.inner.get_fork_by_id_mut(id)?;
947 let full_block =
948 fork.backend().get_full_block(evm_env.block_env.number().saturating_to::<u64>())?;
949
950 let txs = full_block
952 .transactions()
953 .txns()
954 .filter(|tx| !is_known_system_sender(tx.from()) && tx.ty() != SYSTEM_TRANSACTION_TYPE);
955
956 let mut txs_to_replay = Vec::new();
957 let mut target_tx = None;
958 for tx in txs {
959 if tx.tx_hash() == tx_hash {
960 target_tx = Some(tx.clone());
961 break;
962 }
963 txs_to_replay.push(tx.clone());
964 }
965
966 if !txs_to_replay.is_empty() {
968 let now = Instant::now();
969
970 let chain_id = evm_env.cfg_env.chain_id;
973 let timestamp = evm_env.block_env.timestamp().saturating_to();
974 let replay_db = fork.db.clone();
975 let mut evm = FEN::EvmFactory::default().create_evm(replay_db, evm_env);
976 NetworkConfigs::default().inject_chain_precompiles(
977 evm.precompiles_mut(),
978 chain_id,
979 timestamp,
980 );
981
982 for tx in &txs_to_replay {
983 let tx_env = TxEnvFor::<FEN>::from_any_rpc_transaction(tx)?;
984 trace!(tx=?tx.tx_hash(), "committing transaction");
985 evm.transact_commit(tx_env).wrap_err("backend: failed committing transaction")?;
986 }
987
988 fork.db = evm.into_db();
990
991 fork.refresh_journaled_states(journaled_state, &persistent_accounts)?;
994
995 trace!(elapsed=?now.elapsed(), count=txs_to_replay.len(), "replayed transactions");
996 }
997
998 Ok(target_tx)
999 }
1000}
1001
1002impl<FEN: FoundryEvmNetwork> DatabaseExt<FEN::EvmFactory> for Backend<FEN> {
1003 fn snapshot_state(
1004 &mut self,
1005 journaled_state: &JournaledState,
1006 evm_env: &EvmEnvFor<FEN>,
1007 ) -> U256 {
1008 trace!("create snapshot");
1009 let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new(
1010 self.create_db_snapshot(),
1011 journaled_state.clone(),
1012 evm_env.clone(),
1013 ));
1014 trace!(target: "backend", "Created new snapshot {}", id);
1015 id
1016 }
1017
1018 fn revert_state(
1019 &mut self,
1020 id: U256,
1021 current_state: &JournaledState,
1022 evm_env: &mut EvmEnvFor<FEN>,
1023 caller: Address,
1024 action: RevertStateSnapshotAction,
1025 ) -> Option<JournaledState> {
1026 trace!(?id, "revert snapshot");
1027 if let Some(mut snapshot) = self.inner.state_snapshots.remove_at(id) {
1028 if action.is_keep() {
1030 self.inner.state_snapshots.insert_at(snapshot.clone(), id);
1031 }
1032
1033 if let Some(account) = current_state.state.get(&CHEATCODE_ADDRESS)
1038 && let Some(slot) = account.storage.get(&GLOBAL_FAIL_SLOT)
1039 && !slot.present_value.is_zero()
1040 {
1041 self.set_state_snapshot_failure(true);
1042 }
1043
1044 snapshot.merge(current_state);
1046 let BackendStateSnapshot { db, mut journaled_state, snap_evm_env } = snapshot;
1047 match db {
1048 BackendDatabaseSnapshot::InMemory(mem_db) => {
1049 self.mem_db = mem_db;
1050 }
1051 BackendDatabaseSnapshot::Forked(id, fork_id, idx, mut fork) => {
1052 journaled_state.state.entry(caller).or_insert_with(|| {
1056 let caller_account = current_state
1057 .state
1058 .get(&caller)
1059 .map(|acc| acc.info.clone())
1060 .unwrap_or_default();
1061
1062 if !fork.db.cache.accounts.contains_key(&caller) {
1063 fork.db.insert_account_info(caller, caller_account.clone());
1065 }
1066 caller_account.into()
1067 });
1068 self.inner.revert_state_snapshot(id, fork_id, idx, *fork);
1069 self.active_fork_ids = Some((id, idx))
1070 }
1071 }
1072
1073 *evm_env = snap_evm_env;
1074 trace!(target: "backend", "Reverted snapshot {}", id);
1075
1076 Some(journaled_state)
1077 } else {
1078 warn!(target: "backend", "No snapshot to revert for {}", id);
1079 None
1080 }
1081 }
1082
1083 fn delete_state_snapshot(&mut self, id: U256) -> bool {
1084 self.inner.state_snapshots.remove_at(id).is_some()
1085 }
1086
1087 fn delete_state_snapshots(&mut self) {
1088 self.inner.state_snapshots.clear()
1089 }
1090
1091 fn create_fork(&mut self, create_fork: CreateFork) -> eyre::Result<LocalForkId> {
1092 trace!("create fork");
1093 let (fork_id, fork, _) = self.forks.create_fork(create_fork)?;
1094
1095 let fork_db = ForkDB::new(fork);
1096 let (id, _) =
1097 self.inner.insert_new_fork(fork_id, fork_db, self.fork_init_journaled_state.clone());
1098 Ok(id)
1099 }
1100
1101 fn create_fork_at_transaction(
1102 &mut self,
1103 fork: CreateFork,
1104 transaction: B256,
1105 ) -> eyre::Result<LocalForkId> {
1106 trace!(?transaction, "create fork at transaction");
1107 let id = self.create_fork(fork)?;
1108 let fork_id = self.ensure_fork_id(id).cloned()?;
1109 let mut evm_env = self
1110 .forks
1111 .get_evm_env(fork_id)?
1112 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
1113
1114 self.roll_fork_to_transaction(
1117 Some(id),
1118 transaction,
1119 &mut evm_env,
1120 &mut self.inner.new_journaled_state(),
1121 )?;
1122
1123 Ok(id)
1124 }
1125
1126 fn select_fork(
1129 &mut self,
1130 id: LocalForkId,
1131 evm_env: &mut EvmEnvFor<FEN>,
1132 tx_env: &mut TxEnvFor<FEN>,
1133 active_journaled_state: &mut JournaledState,
1134 ) -> eyre::Result<()> {
1135 trace!(?id, "select fork");
1136 if self.is_active_fork(id) {
1137 return Ok(());
1139 }
1140
1141 if let Some(active_fork_id) = self.active_fork_id() {
1144 self.forks.update_block(
1145 self.ensure_fork_id(active_fork_id).cloned()?,
1146 evm_env.block_env.number(),
1147 evm_env.block_env.timestamp(),
1148 )?;
1149 }
1150
1151 let fork_id = self.ensure_fork_id(id).cloned()?;
1152 let idx = self.inner.ensure_fork_index(&fork_id)?;
1153 let fork_evm_env = self
1154 .forks
1155 .get_evm_env(fork_id)?
1156 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
1157
1158 if let Some(active) = self.active_fork_mut() {
1161 active.journaled_state = active_journaled_state.clone();
1162
1163 let caller = tx_env.caller();
1164 let caller_account = active.journaled_state.state.get(&caller).cloned();
1165 let target_fork = self.inner.get_fork_mut(idx);
1166
1167 if target_fork.journaled_state.depth == 0 {
1169 if let Some(mut acc) = caller_account {
1171 let fork_account = Database::basic(&mut target_fork.db, caller)?
1172 .ok_or(BackendError::MissingAccount(caller))?;
1173
1174 acc.info = fork_account;
1175 target_fork.journaled_state.state.insert(caller, acc);
1176 }
1177 }
1178 } else {
1179 self.set_init_journaled_state(active_journaled_state.clone());
1186 self.prepare_init_journal_state()?;
1187
1188 self.fork_init_journaled_state.depth = 0;
1190 }
1191
1192 {
1193 let mut fork = self.inner.take_fork(idx);
1195
1196 let persistent_accounts = self.inner.persistent_accounts.clone();
1204 if let Some(db) = self.active_fork_db_mut() {
1205 for addr in persistent_accounts {
1206 let Ok(db_account) = db.load_account(addr) else { continue };
1207
1208 let Some(fork_account) = fork.journaled_state.state.get_mut(&addr) else {
1209 continue;
1210 };
1211
1212 for (key, val) in &db_account.storage {
1213 if let Some(fork_storage) = fork_account.storage.get_mut(key) {
1214 fork_storage.present_value = *val;
1215 }
1216 }
1217 }
1218 }
1219
1220 fork.journaled_state.depth = active_journaled_state.depth;
1225
1226 let caller = tx_env.caller();
1230 fork.journaled_state.state.entry(caller).or_insert_with(|| {
1231 let caller_account = active_journaled_state
1232 .state
1233 .get(&caller)
1234 .map(|acc| acc.info.clone())
1235 .unwrap_or_default();
1236
1237 if !fork.db.cache.accounts.contains_key(&caller) {
1238 fork.db.insert_account_info(caller, caller_account.clone());
1240 }
1241 caller_account.into()
1242 });
1243
1244 self.update_fork_db(active_journaled_state, &mut fork);
1245
1246 self.inner.set_fork(idx, fork);
1248 }
1249
1250 self.active_fork_ids = Some((id, idx));
1251 let preserved_spec = evm_env.cfg_env.spec;
1255 tx_env.set_chain_id(Some(fork_evm_env.cfg_env.chain_id));
1256 *evm_env = fork_evm_env;
1257 evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1258
1259 Ok(())
1260 }
1261
1262 fn roll_fork(
1265 &mut self,
1266 id: Option<LocalForkId>,
1267 block_number: u64,
1268 evm_env: &mut EvmEnvFor<FEN>,
1269 journaled_state: &mut JournaledState,
1270 ) -> eyre::Result<()> {
1271 trace!(?id, ?block_number, "roll fork");
1272 let id = self.ensure_fork(id)?;
1273 let (fork_id, backend, fork_env) =
1274 self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?;
1275 self.inner.roll_fork(id, fork_id, backend)?;
1277
1278 if let Some((active_id, active_idx)) = self.active_fork_ids {
1279 if active_id == id {
1281 let preserved_spec = evm_env.cfg_env.spec;
1284 *evm_env = fork_env;
1285 evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1286
1287 let mut persistent_addrs = self.inner.persistent_accounts.clone();
1292 persistent_addrs.extend(self.caller_address());
1294
1295 let active = self.inner.get_fork_mut(active_idx);
1296 active.journaled_state = self.fork_init_journaled_state.clone();
1297 active.journaled_state.depth = journaled_state.depth;
1298
1299 for addr in persistent_addrs {
1300 merge_journaled_state_data(addr, journaled_state, &mut active.journaled_state);
1301 }
1302
1303 for (addr, acc) in &journaled_state.state {
1311 if acc.is_created() {
1312 if acc.is_touched() {
1313 merge_journaled_state_data(
1314 *addr,
1315 journaled_state,
1316 &mut active.journaled_state,
1317 );
1318 }
1319 } else {
1320 let _ = active.journaled_state.load_account(&mut active.db, *addr);
1321 }
1322 }
1323
1324 *journaled_state = active.journaled_state.clone();
1325 }
1326 }
1327 Ok(())
1328 }
1329
1330 fn roll_fork_to_transaction(
1331 &mut self,
1332 id: Option<LocalForkId>,
1333 transaction: B256,
1334 evm_env: &mut EvmEnvFor<FEN>,
1335 journaled_state: &mut JournaledState,
1336 ) -> eyre::Result<()> {
1337 trace!(?id, ?transaction, "roll fork to transaction");
1338 let id = self.ensure_fork(id)?;
1339
1340 let (fork_block, block) =
1341 self.get_block_number_and_block_for_transaction(id, transaction)?;
1342
1343 self.roll_fork(Some(id), fork_block, evm_env, journaled_state)?;
1347
1348 update_env_block(evm_env, block.header());
1350
1351 let _ = self
1354 .forks
1355 .update_block_env(self.inner.ensure_fork_id(id).cloned()?, evm_env.block_env.clone());
1356
1357 self.replay_until(id, evm_env.clone(), transaction, journaled_state)?;
1359
1360 Ok(())
1361 }
1362
1363 fn transact(
1364 &mut self,
1365 maybe_id: Option<LocalForkId>,
1366 transaction: B256,
1367 mut evm_env: EvmEnvFor<FEN>,
1368 journaled_state: &mut JournaledState,
1369 inspector: &mut dyn for<'db> FoundryInspectorExt<
1370 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
1371 >,
1372 ) -> eyre::Result<()> {
1373 trace!(?maybe_id, ?transaction, "execute transaction");
1374 let persistent_accounts = self.inner.persistent_accounts.clone();
1375 let id = self.ensure_fork(maybe_id)?;
1376 let fork_id = self.ensure_fork_id(id).cloned()?;
1377
1378 let tx = {
1379 let fork = self.inner.get_fork_by_id_mut(id)?;
1380 fork.backend().get_transaction(transaction)?
1381 };
1382 let tx_env = TxEnvFor::<FEN>::from_any_rpc_transaction(&tx)?;
1383
1384 let (_fork_block, block) =
1391 self.get_block_number_and_block_for_transaction(id, transaction)?;
1392 update_env_block(&mut evm_env, block.header());
1393
1394 let fork = self.inner.get_fork_by_id_mut(id)?;
1395 commit_transaction::<FEN>(
1396 evm_env,
1397 tx_env,
1398 journaled_state,
1399 fork,
1400 &fork_id,
1401 &persistent_accounts,
1402 inspector,
1403 )
1404 }
1405
1406 fn transact_from_tx(
1407 &mut self,
1408 tx_env: TxEnvFor<FEN>,
1409 evm_env: EvmEnvFor<FEN>,
1410 journaled_state: &mut JournaledState,
1411 inspector: &mut dyn for<'db> FoundryInspectorExt<
1412 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
1413 >,
1414 ) -> eyre::Result<()> {
1415 trace!("execute signed transaction");
1416
1417 self.commit(journaled_state.state.clone());
1418
1419 let res = {
1420 let mut db = self.clone();
1421 let depth = journaled_state.depth + 1;
1422 let mut evm =
1423 FEN::EvmFactory::default().create_foundry_nested_evm(&mut db, evm_env, inspector);
1424 evm.journal_inner_mut().depth = depth;
1425 evm.transact_raw(tx_env)?
1426 };
1427
1428 self.commit(res.state);
1429 update_state(&mut journaled_state.state, self, None)?;
1430
1431 Ok(())
1432 }
1433
1434 fn active_fork_id(&self) -> Option<LocalForkId> {
1435 self.active_fork_ids.map(|(id, _)| id)
1436 }
1437
1438 fn active_fork_url(&self) -> Option<String> {
1439 let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
1440 self.forks.get_fork_url(fork.clone()).ok()?
1441 }
1442
1443 fn active_fork_block_number(&self) -> Option<u64> {
1444 let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
1445 let fork_block = fork_block_number(fork);
1446 let env_block = self
1447 .forks
1448 .get_evm_env(fork.clone())
1449 .ok()
1450 .flatten()
1451 .map(|env| env.block_env.number().saturating_to::<u64>());
1452
1453 match (fork_block, env_block) {
1457 (Some(fork_block), Some(env_block)) => Some(fork_block.max(env_block)),
1458 (Some(fork_block), None) => Some(fork_block),
1459 (None, Some(env_block)) => Some(env_block),
1460 (None, None) => None,
1461 }
1462 }
1463
1464 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
1465 if let Some(id) = id {
1466 if self.inner.issued_local_fork_ids.contains_key(&id) {
1467 return Ok(id);
1468 }
1469 eyre::bail!("Requested fork `{}` does not exist", id);
1470 }
1471 if let Some(id) = self.active_fork_id() {
1472 Ok(id)
1473 } else {
1474 eyre::bail!("No fork active");
1475 }
1476 }
1477
1478 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
1479 self.inner.ensure_fork_id(id)
1480 }
1481
1482 fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic> {
1483 let active_id = self.active_fork_id()?;
1484 let active_fork = self.active_fork()?;
1485
1486 if self.inner.forks.len() == 1 {
1487 return None;
1490 }
1491
1492 if !active_fork.is_contract(callee) && !is_contract_in_state(evm_state, callee) {
1493 let mut available_on = Vec::new();
1495 for (id, fork) in self.inner.forks_iter().filter(|(id, _)| *id != active_id) {
1496 trace!(?id, address=?callee, "checking if account exists");
1497 if fork.is_contract(callee) {
1498 available_on.push(id);
1499 }
1500 }
1501
1502 return if available_on.is_empty() {
1503 Some(RevertDiagnostic::ContractDoesNotExist {
1504 contract: callee,
1505 active: active_id,
1506 persistent: self.is_persistent(&callee),
1507 })
1508 } else {
1509 Some(RevertDiagnostic::ContractExistsOnOtherForks {
1512 contract: callee,
1513 active: active_id,
1514 available_on,
1515 })
1516 };
1517 }
1518 None
1519 }
1520
1521 fn load_allocs(
1525 &mut self,
1526 allocs: &BTreeMap<Address, GenesisAccount>,
1527 journaled_state: &mut JournaledState,
1528 ) -> Result<(), BackendError> {
1529 for (addr, acc) in allocs {
1531 self.clone_account(acc, addr, journaled_state)?;
1532 }
1533
1534 Ok(())
1535 }
1536
1537 fn clone_account(
1542 &mut self,
1543 source: &GenesisAccount,
1544 target: &Address,
1545 journaled_state: &mut JournaledState,
1546 ) -> Result<(), BackendError> {
1547 let mut state_acc = journaled_state.load_account_mut(self, *target)?;
1550
1551 if let Some(bytecode) = source.code.as_ref() {
1553 let bytecode_hash = keccak256(bytecode);
1554 let bytecode = Bytecode::new_raw(bytecode.0.clone().into());
1555 state_acc.set_code(bytecode_hash, bytecode);
1556 }
1557
1558 state_acc.set_balance(source.balance);
1560
1561 if let Some(acc) = journaled_state.state.get_mut(target) {
1563 if let Some(storage) = source.storage.as_ref() {
1564 for (slot, value) in storage {
1565 let slot = U256::from_be_bytes(slot.0);
1566 acc.storage.insert(
1567 slot,
1568 EvmStorageSlot::new_changed(
1569 acc.storage.get(&slot).map(|s| s.present_value).unwrap_or_default(),
1570 U256::from_be_bytes(value.0),
1571 TransactionId::ZERO,
1572 ),
1573 );
1574 }
1575 }
1576
1577 acc.info.nonce = source.nonce.unwrap_or_default();
1579 };
1580
1581 journaled_state.touch(*target);
1583
1584 Ok(())
1585 }
1586
1587 fn add_persistent_account(&mut self, account: Address) -> bool {
1588 trace!(?account, "add persistent account");
1589 self.inner.persistent_accounts.insert(account)
1590 }
1591
1592 fn invalidate_fork_cache_account(&mut self, address: Address) {
1593 let Some(fork_db) = self.active_fork_db_mut() else { return };
1594 trace!(?address, "invalidate fork cache account");
1595 fork_db.db.data().accounts.write().remove(&address);
1596 if fork_db
1598 .cache
1599 .accounts
1600 .get(&address)
1601 .is_some_and(|account| account.account_state == AccountState::None)
1602 {
1603 fork_db.cache.accounts.remove(&address);
1604 }
1605 }
1606
1607 fn invalidate_fork_cache_storage(&mut self, address: Address, slot: U256) {
1608 let Some(fork_db) = self.active_fork_db_mut() else { return };
1609 trace!(?address, ?slot, "invalidate fork cache storage");
1610 if let Some(storage) = fork_db.db.data().storage.write().get_mut(&address) {
1611 storage.remove(&slot);
1612 }
1613 if let Some(account) = fork_db.cache.accounts.get_mut(&address)
1615 && account.account_state == AccountState::None
1616 {
1617 account.storage.remove(&slot);
1618 }
1619 }
1620
1621 fn remove_persistent_account(&mut self, account: &Address) -> bool {
1622 trace!(?account, "remove persistent account");
1623 self.inner.persistent_accounts.remove(account)
1624 }
1625
1626 fn is_persistent(&self, acc: &Address) -> bool {
1627 self.inner.persistent_accounts.contains(acc)
1628 }
1629
1630 fn allow_cheatcode_access(&mut self, account: Address) -> bool {
1631 trace!(?account, "allow cheatcode access");
1632 self.inner.cheatcode_access_accounts.insert(account)
1633 }
1634
1635 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
1636 trace!(?account, "revoke cheatcode access");
1637 self.inner.cheatcode_access_accounts.remove(account)
1638 }
1639
1640 fn has_cheatcode_access(&self, account: &Address) -> bool {
1641 self.inner.cheatcode_access_accounts.contains(account)
1642 }
1643
1644 fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
1645 if let Some(db) = self.active_fork_db_mut() {
1646 db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
1647 } else {
1648 self.mem_db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
1649 }
1650 }
1651}
1652
1653impl<FEN: FoundryEvmNetwork> DatabaseRef for Backend<FEN> {
1654 type Error = DatabaseError;
1655
1656 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
1657 if let Some(db) = self.active_fork_db() {
1658 db.basic_ref(address)
1659 } else {
1660 Ok(self.mem_db.basic_ref(address)?)
1661 }
1662 }
1663
1664 fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
1665 if let Some(db) = self.active_fork_db() {
1666 db.code_by_hash_ref(code_hash)
1667 } else {
1668 Ok(self.mem_db.code_by_hash_ref(code_hash)?)
1669 }
1670 }
1671
1672 fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
1673 if let Some(db) = self.active_fork_db() {
1674 DatabaseRef::storage_ref(db, address, index)
1675 } else {
1676 Ok(DatabaseRef::storage_ref(&self.mem_db, address, index)?)
1677 }
1678 }
1679
1680 fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
1681 if let Some(db) = self.active_fork_db() {
1682 db.block_hash_ref(number)
1683 } else {
1684 Ok(self.mem_db.block_hash_ref(number)?)
1685 }
1686 }
1687}
1688
1689impl<FEN: FoundryEvmNetwork> DatabaseCommit for Backend<FEN> {
1690 fn commit(&mut self, changes: AddressMap<Account>) {
1691 if let Some(db) = self.active_fork_db_mut() {
1692 db.commit(changes)
1693 } else {
1694 self.mem_db.commit(changes)
1695 }
1696 }
1697}
1698
1699impl<FEN: FoundryEvmNetwork> Database for Backend<FEN> {
1700 type Error = DatabaseError;
1701 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
1702 if let Some(db) = self.active_fork_db_mut() {
1703 Ok(db.basic(address)?)
1704 } else {
1705 Ok(self.mem_db.basic(address)?)
1706 }
1707 }
1708
1709 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
1710 if let Some(db) = self.active_fork_db_mut() {
1711 Ok(db.code_by_hash(code_hash)?)
1712 } else {
1713 Ok(self.mem_db.code_by_hash(code_hash)?)
1714 }
1715 }
1716
1717 fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
1718 if let Some(db) = self.active_fork_db_mut() {
1719 Ok(Database::storage(db, address, index)?)
1720 } else {
1721 Ok(Database::storage(&mut self.mem_db, address, index)?)
1722 }
1723 }
1724
1725 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
1726 if let Some(db) = self.active_fork_db_mut() {
1727 Ok(db.block_hash(number)?)
1728 } else {
1729 Ok(self.mem_db.block_hash(number)?)
1730 }
1731 }
1732}
1733
1734#[derive(Clone, Debug)]
1736pub enum BackendDatabaseSnapshot<N: Network, B: ForkBlockEnv = BlockEnv> {
1737 InMemory(FoundryEvmInMemoryDB),
1739 Forked(LocalForkId, ForkId, ForkLookupIndex, Box<Fork<N, B>>),
1741}
1742
1743#[derive(Clone, Debug)]
1745pub struct Fork<N: Network, B: ForkBlockEnv = BlockEnv> {
1746 db: ForkDB<N, B>,
1747 journaled_state: JournaledState,
1748}
1749
1750impl<N: Network, B: ForkBlockEnv> Fork<N, B> {
1751 pub const fn backend(&self) -> &SharedBackend<N, B> {
1753 &self.db.db
1754 }
1755
1756 pub fn is_contract(&self, acc: Address) -> bool {
1758 if let Ok(Some(acc)) = self.db.basic_ref(acc)
1759 && acc.code_hash != KECCAK_EMPTY
1760 {
1761 return true;
1762 }
1763 is_contract_in_state(&self.journaled_state.state, acc)
1764 }
1765
1766 fn refresh_journaled_states(
1769 &mut self,
1770 journaled_state: &mut JournaledState,
1771 persistent_accounts: &HashSet<Address>,
1772 ) -> Result<(), BackendError> {
1773 update_state(&mut journaled_state.state, &mut self.db, Some(persistent_accounts))?;
1774 update_state(&mut self.journaled_state.state, &mut self.db, Some(persistent_accounts))?;
1775 Ok(())
1776 }
1777}
1778
1779pub struct BackendInner<FEN: FoundryEvmNetwork> {
1781 pub launched_with_fork: Option<(ForkId, LocalForkId, ForkLookupIndex)>,
1786 pub issued_local_fork_ids: HashMap<LocalForkId, ForkId>,
1798 pub created_forks: HashMap<ForkId, ForkLookupIndex>,
1801 pub forks: Vec<Option<Fork<AnyNetwork, BlockEnvFor<FEN>>>>,
1804 #[allow(clippy::type_complexity)]
1806 pub state_snapshots: StateSnapshots<
1807 BackendStateSnapshot<
1808 BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
1809 SpecFor<FEN>,
1810 BlockEnvFor<FEN>,
1811 >,
1812 >,
1813 pub has_state_snapshot_failure: bool,
1823 pub caller: Option<Address>,
1825 pub next_fork_id: LocalForkId,
1827 pub persistent_accounts: HashSet<Address>,
1831 pub spec_id: SpecFor<FEN>,
1833 pub cheatcode_access_accounts: HashSet<Address>,
1835}
1836
1837impl<FEN: FoundryEvmNetwork> Clone for BackendInner<FEN> {
1838 fn clone(&self) -> Self {
1839 Self {
1840 launched_with_fork: self.launched_with_fork.clone(),
1841 issued_local_fork_ids: self.issued_local_fork_ids.clone(),
1842 created_forks: self.created_forks.clone(),
1843 forks: self.forks.clone(),
1844 state_snapshots: self.state_snapshots.clone(),
1845 has_state_snapshot_failure: self.has_state_snapshot_failure,
1846 caller: self.caller,
1847 next_fork_id: self.next_fork_id,
1848 persistent_accounts: self.persistent_accounts.clone(),
1849 spec_id: self.spec_id,
1850 cheatcode_access_accounts: self.cheatcode_access_accounts.clone(),
1851 }
1852 }
1853}
1854
1855impl<FEN: FoundryEvmNetwork> Debug for BackendInner<FEN> {
1856 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1857 f.debug_struct("BackendInner")
1858 .field("launched_with_fork", &self.launched_with_fork)
1859 .field("issued_local_fork_ids", &self.issued_local_fork_ids)
1860 .field("created_forks", &self.created_forks)
1861 .field("forks", &self.forks)
1862 .field("state_snapshots", &self.state_snapshots)
1863 .field("has_state_snapshot_failure", &self.has_state_snapshot_failure)
1864 .field("caller", &self.caller)
1865 .field("next_fork_id", &self.next_fork_id)
1866 .field("persistent_accounts", &self.persistent_accounts)
1867 .field("spec_id", &self.spec_id)
1868 .field("cheatcode_access_accounts", &self.cheatcode_access_accounts)
1869 .finish()
1870 }
1871}
1872
1873impl<FEN: FoundryEvmNetwork> BackendInner<FEN> {
1874 pub fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
1875 self.issued_local_fork_ids
1876 .get(&id)
1877 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
1878 }
1879
1880 pub fn ensure_fork_index(&self, id: &ForkId) -> eyre::Result<ForkLookupIndex> {
1881 self.created_forks
1882 .get(id)
1883 .copied()
1884 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
1885 }
1886
1887 pub fn ensure_fork_index_by_local_id(&self, id: LocalForkId) -> eyre::Result<ForkLookupIndex> {
1888 self.ensure_fork_index(self.ensure_fork_id(id)?)
1889 }
1890
1891 #[track_caller]
1893 fn get_fork(&self, idx: ForkLookupIndex) -> &Fork<AnyNetwork, BlockEnvFor<FEN>> {
1894 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1895 self.forks[idx].as_ref().unwrap()
1896 }
1897
1898 #[track_caller]
1900 fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork<AnyNetwork, BlockEnvFor<FEN>> {
1901 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1902 self.forks[idx].as_mut().unwrap()
1903 }
1904
1905 #[track_caller]
1907 fn get_fork_by_id_mut(
1908 &mut self,
1909 id: LocalForkId,
1910 ) -> eyre::Result<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
1911 let idx = self.ensure_fork_index_by_local_id(id)?;
1912 Ok(self.get_fork_mut(idx))
1913 }
1914
1915 #[track_caller]
1917 fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
1918 let idx = self.ensure_fork_index_by_local_id(id)?;
1919 Ok(self.get_fork(idx))
1920 }
1921
1922 fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork<AnyNetwork, BlockEnvFor<FEN>> {
1924 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1925 self.forks[idx].take().unwrap()
1926 }
1927
1928 fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork<AnyNetwork, BlockEnvFor<FEN>>) {
1929 self.forks[idx] = Some(fork)
1930 }
1931
1932 pub fn forks_iter(
1934 &self,
1935 ) -> impl Iterator<Item = (LocalForkId, &Fork<AnyNetwork, BlockEnvFor<FEN>>)> + '_ {
1936 self.issued_local_fork_ids
1937 .iter()
1938 .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id])))
1939 }
1940
1941 pub fn forks_iter_mut(
1943 &mut self,
1944 ) -> impl Iterator<Item = &mut Fork<AnyNetwork, BlockEnvFor<FEN>>> + '_ {
1945 self.forks.iter_mut().filter_map(|f| f.as_mut())
1946 }
1947
1948 pub fn revert_state_snapshot(
1950 &mut self,
1951 id: LocalForkId,
1952 fork_id: ForkId,
1953 idx: ForkLookupIndex,
1954 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
1955 ) {
1956 self.created_forks.insert(fork_id.clone(), idx);
1957 self.issued_local_fork_ids.insert(id, fork_id);
1958 self.set_fork(idx, fork)
1959 }
1960
1961 pub fn update_fork_mapping(
1963 &mut self,
1964 id: LocalForkId,
1965 fork_id: ForkId,
1966 db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
1967 journaled_state: JournaledState,
1968 ) -> ForkLookupIndex {
1969 let idx = self.forks.len();
1970 self.issued_local_fork_ids.insert(id, fork_id.clone());
1971 self.created_forks.insert(fork_id, idx);
1972
1973 let fork = Fork { db, journaled_state };
1974 self.forks.push(Some(fork));
1975 idx
1976 }
1977
1978 pub fn roll_fork(
1979 &mut self,
1980 id: LocalForkId,
1981 new_fork_id: ForkId,
1982 backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
1983 ) -> eyre::Result<ForkLookupIndex> {
1984 let fork_id = self.ensure_fork_id(id)?;
1985 let idx = self.ensure_fork_index(fork_id)?;
1986
1987 if let Some(active) = self.forks[idx].as_mut() {
1988 let mut new_db = ForkDB::new(backend);
1990 for addr in self.persistent_accounts.iter().copied() {
1991 merge_db_account_data(addr, &active.db, &mut new_db);
1992 }
1993 active.db = new_db;
1994 }
1995 self.issued_local_fork_ids.insert(id, new_fork_id.clone());
1997 self.created_forks.insert(new_fork_id, idx);
1998 Ok(idx)
1999 }
2000
2001 pub fn insert_new_fork(
2005 &mut self,
2006 fork_id: ForkId,
2007 db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
2008 journaled_state: JournaledState,
2009 ) -> (LocalForkId, ForkLookupIndex) {
2010 let idx = self.forks.len();
2011 self.created_forks.insert(fork_id.clone(), idx);
2012 let id = self.next_id();
2013 self.issued_local_fork_ids.insert(id, fork_id);
2014 let fork = Fork { db, journaled_state };
2015 self.forks.push(Some(fork));
2016 (id, idx)
2017 }
2018
2019 fn next_id(&mut self) -> U256 {
2020 let id = self.next_fork_id;
2021 self.next_fork_id += U256::from(1);
2022 id
2023 }
2024
2025 pub fn len(&self) -> usize {
2027 self.issued_local_fork_ids.len()
2028 }
2029
2030 pub fn is_empty(&self) -> bool {
2032 self.issued_local_fork_ids.is_empty()
2033 }
2034
2035 pub fn precompile_addresses(&self) -> AddressSet {
2036 let evm = FEN::EvmFactory::default().create_evm(
2037 EmptyDB::default(),
2038 EvmEnv::new(CfgEnv::new_with_spec(self.spec_id), Default::default()),
2039 );
2040 evm.precompiles().addresses().copied().collect()
2041 }
2042
2043 pub fn new_journaled_state(&self) -> JournaledState {
2045 let mut journal = {
2046 let mut journal_inner = JournalInner::new();
2047 journal_inner.set_spec_id(self.spec_id.into());
2048 journal_inner
2049 };
2050 let precompile_addresses = self.precompile_addresses();
2051 journal.warm_addresses.set_precompile_addresses(&precompile_addresses);
2052 journal
2053 }
2054}
2055
2056impl<FEN: FoundryEvmNetwork> Default for BackendInner<FEN> {
2057 fn default() -> Self {
2058 Self {
2059 launched_with_fork: None,
2060 issued_local_fork_ids: Default::default(),
2061 created_forks: Default::default(),
2062 forks: vec![],
2063 state_snapshots: Default::default(),
2064 has_state_snapshot_failure: false,
2065 caller: None,
2066 next_fork_id: Default::default(),
2067 persistent_accounts: Default::default(),
2068 spec_id: SpecFor::<FEN>::default(),
2069 cheatcode_access_accounts: HashSet::from([
2072 CHEATCODE_ADDRESS,
2073 TEST_CONTRACT_ADDRESS,
2074 CALLER,
2075 ]),
2076 }
2077 }
2078}
2079
2080pub(crate) fn merge_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
2083 accounts: impl IntoIterator<Item = Address>,
2084 active: &CacheDB<ExtDB>,
2085 active_journaled_state: &mut JournaledState,
2086 target_fork: &mut Fork<N, B>,
2087) {
2088 for addr in accounts {
2089 merge_db_account_data(addr, active, &mut target_fork.db);
2090 merge_journaled_state_data(addr, active_journaled_state, &mut target_fork.journaled_state);
2091 }
2092
2093 *active_journaled_state = target_fork.journaled_state.clone();
2094}
2095
2096fn merge_journaled_state_data(
2098 addr: Address,
2099 active_journaled_state: &JournaledState,
2100 fork_journaled_state: &mut JournaledState,
2101) {
2102 if let Some(mut acc) = active_journaled_state.state.get(&addr).cloned() {
2103 trace!(?addr, "updating journaled_state account data");
2104 if let Some(fork_account) = fork_journaled_state.state.get_mut(&addr) {
2105 fork_account.storage.extend(std::mem::take(&mut acc.storage));
2107 std::mem::swap(&mut fork_account.storage, &mut acc.storage);
2109 }
2110 fork_journaled_state.state.insert(addr, acc);
2111 }
2112}
2113
2114fn merge_db_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
2116 addr: Address,
2117 active: &CacheDB<ExtDB>,
2118 fork_db: &mut ForkDB<N, B>,
2119) {
2120 trace!(?addr, "merging database data");
2121
2122 let Some(acc) = active.cache.accounts.get(&addr) else { return };
2123
2124 if let Some(code) = active.cache.contracts.get(&acc.info.code_hash) {
2126 trace!("merging contract cache");
2127 fork_db.cache.contracts.insert(acc.info.code_hash, code.clone());
2128 }
2129
2130 use std::collections::hash_map::Entry;
2132 match fork_db.cache.accounts.entry(addr) {
2133 Entry::Vacant(vacant) => {
2134 trace!("target account not present - inserting from active");
2135 vacant.insert(acc.clone());
2138 }
2139 Entry::Occupied(mut occupied) => {
2140 trace!("target account present - merging storage slots");
2141 let fork_account = occupied.get_mut();
2144 fork_account.storage.extend(&acc.storage);
2145 }
2146 }
2147}
2148
2149fn is_contract_in_state(evm_state: &EvmState, acc: Address) -> bool {
2151 evm_state.get(&acc).map(|acc| acc.info.code_hash != KECCAK_EMPTY).unwrap_or_default()
2152}
2153
2154fn update_env_block<SPEC, BLOCK: FoundryBlock>(
2156 evm_env: &mut EvmEnv<SPEC, BLOCK>,
2157 header: &impl BlockHeader,
2158) {
2159 let block_env = &mut evm_env.block_env;
2160 block_env.set_timestamp(U256::from(header.timestamp()));
2161 block_env.set_beneficiary(header.beneficiary());
2162 block_env.set_difficulty(header.difficulty());
2163 block_env.set_prevrandao(header.mix_hash());
2164 block_env.set_basefee(header.base_fee_per_gas().unwrap_or_default());
2165 block_env.set_gas_limit(header.gas_limit());
2166 block_env.set_number(U256::from(header.number()));
2167
2168 if let Some(excess_blob_gas) = header.excess_blob_gas() {
2169 evm_env.block_env.set_blob_excess_gas_and_price(
2170 excess_blob_gas,
2171 get_blob_base_fee_update_fraction(evm_env.cfg_env.chain_id, header.timestamp()),
2172 );
2173 }
2174}
2175
2176fn commit_transaction<FEN: FoundryEvmNetwork>(
2179 evm_env: EvmEnvFor<FEN>,
2180 tx_env: TxEnvFor<FEN>,
2181 journaled_state: &mut JournaledState,
2182 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
2183 fork_id: &ForkId,
2184 persistent_accounts: &HashSet<Address>,
2185 inspector: &mut dyn for<'db> FoundryInspectorExt<
2186 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2187 >,
2188) -> eyre::Result<()> {
2189 let now = Instant::now();
2190 let res = {
2191 let fork = fork.clone();
2192 let journaled_state = journaled_state.clone();
2193 let depth = journaled_state.depth;
2194 let mut db: Backend<FEN> = Backend::new_with_fork(fork_id, fork, journaled_state)?;
2195
2196 let mut evm =
2197 FEN::EvmFactory::default().create_foundry_nested_evm(&mut db, evm_env, inspector);
2198 evm.journal_inner_mut().depth = depth + 1;
2199 evm.transact_raw(tx_env).wrap_err("backend: failed committing transaction")?
2200 };
2201 trace!(elapsed = ?now.elapsed(), "transacted transaction");
2202
2203 apply_state_changeset(res.state, journaled_state, fork, persistent_accounts)?;
2204 Ok(())
2205}
2206
2207pub fn update_state<DB: Database>(
2210 state: &mut EvmState,
2211 db: &mut DB,
2212 persistent_accounts: Option<&HashSet<Address>>,
2213) -> Result<(), DB::Error> {
2214 for (addr, acc) in state.iter_mut() {
2215 if persistent_accounts.is_none_or(|accounts| !accounts.contains(addr)) {
2216 acc.info = db.basic(*addr)?.unwrap_or_default();
2217 for (key, val) in &mut acc.storage {
2218 val.present_value = db.storage(*addr, *key)?;
2219 }
2220 }
2221 }
2222
2223 Ok(())
2224}
2225
2226fn apply_state_changeset<N: Network, B: ForkBlockEnv>(
2229 state: EvmState,
2230 journaled_state: &mut JournaledState,
2231 fork: &mut Fork<N, B>,
2232 persistent_accounts: &HashSet<Address>,
2233) -> Result<(), BackendError> {
2234 fork.db.commit(state);
2236 fork.refresh_journaled_states(journaled_state, persistent_accounts)
2237}
2238
2239fn fork_block_number(fork: &ForkId) -> Option<u64> {
2240 let (_, block) = fork.as_str().rsplit_once('@')?;
2241 let block = block.split_once('-').map_or(block, |(block, _)| block);
2242 let block = block.strip_prefix("0x")?;
2243 u64::from_str_radix(block, 16).ok()
2244}
2245
2246#[cfg(test)]
2247mod tests {
2248 use crate::{backend::Backend, evm::EthEvmNetwork, fork::ForkId, opts::EvmOpts};
2249 use alloy_primitives::{U256, address};
2250 use alloy_provider::Provider;
2251 use foundry_common::provider::get_http_provider;
2252 use foundry_config::{Config, NamedChain};
2253 use foundry_fork_db::cache::{BlockchainDb, BlockchainDbMeta};
2254 use revm::{
2255 context::{BlockEnv, TxEnv},
2256 database::DatabaseRef,
2257 primitives::hardfork::SpecId,
2258 };
2259
2260 #[tokio::test(flavor = "multi_thread")]
2261 async fn can_read_write_cache() {
2262 let endpoint = &*foundry_test_utils::rpc::next_http_rpc_endpoint();
2263 let provider = get_http_provider(endpoint);
2264
2265 let block_num = provider.get_block_number().await.unwrap();
2266
2267 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
2268 evm_opts.fork_url = Some(endpoint.to_string());
2269 evm_opts.fork_block_number = Some(block_num);
2270
2271 let (evm_env, _, fork_block) = evm_opts.env::<SpecId, BlockEnv, TxEnv>().await.unwrap();
2272
2273 let fork =
2274 evm_opts.get_fork(&Config::default(), evm_env.cfg_env.chain_id, fork_block).unwrap();
2275
2276 let backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
2277
2278 let address = address!("0x63091244180ae240c87d1f528f5f269134cb07b3");
2280
2281 let num_slots = 5;
2282 let _account = backend.basic_ref(address);
2283 for idx in 0..num_slots {
2284 let _ = backend.storage_ref(address, U256::from(idx));
2285 }
2286 drop(backend);
2287
2288 let meta = BlockchainDbMeta {
2289 chain: None,
2290 block_env: evm_env.block_env,
2291 hosts: Default::default(),
2292 };
2293
2294 let db = BlockchainDb::new(
2295 meta,
2296 Some(Config::foundry_block_cache_dir(NamedChain::Mainnet, block_num).unwrap()),
2297 );
2298 assert!(db.accounts().read().contains_key(&address));
2299 assert!(db.storage().read().contains_key(&address));
2300 assert_eq!(db.storage().read().get(&address).unwrap().len(), num_slots as usize);
2301 }
2302
2303 #[test]
2304 fn parses_fork_block_number_from_fork_id() {
2305 let fork = ForkId::new("https://example.com/@rpc", Some(75_219_831));
2306 assert_eq!(super::fork_block_number(&fork), Some(75_219_831));
2307 assert_eq!(
2308 super::fork_block_number(&format!("{}-1", fork.as_str()).into()),
2309 Some(75_219_831)
2310 );
2311 assert_eq!(super::fork_block_number(&ForkId::new("https://example.com", None)), None);
2312 }
2313}