1use crate::{
4 FoundryBlock, FoundryChain, FoundryInspectorExt, FoundryTransaction, FromAnyRpcTransaction,
5 constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS},
6 evm::{
7 BlockContext, BlockEnvFor, ChainFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor,
8 FoundryEvmFactory, FoundryEvmNetwork, HaltReasonFor, SpecFor, TxEnvFor,
9 },
10 fork::{CreateFork, ForkId, ForkResult, MultiFork},
11 state_snapshot::StateSnapshots,
12 utils::{
13 apply_chain_and_block_specific_env_changes_for_chain,
14 apply_chain_specific_tx_replay_env_changes_for_chain, get_blob_base_fee_update_fraction,
15 },
16};
17use alloy_consensus::{BlockHeader, Typed2718};
18use alloy_eips::BlockNumHash;
19use alloy_evm::{Evm, EvmEnv, EvmFactory};
20use alloy_genesis::GenesisAccount;
21use alloy_network::{
22 AnyNetwork, AnyRpcBlock, AnyRpcTransaction, BlockResponse, Network, TransactionResponse,
23};
24use alloy_primitives::{Address, B256, ChainId, TxKind, U256, keccak256, map::AddressSet, uint};
25use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions};
26use eyre::Context;
27use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender};
28use foundry_evm_networks::NetworkConfigs;
29pub use foundry_fork_db::{
30 BlockchainDb, ForkBlock, ForkBlockEnv, SharedBackend, cache::BlockchainDbMeta,
31};
32use revm::{
33 Database, DatabaseCommit, JournalEntry,
34 bytecode::Bytecode,
35 context::{Block, BlockEnv, CfgEnv, ContextTr, JournalInner, Transaction},
36 context_interface::{journaled_state::account::JournaledAccountTr, result::ResultAndState},
37 database::{AccountState, CacheDB, DatabaseRef, EmptyDB},
38 primitives::{AddressMap, HashMap as Map, KECCAK_EMPTY, Log, hardfork::SpecId},
39 state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
40};
41use std::{
42 collections::{BTreeMap, HashMap, HashSet},
43 fmt::Debug,
44 time::Instant,
45};
46
47mod diagnostic;
48pub use diagnostic::RevertDiagnostic;
49
50mod error;
51pub use error::{BackendError, BackendResult, DatabaseError, DatabaseResult};
52
53mod cow;
54pub use cow::CowBackend;
55
56mod in_memory_db;
57pub use in_memory_db::{EmptyDBWrapper, FoundryEvmInMemoryDB, MemDb};
58
59mod snapshot;
60pub use snapshot::{BackendStateSnapshot, RevertStateSnapshotAction, StateSnapshot};
61
62type ForkDB<N, B> = CacheDB<SharedBackend<N, B>>;
64
65pub type LocalForkId = U256;
70
71#[cfg(feature = "monad")]
73pub enum ContextUpdate<C> {
74 Unchanged,
76 Replace(C),
78 Rebase,
80}
81
82#[cfg(feature = "monad")]
87pub type ContextUpdateFor<F> = ContextUpdate<<F as FoundryEvmFactory>::Chain>;
88#[cfg(not(feature = "monad"))]
89pub type ContextUpdateFor<F> = std::marker::PhantomData<F>;
90
91type ForkLookupIndex = usize;
94
95struct TransactionInputs<FEN: FoundryEvmNetwork> {
97 evm_env: EvmEnvFor<FEN>,
98 tx_env: TxEnvFor<FEN>,
99 chain_context: ChainFor<FEN>,
100 rpc_block_number: u64,
101}
102
103struct ReplayInputs<FEN: FoundryEvmNetwork> {
105 evm_env: EvmEnvFor<FEN>,
106 networks: NetworkConfigs,
107}
108
109struct TransactionForkTarget {
111 fork_block: BlockNumHash,
112 transaction: AnyRpcTransaction,
113 block: AnyRpcBlock,
114 mined: bool,
115 position: Option<TransactionPosition>,
116}
117
118#[derive(Clone, Copy)]
120struct TransactionPosition {
121 index: usize,
122 count: usize,
123}
124
125#[cfg(feature = "monad")]
127struct StagedForkRoll<FEN: FoundryEvmNetwork> {
128 local_id: LocalForkId,
129 fork_id: ForkId,
130 fork_index: ForkLookupIndex,
131 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
132}
133
134#[derive(Clone, Copy, Debug, PartialEq, Eq)]
136enum ForkPosition {
137 AfterBlock { block: BlockNumHash },
139 BeforeTransaction { block: BlockNumHash, transaction_index: usize },
141}
142
143impl ForkPosition {
144 fn after_transaction(
147 self,
148 block: BlockNumHash,
149 parent_hash: B256,
150 transaction_index: usize,
151 transaction_count: usize,
152 ) -> Option<Self> {
153 let is_next = match self {
154 Self::AfterBlock { block: previous } => {
155 transaction_index == 0
156 && previous.number.checked_add(1) == Some(block.number)
157 && previous.hash == parent_hash
158 }
159 Self::BeforeTransaction { block: current, transaction_index: current_index } => {
160 current == block && current_index == transaction_index
161 }
162 };
163 if !is_next {
164 return None;
165 }
166 let next_index = transaction_index.checked_add(1)?;
167 if next_index > transaction_count {
168 return None;
169 }
170
171 Some(if next_index == transaction_count {
172 Self::AfterBlock { block }
173 } else {
174 Self::BeforeTransaction { block, transaction_index: next_index }
175 })
176 }
177}
178
179const DEFAULT_PERSISTENT_ACCOUNTS: [Address; 3] =
181 [CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, CALLER];
182
183pub const GLOBAL_FAIL_SLOT: U256 =
188 uint!(0x6661696c65640000000000000000000000000000000000000000000000000000_U256);
189
190pub type JournaledState = JournalInner<JournalEntry>;
191
192#[derive(Clone, Copy, Debug)]
194pub enum ForkAccountField {
195 Balance,
196 Nonce,
197 Code,
198}
199
200impl ForkAccountField {
201 fn update(self, target: &mut AccountInfo, refreshed: &AccountInfo) {
202 match self {
203 Self::Balance => target.balance = refreshed.balance,
204 Self::Nonce => target.nonce = refreshed.nonce,
205 Self::Code => {
206 target.code_hash = refreshed.code_hash;
207 target.code = refreshed.code.clone();
208 }
209 }
210 }
211}
212
213#[auto_impl::auto_impl(&mut)]
215pub trait DatabaseExt<F: FoundryEvmFactory>:
216 Database<Error = DatabaseError> + DatabaseCommit + Debug
217{
218 fn snapshot_state(
224 &mut self,
225 journaled_state: &JournaledState,
226 evm_env: &EvmEnv<F::Spec, F::BlockEnv>,
227 ) -> U256;
228
229 fn revert_state(
242 &mut self,
243 id: U256,
244 journaled_state: &JournaledState,
245 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
246 caller: Address,
247 action: RevertStateSnapshotAction,
248 ) -> Option<JournaledState>;
249
250 fn delete_state_snapshot(&mut self, id: U256) -> bool;
255
256 fn delete_state_snapshots(&mut self);
258
259 fn create_select_fork(
263 &mut self,
264 fork: CreateFork,
265 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
266 tx_env: &mut F::Tx,
267 journaled_state: &mut JournaledState,
268 ) -> eyre::Result<(LocalForkId, ContextUpdateFor<F>)> {
269 let id = self.create_fork(fork)?;
270 let context = self.select_fork(id, evm_env, tx_env, journaled_state)?;
271 Ok((id, context))
272 }
273
274 fn create_select_fork_at_transaction(
278 &mut self,
279 fork: CreateFork,
280 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
281 tx_env: &mut F::Tx,
282 journaled_state: &mut JournaledState,
283 transaction: B256,
284 ) -> eyre::Result<(LocalForkId, ContextUpdateFor<F>)> {
285 let id = self.create_fork_at_transaction(fork, transaction)?;
286 let context = self.select_fork(id, evm_env, tx_env, journaled_state)?;
287 Ok((id, context))
288 }
289
290 fn create_fork(&mut self, fork: CreateFork) -> eyre::Result<LocalForkId>;
292
293 fn create_fork_at_transaction(
295 &mut self,
296 fork: CreateFork,
297 transaction: B256,
298 ) -> eyre::Result<LocalForkId>;
299
300 fn select_fork(
310 &mut self,
311 id: LocalForkId,
312 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
313 tx_env: &mut F::Tx,
314 journaled_state: &mut JournaledState,
315 ) -> eyre::Result<ContextUpdateFor<F>>;
316
317 fn roll_fork(
325 &mut self,
326 id: Option<LocalForkId>,
327 block_number: u64,
328 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
329 tx_env: &F::Tx,
330 journaled_state: &mut JournaledState,
331 ) -> eyre::Result<ContextUpdateFor<F>>;
332
333 fn roll_fork_to_transaction(
342 &mut self,
343 id: Option<LocalForkId>,
344 transaction: B256,
345 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
346 tx_env: &F::Tx,
347 journaled_state: &mut JournaledState,
348 ) -> eyre::Result<ContextUpdateFor<F>>;
349
350 fn transact(
352 &mut self,
353 id: Option<LocalForkId>,
354 transaction: B256,
355 evm_env: EvmEnv<F::Spec, F::BlockEnv>,
356 outer_tx_env: &F::Tx,
357 journaled_state: &mut JournaledState,
358 inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
359 ) -> eyre::Result<ContextUpdateFor<F>>;
360
361 fn transact_from_tx(
363 &mut self,
364 tx_env: F::Tx,
365 evm_env: EvmEnv<F::Spec, F::BlockEnv>,
366 journaled_state: &mut JournaledState,
367 inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
368 ) -> eyre::Result<()>;
369
370 fn chain_context_for_synthetic_transaction(&self, tx: &F::Tx) -> eyre::Result<F::Chain> {
372 Ok(F::Chain::for_transaction(tx))
373 }
374
375 fn active_fork_id(&self) -> Option<LocalForkId>;
377
378 fn active_fork_url(&self) -> Option<String>;
380
381 fn active_fork_block_number(&self) -> Option<u64> {
383 None
384 }
385
386 fn is_forked_mode(&self) -> bool {
388 self.active_fork_id().is_some()
389 }
390
391 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId>;
402
403 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId>;
405
406 fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic>;
433
434 fn load_allocs(
438 &mut self,
439 allocs: &BTreeMap<Address, GenesisAccount>,
440 journaled_state: &mut JournaledState,
441 ) -> Result<(), BackendError>;
442
443 fn clone_account(
448 &mut self,
449 source: &GenesisAccount,
450 target: &Address,
451 journaled_state: &mut JournaledState,
452 ) -> Result<(), BackendError>;
453
454 fn is_persistent(&self, acc: &Address) -> bool;
456
457 fn refresh_fork_account(
460 &mut self,
461 address: Address,
462 field: ForkAccountField,
463 journaled_state: &mut JournaledState,
464 ) -> Result<(), BackendError>;
465
466 fn refresh_fork_storage(
468 &mut self,
469 address: Address,
470 slot: U256,
471 journaled_state: &mut JournaledState,
472 ) -> Result<(), BackendError>;
473
474 fn remove_persistent_account(&mut self, account: &Address) -> bool;
476
477 fn add_persistent_account(&mut self, account: Address) -> bool;
479
480 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
482 fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
483 where
484 Self: Sized,
485 {
486 for acc in accounts {
487 self.remove_persistent_account(&acc);
488 }
489 }
490
491 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
493 fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
494 where
495 Self: Sized,
496 {
497 for acc in accounts {
498 self.add_persistent_account(acc);
499 }
500 }
501
502 fn allow_cheatcode_access(&mut self, account: Address) -> bool;
506
507 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool;
511
512 fn has_cheatcode_access(&self, account: &Address) -> bool;
514
515 fn ensure_cheatcode_access(&self, account: &Address) -> Result<(), BackendError> {
519 if !self.has_cheatcode_access(account) {
520 return Err(BackendError::NoCheats(*account));
521 }
522 Ok(())
523 }
524
525 fn ensure_cheatcode_access_forking_mode(&self, account: &Address) -> Result<(), BackendError> {
528 if self.is_forked_mode() {
529 return self.ensure_cheatcode_access(account);
530 }
531 Ok(())
532 }
533
534 fn set_blockhash(&mut self, block_number: U256, block_hash: B256);
549}
550
551#[must_use]
604pub struct Backend<FEN: FoundryEvmNetwork = EthEvmNetwork> {
605 networks: NetworkConfigs,
607 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
609 mem_db: FoundryEvmInMemoryDB,
611 fork_init_journaled_state: JournaledState,
628 active_fork_ids: Option<(LocalForkId, ForkLookupIndex)>,
632 fork_block_number_override: Option<u64>,
634 inner: BackendInner<FEN>,
636}
637
638impl<FEN: FoundryEvmNetwork> Clone for Backend<FEN> {
639 fn clone(&self) -> Self {
640 Self {
641 networks: self.networks,
642 forks: self.forks.clone(),
643 mem_db: self.mem_db.clone(),
644 fork_init_journaled_state: self.fork_init_journaled_state.clone(),
645 active_fork_ids: self.active_fork_ids,
646 fork_block_number_override: self.fork_block_number_override,
647 inner: self.inner.clone(),
648 }
649 }
650}
651
652impl<FEN: FoundryEvmNetwork> Debug for Backend<FEN> {
653 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
654 f.debug_struct("Backend")
655 .field("networks", &self.networks)
656 .field("forks", &self.forks)
657 .field("mem_db", &self.mem_db)
658 .field("fork_init_journaled_state", &self.fork_init_journaled_state)
659 .field("active_fork_ids", &self.active_fork_ids)
660 .field("inner", &self.inner)
661 .finish()
662 }
663}
664
665impl<FEN: FoundryEvmNetwork> Backend<FEN> {
666 pub fn spawn(fork: Option<CreateFork>) -> eyre::Result<Self> {
671 Self::new(MultiFork::<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>::spawn(), fork)
672 }
673
674 pub fn new(
681 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
682 fork: Option<CreateFork>,
683 ) -> eyre::Result<Self> {
684 trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend");
685 let persistent_accounts = HashSet::from(DEFAULT_PERSISTENT_ACCOUNTS);
687 let inner = BackendInner { persistent_accounts, ..Default::default() };
688
689 let mut backend = Self {
690 networks: NetworkConfigs::default(),
691 forks,
692 mem_db: CacheDB::new(Default::default()),
693 fork_init_journaled_state: inner.new_journaled_state(),
694 active_fork_ids: None,
695 fork_block_number_override: None,
696 inner,
697 };
698
699 if let Some(fork) = fork {
700 let ForkResult { id: fork_id, backend: fork, resolved, .. } =
701 backend.forks.create_fork(fork)?;
702 let context = resolved.context();
703 let block = resolved.block();
704 let fork_db = ForkDB::new(fork);
705 let fork_ids = backend.inner.insert_new_fork(
706 fork_id.clone(),
707 block,
708 context.source_chain_id,
709 fork_db,
710 backend.inner.new_journaled_state(),
711 );
712 backend.inner.launched_with_fork = Some((fork_id, fork_ids.0, fork_ids.1));
713 backend.active_fork_ids = Some(fork_ids);
714 }
715
716 trace!(target: "backend", forking_mode=? backend.active_fork_ids.is_some(), "created executor backend");
717
718 Ok(backend)
719 }
720
721 pub(crate) fn new_with_fork(
724 id: &ForkId,
725 mut fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
726 journaled_state: JournaledState,
727 networks: NetworkConfigs,
728 ) -> eyre::Result<Self> {
729 let mut backend = Self::spawn(None)?;
730 backend.networks = networks;
731 fork.journaled_state = journaled_state;
732 let fork_ids = backend.inner.insert_fork(id.clone(), fork);
733 backend.inner.launched_with_fork = Some((id.clone(), fork_ids.0, fork_ids.1));
734 backend.active_fork_ids = Some(fork_ids);
735 Ok(backend)
736 }
737
738 pub fn clone_empty(&self) -> Self {
740 Self {
741 networks: self.networks,
742 forks: self.forks.clone(),
743 mem_db: CacheDB::new(Default::default()),
744 fork_init_journaled_state: self.inner.new_journaled_state(),
745 active_fork_ids: None,
746 fork_block_number_override: None,
747 inner: Default::default(),
748 }
749 }
750
751 pub const fn networks(&self) -> NetworkConfigs {
753 self.networks
754 }
755
756 pub const fn set_networks(&mut self, networks: NetworkConfigs) {
758 self.networks = networks;
759 }
760
761 pub fn insert_account_info(&mut self, address: Address, account: AccountInfo) {
762 if let Some(db) = self.active_fork_db_mut() {
763 db.insert_account_info(address, account)
764 } else {
765 self.mem_db.insert_account_info(address, account)
766 }
767 }
768
769 pub fn insert_account_storage(
771 &mut self,
772 address: Address,
773 slot: U256,
774 value: U256,
775 ) -> Result<(), DatabaseError> {
776 if let Some(db) = self.active_fork_db_mut() {
777 db.insert_account_storage(address, slot, value)
778 } else {
779 self.mem_db.insert_account_storage(address, slot, value)
780 }
781 }
782
783 pub fn replace_account_storage(
788 &mut self,
789 address: Address,
790 storage: Map<U256, U256>,
791 ) -> Result<(), DatabaseError> {
792 if let Some(db) = self.active_fork_db_mut() {
793 db.replace_account_storage(address, storage.into_iter().collect())
794 } else {
795 self.mem_db.replace_account_storage(address, storage.into_iter().collect())
796 }
797 }
798
799 #[allow(clippy::type_complexity)]
801 pub const fn state_snapshots(
802 &self,
803 ) -> &StateSnapshots<
804 BackendStateSnapshot<
805 BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
806 SpecFor<FEN>,
807 BlockEnvFor<FEN>,
808 >,
809 > {
810 &self.inner.state_snapshots
811 }
812
813 pub fn set_test_contract(&mut self, acc: Address) -> &mut Self {
820 trace!(?acc, "setting test account");
821 self.inner.persistent_accounts.insert(acc);
822 self.inner.cheatcode_access_accounts.insert(acc);
823 self
824 }
825
826 pub fn set_caller(&mut self, acc: Address) -> &mut Self {
828 trace!(?acc, "setting caller account");
829 self.inner.caller = Some(acc);
830 self.inner.cheatcode_access_accounts.insert(acc);
831 self
832 }
833
834 pub fn set_spec_id(&mut self, spec_id: impl Into<SpecFor<FEN>>) -> &mut Self {
836 self.inner.spec_id = spec_id.into();
837 self
838 }
839
840 pub const fn caller_address(&self) -> Option<Address> {
842 self.inner.caller
843 }
844
845 pub const fn has_state_snapshot_failure(&self) -> bool {
851 self.inner.has_state_snapshot_failure
852 }
853
854 pub const fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) {
856 self.inner.has_state_snapshot_failure = has_state_snapshot_failure
857 }
858
859 pub(crate) fn update_fork_db(
861 &self,
862 active_journaled_state: &mut JournaledState,
863 target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
864 ) {
865 self.update_fork_db_contracts(
866 self.inner.persistent_accounts.iter().copied(),
867 active_journaled_state,
868 target_fork,
869 )
870 }
871
872 pub(crate) fn update_fork_db_contracts(
874 &self,
875 accounts: impl IntoIterator<Item = Address>,
876 active_journaled_state: &mut JournaledState,
877 target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
878 ) {
879 if let Some(db) = self.active_fork_db() {
880 merge_account_data(accounts, db, active_journaled_state, target_fork)
881 } else {
882 merge_account_data(accounts, &self.mem_db, active_journaled_state, target_fork)
883 }
884 }
885
886 pub const fn mem_db(&self) -> &FoundryEvmInMemoryDB {
888 &self.mem_db
889 }
890
891 pub fn is_active_fork(&self, id: LocalForkId) -> bool {
893 self.active_fork_ids.map(|(i, _)| i == id).unwrap_or_default()
894 }
895
896 pub fn is_in_forking_mode(&self) -> bool {
898 self.active_fork().is_some()
899 }
900
901 pub fn active_fork(&self) -> Option<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
903 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx))
904 }
905
906 pub fn active_fork_mut(&mut self) -> Option<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
908 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx))
909 }
910
911 pub fn active_fork_db(&self) -> Option<&ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
913 self.active_fork().map(|f| &f.db)
914 }
915
916 pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
918 self.active_fork_mut().map(|f| &mut f.db)
919 }
920
921 pub fn db(&self) -> &dyn Database<Error = DatabaseError> {
923 match self.active_fork_db() {
924 Some(fork_db) => fork_db,
925 None => &self.mem_db,
926 }
927 }
928
929 pub fn db_mut(&mut self) -> &mut dyn Database<Error = DatabaseError> {
931 match self.active_fork_ids.map(|(_, idx)| &mut self.inner.get_fork_mut(idx).db) {
932 Some(fork_db) => fork_db,
933 None => &mut self.mem_db,
934 }
935 }
936
937 pub(crate) fn create_db_snapshot(
939 &self,
940 ) -> BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>> {
941 if let Some((id, idx)) = self.active_fork_ids {
942 let fork = self.inner.get_fork(idx).clone();
943 let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed");
944 BackendDatabaseSnapshot::Forked(id, fork_id, idx, Box::new(fork))
945 } else {
946 BackendDatabaseSnapshot::InMemory(self.mem_db.clone())
947 }
948 }
949
950 pub fn merged_logs(&self, mut logs: Vec<Log>) -> Vec<Log> {
952 if let Some((_, active)) = self.active_fork_ids {
953 let mut all_logs = Vec::with_capacity(logs.len());
954
955 self.inner
956 .forks
957 .iter()
958 .enumerate()
959 .filter_map(|(idx, f)| f.as_ref().map(|f| (idx, f)))
960 .for_each(|(idx, f)| {
961 if idx == active {
962 all_logs.append(&mut logs);
963 } else {
964 all_logs.extend(f.journaled_state.logs.clone())
965 }
966 });
967 return all_logs;
968 }
969
970 logs
971 }
972
973 pub(crate) fn initialize(
977 &mut self,
978 spec_id: impl Into<SpecFor<FEN>>,
979 caller: Address,
980 tx_kind: TxKind,
981 ) {
982 self.set_caller(caller);
983 self.set_spec_id(spec_id);
984
985 let test_contract = match tx_kind {
986 TxKind::Call(to) => to,
987 TxKind::Create => {
988 let nonce =
989 self.basic_ref(caller).map(|b| b.unwrap_or_default().nonce).unwrap_or_default();
990 caller.create(nonce)
991 }
992 };
993 self.set_test_contract(test_contract);
994 }
995
996 #[instrument(name = "inspect", level = "debug", skip_all)]
1001 pub fn inspect<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
1002 &mut self,
1003 evm_env: &mut EvmEnvFor<FEN>,
1004 tx_env: &mut TxEnvFor<FEN>,
1005 inspector: I,
1006 ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
1007 let chain_context = self.chain_context_for_synthetic_transaction(tx_env)?;
1008 self.inspect_with_context(evm_env, tx_env, chain_context, inspector)
1009 }
1010
1011 #[instrument(name = "inspect", level = "debug", skip_all)]
1013 pub fn inspect_with_context<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
1014 &mut self,
1015 evm_env: &mut EvmEnvFor<FEN>,
1016 tx_env: &mut TxEnvFor<FEN>,
1017 chain_context: ChainFor<FEN>,
1018 inspector: I,
1019 ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
1020 self.initialize(evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind());
1021 let factory = FEN::EvmFactory::default();
1022 let mut evm = factory.create_foundry_evm_with_inspector(
1023 self,
1024 evm_env.to_owned(),
1025 chain_context,
1026 inspector,
1027 );
1028 let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?;
1029
1030 *tx_env = evm.tx().clone();
1031 *evm_env = evm.finish().1;
1032
1033 Ok(res)
1034 }
1035
1036 pub fn is_existing_precompile(&self, addr: &Address) -> bool {
1038 self.inner.precompile_addresses().contains(addr)
1039 }
1040
1041 #[inline]
1043 fn set_init_journaled_state(&mut self, journaled_state: JournaledState) {
1044 trace!("recording fork init journaled_state");
1045 self.fork_init_journaled_state = journaled_state;
1046 }
1047
1048 fn prepare_init_journal_state(&mut self) -> Result<(), BackendError> {
1058 let loaded_accounts = self
1059 .fork_init_journaled_state
1060 .state
1061 .iter()
1062 .filter(|(addr, _)| {
1063 !self.is_existing_precompile(addr)
1064 && !self.inner.persistent_accounts.contains(*addr)
1065 })
1066 .map(|(addr, _)| addr)
1067 .copied()
1068 .collect::<Vec<_>>();
1069
1070 for fork in self.inner.forks_iter_mut() {
1071 let mut journaled_state = self.fork_init_journaled_state.clone();
1072 for loaded_account in loaded_accounts.iter().copied() {
1073 trace!(?loaded_account, "replacing account on init");
1074 let init_account =
1075 journaled_state.state.get_mut(&loaded_account).expect("exists; qed");
1076
1077 if init_account.is_created() {
1081 trace!(?loaded_account, "skipping created account");
1082 continue;
1083 }
1084
1085 let fork_account = Database::basic(&mut fork.db, loaded_account)?
1088 .ok_or(BackendError::MissingAccount(loaded_account))?;
1089 init_account.info = fork_account;
1090 }
1091 fork.journaled_state = journaled_state;
1092 }
1093 Ok(())
1094 }
1095
1096 fn get_block_number_and_block_for_transaction(
1098 &self,
1099 id: LocalForkId,
1100 transaction: B256,
1101 ) -> eyre::Result<TransactionForkTarget> {
1102 let fork = self.inner.get_fork_by_id(id)?;
1103 let tx = fork.backend().get_transaction(transaction)?;
1104
1105 if let Some(tx_block) = tx.block_number() {
1107 let tx_block_hash = tx
1108 .block_hash()
1109 .ok_or_else(|| eyre::eyre!("mined transaction is missing its block hash"))?;
1110 let block = fork.backend().get_full_block(tx_block_hash)?;
1111 eyre::ensure!(
1112 block.header().number() == tx_block && block.header().hash == tx_block_hash,
1113 "transaction block changed: expected {} ({}), got {} ({})",
1114 tx_block,
1115 tx_block_hash,
1116 block.header().number(),
1117 block.header().hash
1118 );
1119 let position = if let BlockTransactions::Full(transactions) = block.transactions() {
1120 let index = transactions.iter().position(|tx| tx.tx_hash() == transaction);
1121 if self.networks.is_monad() && index.is_none() {
1122 eyre::bail!(
1123 "transaction {transaction:?} is missing from block {}",
1124 block.header().number()
1125 );
1126 }
1127 index.map(|index| TransactionPosition { index, count: transactions.len() })
1128 } else {
1129 if self.networks.is_monad() {
1130 eyre::bail!(
1131 "block {} does not contain full transactions",
1132 block.header().number()
1133 );
1134 }
1135 None
1136 };
1137
1138 let fork_block = BlockNumHash::new(
1141 tx_block.checked_sub(1).ok_or_else(|| {
1142 eyre::eyre!("cannot replay a transaction in the genesis block")
1143 })?,
1144 block.header().parent_hash(),
1145 );
1146 Ok(TransactionForkTarget { fork_block, transaction: tx, block, mined: true, position })
1147 } else {
1148 if self.networks.is_monad() {
1149 eyre::bail!(
1150 "transaction {transaction} is pending and has no canonical block context"
1151 );
1152 }
1153 let block = fork.backend().get_full_block(BlockNumberOrTag::Latest)?;
1154
1155 let fork_block = BlockNumHash::new(block.header().number(), block.header().hash);
1156
1157 Ok(TransactionForkTarget {
1158 fork_block,
1159 transaction: tx,
1160 block,
1161 mined: false,
1162 position: None,
1163 })
1164 }
1165 }
1166
1167 fn full_block_tx_envs(block: &AnyRpcBlock) -> eyre::Result<Vec<TxEnvFor<FEN>>> {
1169 let BlockTransactions::Full(transactions) = block.transactions() else {
1170 eyre::bail!("block {} does not contain full transactions", block.header().number());
1171 };
1172 transactions.iter().map(TxEnvFor::<FEN>::from_any_rpc_transaction).collect()
1173 }
1174
1175 fn replay_tx_env(tx: &AnyRpcTransaction) -> eyre::Result<Option<TxEnvFor<FEN>>> {
1178 let is_system = is_known_system_sender(tx.from()) || tx.ty() == SYSTEM_TRANSACTION_TYPE;
1179 if is_system {
1180 #[cfg(not(feature = "monad"))]
1181 return Ok(None);
1182 #[cfg(feature = "monad")]
1183 return Ok(TxEnvFor::<FEN>::from_any_rpc_transaction(tx).ok());
1184 }
1185
1186 TxEnvFor::<FEN>::from_any_rpc_transaction(tx).map(Some)
1187 }
1188
1189 fn block_context_inputs_from_backend(
1191 backend: &SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
1192 block: &AnyRpcBlock,
1193 ) -> eyre::Result<BlockContext<FEN>> {
1194 let current = Self::full_block_tx_envs(block)?;
1195
1196 let parent_hash = block.header().parent_hash();
1197 let parent_block = if parent_hash.is_zero() {
1198 None
1199 } else {
1200 let parent_number = block.header().number().checked_sub(1).ok_or_else(|| {
1201 eyre::eyre!("genesis block has non-zero parent hash {parent_hash}")
1202 })?;
1203 let parent = backend
1204 .get_full_block(parent_hash)
1205 .wrap_err_with(|| format!("failed to fetch parent block {parent_hash}"))?;
1206 ensure_block_identity(
1207 &parent,
1208 BlockNumHash::new(parent_number, parent_hash),
1209 "parent",
1210 )?;
1211 Some(parent)
1212 };
1213 let parent =
1214 parent_block.as_ref().map(Self::full_block_tx_envs).transpose()?.unwrap_or_default();
1215
1216 let grandparent = if let Some(parent_block) = &parent_block {
1217 let grandparent_hash = parent_block.header().parent_hash();
1218 if grandparent_hash.is_zero() {
1219 Vec::new()
1220 } else {
1221 let block = backend.get_full_block(grandparent_hash).wrap_err_with(|| {
1222 format!("failed to fetch grandparent block {grandparent_hash}")
1223 })?;
1224 let grandparent_number =
1225 parent_block.header().number().checked_sub(1).ok_or_else(|| {
1226 eyre::eyre!("genesis block has non-zero parent hash {grandparent_hash}")
1227 })?;
1228 ensure_block_identity(
1229 &block,
1230 BlockNumHash::new(grandparent_number, grandparent_hash),
1231 "grandparent",
1232 )?;
1233 Self::full_block_tx_envs(&block)?
1234 }
1235 } else {
1236 Vec::new()
1237 };
1238
1239 Ok(BlockContext::new(grandparent, parent, current))
1240 }
1241
1242 fn block_context_inputs(
1244 &self,
1245 id: LocalForkId,
1246 block: &AnyRpcBlock,
1247 ) -> eyre::Result<BlockContext<FEN>> {
1248 let fork = self.inner.get_fork_by_id(id)?;
1249 Self::block_context_inputs_from_backend(fork.backend(), block)
1250 }
1251
1252 #[cfg(feature = "monad")]
1254 fn context_for_block_position(
1255 block_context: BlockContext<FEN>,
1256 position: ForkPosition,
1257 tx: &TxEnvFor<FEN>,
1258 ) -> eyre::Result<ChainFor<FEN>> {
1259 let cursor = match position {
1260 ForkPosition::AfterBlock { .. } => block_context.into_child(),
1261 ForkPosition::BeforeTransaction { transaction_index, .. } => {
1262 block_context.before_transaction(transaction_index)?
1263 }
1264 };
1265 Ok(cursor.next_transaction(tx))
1266 }
1267
1268 #[cfg(feature = "monad")]
1270 fn context_for_fork_synthetic_transaction(
1271 &self,
1272 id: LocalForkId,
1273 tx: &TxEnvFor<FEN>,
1274 ) -> eyre::Result<ChainFor<FEN>> {
1275 if !self.networks.is_monad() {
1276 return Ok(ChainFor::<FEN>::for_transaction(tx));
1277 }
1278
1279 let fork = self.inner.get_fork_by_id(id)?;
1280 let (position_block, position) = match fork.position {
1281 position @ (ForkPosition::AfterBlock { block }
1282 | ForkPosition::BeforeTransaction { block, .. }) => (block, position),
1283 };
1284 let block = fork.backend().get_full_block(position_block.hash).wrap_err_with(|| {
1285 format!(
1286 "failed to fetch fork block {} ({})",
1287 position_block.number, position_block.hash
1288 )
1289 })?;
1290 ensure_block_identity(&block, position_block, "fork")?;
1291 let context = Self::block_context_inputs_from_backend(fork.backend(), &block)?;
1292 Self::context_for_block_position(context, position, tx)
1293 }
1294
1295 pub fn block_context_for_synthetic_transaction(
1297 &self,
1298 ) -> eyre::Result<Option<BlockContext<FEN>>> {
1299 if !self.networks.is_monad() {
1300 return Ok(None);
1301 }
1302 let Some(id) = self.active_fork_id() else {
1303 return Ok(None);
1304 };
1305
1306 let fork = self.inner.get_fork_by_id(id)?;
1307 let (position_block, transaction_index) = match fork.position {
1308 ForkPosition::AfterBlock { block } => (block, None),
1309 ForkPosition::BeforeTransaction { block, transaction_index } => {
1310 (block, Some(transaction_index))
1311 }
1312 };
1313 let block = fork.backend().get_full_block(position_block.hash).wrap_err_with(|| {
1314 format!(
1315 "failed to fetch active fork block {} ({})",
1316 position_block.number, position_block.hash
1317 )
1318 })?;
1319 ensure_block_identity(&block, position_block, "active fork")?;
1320 let context = Self::block_context_inputs_from_backend(fork.backend(), &block)?;
1321
1322 match transaction_index {
1323 Some(index) => context.before_transaction(index).map(Some),
1324 None => Ok(Some(context.into_child())),
1325 }
1326 }
1327
1328 fn apply_fork_tx_replay_env_changes(
1330 &self,
1331 id: LocalForkId,
1332 evm_env: &mut EvmEnvFor<FEN>,
1333 ) -> eyre::Result<()> {
1334 let fork_id = self.inner.ensure_fork_id(id).cloned()?;
1335 let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
1336 self.apply_fork_tx_replay_env_changes_for(&fork_id, source_chain_id, evm_env)
1337 }
1338
1339 fn apply_fork_tx_replay_env_changes_for(
1341 &self,
1342 fork_id: &ForkId,
1343 source_chain_id: ChainId,
1344 evm_env: &mut EvmEnvFor<FEN>,
1345 ) -> eyre::Result<()> {
1346 let fork_evm_env = self
1347 .forks
1348 .get_evm_env(fork_id.clone())?
1349 .ok_or_else(|| eyre::eyre!("Requested fork `{fork_id}` does not exist"))?;
1350 evm_env.cfg_env.chain_id = fork_evm_env.cfg_env.chain_id;
1351 apply_chain_specific_tx_replay_env_changes_for_chain(evm_env, source_chain_id);
1352 Ok(())
1353 }
1354
1355 fn populate_rolled_active_fork(
1357 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1358 persistent_accounts: &HashSet<Address>,
1359 caller: Option<Address>,
1360 journaled_state: &mut JournaledState,
1361 ) {
1362 let mut persistent_addrs = persistent_accounts.clone();
1363 persistent_addrs.extend(caller);
1364
1365 fork.journaled_state.depth = journaled_state.depth;
1366
1367 for addr in persistent_addrs {
1368 merge_journaled_state_data(addr, journaled_state, &mut fork.journaled_state);
1369 }
1370
1371 for (addr, acc) in &journaled_state.state {
1372 if acc.is_created() && acc.is_touched() {
1373 merge_journaled_state_data(*addr, journaled_state, &mut fork.journaled_state);
1374 } else if !acc.is_created() {
1375 let _ = fork.journaled_state.load_account(&mut fork.db, *addr);
1376 }
1377 }
1378
1379 *journaled_state = fork.journaled_state.clone();
1380 }
1381
1382 fn reset_rolled_active_fork(
1384 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1385 fork_init_journaled_state: &JournaledState,
1386 persistent_accounts: &HashSet<Address>,
1387 caller: Option<Address>,
1388 journaled_state: &mut JournaledState,
1389 ) {
1390 fork.journaled_state = fork_init_journaled_state.clone();
1391 Self::populate_rolled_active_fork(fork, persistent_accounts, caller, journaled_state);
1392 }
1393
1394 fn roll_fork_with_context(
1396 &mut self,
1397 id: Option<LocalForkId>,
1398 block_number: u64,
1399 evm_env: &mut EvmEnvFor<FEN>,
1400 tx_env: Option<&TxEnvFor<FEN>>,
1401 journaled_state: &mut JournaledState,
1402 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1403 trace!(?id, ?block_number, "roll fork");
1404 let id = self.ensure_fork(id)?;
1405 let rolled = self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?;
1406 self.apply_rolled_fork_with_context(id, rolled, evm_env, tx_env, journaled_state)
1407 }
1408
1409 fn roll_fork_exact_with_context(
1410 &mut self,
1411 id: LocalForkId,
1412 block: BlockNumHash,
1413 evm_env: &mut EvmEnvFor<FEN>,
1414 tx_env: Option<&TxEnvFor<FEN>>,
1415 journaled_state: &mut JournaledState,
1416 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1417 trace!(?id, ?block, "roll fork to exact block");
1418 let rolled = self.forks.roll_fork_exact(self.inner.ensure_fork_id(id).cloned()?, block)?;
1419 self.apply_rolled_fork_with_context(id, rolled, evm_env, tx_env, journaled_state)
1420 }
1421
1422 fn apply_rolled_fork_with_context(
1423 &mut self,
1424 id: LocalForkId,
1425 rolled: ForkResult<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
1426 evm_env: &mut EvmEnvFor<FEN>,
1427 _tx_env: Option<&TxEnvFor<FEN>>,
1428 journaled_state: &mut JournaledState,
1429 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1430 let ForkResult { id: fork_id, backend, env: fork_env, resolved } = rolled;
1431 let context = resolved.context();
1432 let block = resolved.block();
1433 let _affects_active = self.is_active_fork(id);
1434
1435 #[cfg(feature = "monad")]
1436 let context_update = if _affects_active && let Some(tx) = _tx_env {
1437 let chain_context = if self.networks.is_monad() {
1438 let block_data = backend.get_full_block(block.hash).wrap_err_with(|| {
1439 format!("failed to fetch rolled fork block {} ({})", block.number, block.hash)
1440 })?;
1441 ensure_block_identity(&block_data, block, "rolled fork")?;
1442 let block_context = Self::block_context_inputs_from_backend(&backend, &block_data)?;
1443 block_context.into_child().next_transaction(tx)
1444 } else {
1445 ChainFor::<FEN>::for_transaction(tx)
1446 };
1447 ContextUpdate::Replace(chain_context)
1448 } else {
1449 ContextUpdate::Unchanged
1450 };
1451 #[cfg(not(feature = "monad"))]
1452 let context_update = std::marker::PhantomData;
1453
1454 self.inner.roll_fork(id, fork_id, block, context.source_chain_id, backend)?;
1456
1457 if let Some((active_id, active_idx)) = self.active_fork_ids
1458 && active_id == id
1459 {
1460 let preserved_spec = evm_env.cfg_env.spec;
1461 *evm_env = fork_env;
1462 evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1463
1464 let persistent_accounts = self.inner.persistent_accounts.clone();
1465 let caller = self.inner.caller;
1466 let active = self.inner.get_fork_mut(active_idx);
1467 Self::reset_rolled_active_fork(
1468 active,
1469 &self.fork_init_journaled_state,
1470 &persistent_accounts,
1471 caller,
1472 journaled_state,
1473 );
1474 }
1475
1476 Ok(context_update)
1477 }
1478
1479 fn roll_fork_to_transaction_with_context(
1482 &mut self,
1483 id: Option<LocalForkId>,
1484 transaction: B256,
1485 evm_env: &mut EvmEnvFor<FEN>,
1486 tx_env: Option<&TxEnvFor<FEN>>,
1487 journaled_state: &mut JournaledState,
1488 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1489 if !self.networks.is_monad() {
1490 return self.roll_fork_to_transaction_inner(
1491 id,
1492 transaction,
1493 evm_env,
1494 tx_env,
1495 journaled_state,
1496 );
1497 }
1498
1499 #[cfg(not(feature = "monad"))]
1500 unreachable!("block context is only required when Monad support is enabled");
1501
1502 #[cfg(feature = "monad")]
1503 {
1504 trace!(?id, ?transaction, "roll fork to transaction");
1505 let id = self.ensure_fork(id)?;
1506 let affects_active = self.is_active_fork(id);
1507 let TransactionForkTarget { fork_block, block, position, .. } =
1508 self.get_block_number_and_block_for_transaction(id, transaction)?;
1509 let position = position.expect("Monad transaction target includes canonical position");
1510 let block_context = self.block_context_inputs(id, &block)?;
1511 let context_update = if affects_active && let Some(tx) = tx_env {
1512 let fork_position = ForkPosition::BeforeTransaction {
1513 block: BlockNumHash::new(block.header().number(), block.header().hash),
1514 transaction_index: position.index,
1515 };
1516 ContextUpdate::Replace(Self::context_for_block_position(
1517 block_context.clone(),
1518 fork_position,
1519 tx,
1520 )?)
1521 } else if affects_active {
1522 ContextUpdate::Unchanged
1523 } else {
1524 ContextUpdate::Rebase
1525 };
1526
1527 let current_fork_id = self.inner.ensure_fork_id(id).cloned()?;
1528 let ForkResult { id: fork_id, backend, env: fork_env, resolved } =
1529 self.forks.roll_fork_exact(current_fork_id, fork_block)?;
1530 let staged_fork_journaled_state = if affects_active {
1531 self.fork_init_journaled_state.clone()
1532 } else {
1533 self.inner.get_fork_by_id(id)?.journaled_state.clone()
1534 };
1535 let mut staged_fork = self.inner.stage_fork_roll(
1536 id,
1537 fork_id,
1538 fork_block,
1539 resolved.context().source_chain_id,
1540 backend,
1541 staged_fork_journaled_state,
1542 )?;
1543 let mut staged_evm_env = evm_env.clone();
1544 let mut staged_journaled_state = journaled_state.clone();
1545
1546 if affects_active {
1547 let preserved_spec = staged_evm_env.cfg_env.spec;
1548 staged_evm_env = fork_env;
1549 staged_evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1550 Self::populate_rolled_active_fork(
1551 &mut staged_fork.fork,
1552 &self.inner.persistent_accounts,
1553 self.inner.caller,
1554 &mut staged_journaled_state,
1555 );
1556 }
1557
1558 update_env_block::<AnyNetwork, _, _>(
1559 &mut staged_evm_env,
1560 &block,
1561 staged_fork.fork.source_chain_id,
1562 self.networks,
1563 );
1564 let mut replay_env = staged_evm_env.clone();
1565 self.apply_fork_tx_replay_env_changes_for(
1566 &staged_fork.fork_id,
1567 staged_fork.fork.source_chain_id,
1568 &mut replay_env,
1569 )?;
1570 let target = Self::replay_until(
1571 &mut staged_fork.fork,
1572 ReplayInputs { evm_env: replay_env, networks: self.networks },
1573 &block,
1574 Some(&block_context),
1575 transaction,
1576 &mut staged_journaled_state,
1577 &self.inner.persistent_accounts,
1578 )?;
1579 eyre::ensure!(
1580 target.is_some(),
1581 "transaction {transaction:?} is missing from block {}",
1582 block.header().number()
1583 );
1584 staged_fork.fork.position = ForkPosition::BeforeTransaction {
1585 block: BlockNumHash::new(block.header().number(), block.header().hash),
1586 transaction_index: position.index,
1587 };
1588
1589 self.forks
1591 .update_block_env(staged_fork.fork_id.clone(), staged_evm_env.block_env.clone())?;
1592 self.inner.publish_fork_roll(staged_fork);
1593 *evm_env = staged_evm_env;
1594 *journaled_state = staged_journaled_state;
1595 Ok(context_update)
1596 }
1597 }
1598
1599 fn roll_fork_to_transaction_inner(
1601 &mut self,
1602 id: Option<LocalForkId>,
1603 transaction: B256,
1604 evm_env: &mut EvmEnvFor<FEN>,
1605 _tx_env: Option<&TxEnvFor<FEN>>,
1606 journaled_state: &mut JournaledState,
1607 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1608 trace!(?id, ?transaction, "roll fork to transaction");
1609 let id = self.ensure_fork(id)?;
1610 let _affects_active = self.is_active_fork(id);
1611
1612 let TransactionForkTarget { fork_block, block, mined, position, .. } =
1613 self.get_block_number_and_block_for_transaction(id, transaction)?;
1614 let block_context = if self.networks.is_monad() {
1615 Some(self.block_context_inputs(id, &block)?)
1616 } else {
1617 None
1618 };
1619 #[cfg(feature = "monad")]
1620 let context_update = if _affects_active && let Some(tx) = _tx_env {
1621 let chain_context = if let Some(context) = &block_context {
1622 let fork_position = ForkPosition::BeforeTransaction {
1623 block: BlockNumHash::new(block.header().number(), block.header().hash),
1624 transaction_index: position
1625 .expect("Monad transaction target includes canonical position")
1626 .index,
1627 };
1628 Self::context_for_block_position(context.clone(), fork_position, tx)?
1629 } else {
1630 ChainFor::<FEN>::for_transaction(tx)
1631 };
1632 ContextUpdate::Replace(chain_context)
1633 } else if _affects_active {
1634 ContextUpdate::Unchanged
1635 } else {
1636 ContextUpdate::Rebase
1637 };
1638 #[cfg(not(feature = "monad"))]
1639 let context_update = std::marker::PhantomData;
1640
1641 self.roll_fork_exact_with_context(id, fork_block, evm_env, None, journaled_state)?;
1643
1644 let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
1645 update_env_block::<AnyNetwork, _, _>(evm_env, &block, source_chain_id, self.networks);
1646
1647 let mut replay_env = evm_env.clone();
1648 self.apply_fork_tx_replay_env_changes(id, &mut replay_env)?;
1649 let persistent_accounts = self.inner.persistent_accounts.clone();
1650 let target = if mined {
1651 let fork = self.inner.get_fork_by_id_mut(id)?;
1652 Self::replay_until(
1653 fork,
1654 ReplayInputs { evm_env: replay_env, networks: self.networks },
1655 &block,
1656 block_context.as_ref(),
1657 transaction,
1658 journaled_state,
1659 &persistent_accounts,
1660 )?
1661 } else {
1662 None
1663 };
1664 if target.is_some()
1665 && let Some(position) = position
1666 {
1667 self.inner.get_fork_by_id_mut(id)?.position = ForkPosition::BeforeTransaction {
1668 block: BlockNumHash::new(block.header().number(), block.header().hash),
1669 transaction_index: position.index,
1670 };
1671 }
1672
1673 let fork_id = self.inner.ensure_fork_id(id).cloned().expect("fork was resolved above");
1676 let _ = self.forks.update_block_env(fork_id, evm_env.block_env.clone());
1677
1678 Ok(context_update)
1679 }
1680
1681 fn replay_until(
1685 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1686 replay: ReplayInputs<FEN>,
1687 full_block: &AnyRpcBlock,
1688 block_context: Option<&BlockContext<FEN>>,
1689 tx_hash: B256,
1690 journaled_state: &mut JournaledState,
1691 persistent_accounts: &HashSet<Address>,
1692 ) -> eyre::Result<Option<AnyRpcTransaction>> {
1693 let ReplayInputs { evm_env, networks } = replay;
1694 trace!(?tx_hash, "replay until transaction");
1695 eyre::ensure!(
1696 !networks.is_monad() || block_context.is_some(),
1697 "block context is required to replay transactions for this network"
1698 );
1699
1700 let BlockTransactions::Full(transactions) = full_block.transactions() else {
1701 eyre::bail!(
1702 "block {} does not contain full transactions",
1703 full_block.header().number()
1704 );
1705 };
1706 let Some(target_index) = transactions.iter().position(|tx| tx.tx_hash() == tx_hash) else {
1707 return Ok(None);
1708 };
1709 if networks.is_monad() {
1710 eyre::ensure!(
1711 fork.position
1712 .after_transaction(
1713 BlockNumHash::new(full_block.header().number(), full_block.header().hash),
1714 full_block.header().parent_hash(),
1715 0,
1716 transactions.len(),
1717 )
1718 .is_some(),
1719 "block {} does not immediately follow the active fork position",
1720 full_block.header().number()
1721 );
1722 }
1723 let target_tx = transactions[target_index].clone();
1724 let factory = FEN::EvmFactory::default();
1725 let mut txs_to_replay = Vec::with_capacity(target_index);
1726 for (index, tx) in transactions[..target_index].iter().enumerate() {
1727 let Some(tx_env) = Self::replay_tx_env(tx)? else { continue };
1728 let is_system = is_known_system_sender(tx.from()) || tx.ty() == SYSTEM_TRANSACTION_TYPE;
1729 txs_to_replay.push((index, tx.clone(), tx_env, is_system));
1730 }
1731
1732 if !txs_to_replay.is_empty() {
1734 let now = Instant::now();
1735
1736 let chain_id = evm_env.cfg_env.chain_id;
1739 let timestamp = evm_env.block_env.timestamp().saturating_to();
1740 let mut replay_db = fork.db.clone();
1741
1742 if let Some(context) = block_context {
1743 for (index, tx, tx_env, is_system) in &txs_to_replay {
1744 let chain_context = context.transaction(*index);
1745 let mut evm =
1746 factory.create_evm_with_context(replay_db, evm_env.clone(), chain_context);
1747 networks.inject_chain_precompiles(evm.precompiles_mut(), chain_id, timestamp);
1748 trace!(tx=?tx.tx_hash(), "committing transaction");
1749 let result = if *is_system {
1750 #[cfg(feature = "monad")]
1751 let Some(result) = factory
1752 .try_transact_system_replay(&mut evm, tx_env)
1753 .wrap_err("backend: failed replaying system transaction")?
1754 else {
1755 replay_db = evm.into_db();
1756 continue;
1757 };
1758 #[cfg(not(feature = "monad"))]
1759 unreachable!("system transactions are filtered without Monad support");
1760 #[cfg(feature = "monad")]
1761 result
1762 } else {
1763 evm.transact(tx_env.clone())
1764 .wrap_err("backend: failed replaying transaction")?
1765 };
1766 evm.db_mut().commit(result.state);
1767 replay_db = evm.into_db();
1768 }
1769 } else {
1770 let mut evm = factory.create_evm(replay_db, evm_env);
1771 networks.inject_chain_precompiles(evm.precompiles_mut(), chain_id, timestamp);
1772 for (_, tx, tx_env, is_system) in &txs_to_replay {
1773 trace!(tx=?tx.tx_hash(), "committing transaction");
1774 let result = if *is_system {
1775 #[cfg(feature = "monad")]
1776 let Some(result) = factory
1777 .try_transact_system_replay(&mut evm, tx_env)
1778 .wrap_err("backend: failed replaying system transaction")?
1779 else {
1780 continue;
1781 };
1782 #[cfg(not(feature = "monad"))]
1783 unreachable!("system transactions are filtered without Monad support");
1784 #[cfg(feature = "monad")]
1785 result
1786 } else {
1787 evm.transact(tx_env.clone())
1788 .wrap_err("backend: failed replaying transaction")?
1789 };
1790 evm.db_mut().commit(result.state);
1791 }
1792 replay_db = evm.into_db();
1793 }
1794
1795 fork.db = replay_db;
1797
1798 fork.refresh_journaled_states(journaled_state, persistent_accounts)?;
1801
1802 trace!(elapsed=?now.elapsed(), count=txs_to_replay.len(), "replayed transactions");
1803 }
1804
1805 Ok(Some(target_tx))
1806 }
1807}
1808
1809fn ensure_block_identity(
1810 block: &AnyRpcBlock,
1811 expected: BlockNumHash,
1812 relation: &str,
1813) -> eyre::Result<()> {
1814 eyre::ensure!(
1815 block.header().number() == expected.number && block.header().hash == expected.hash,
1816 "{relation} block changed: expected {} ({}), got {} ({})",
1817 expected.number,
1818 expected.hash,
1819 block.header().number(),
1820 block.header().hash
1821 );
1822 Ok(())
1823}
1824
1825impl<FEN: FoundryEvmNetwork> DatabaseExt<FEN::EvmFactory> for Backend<FEN> {
1826 fn chain_context_for_synthetic_transaction(
1827 &self,
1828 tx: &TxEnvFor<FEN>,
1829 ) -> eyre::Result<ChainFor<FEN>> {
1830 self.block_context_for_synthetic_transaction()?.map_or_else(
1831 || Ok(ChainFor::<FEN>::for_transaction(tx)),
1832 |context| Ok(context.next_transaction(tx)),
1833 )
1834 }
1835
1836 fn snapshot_state(
1837 &mut self,
1838 journaled_state: &JournaledState,
1839 evm_env: &EvmEnvFor<FEN>,
1840 ) -> U256 {
1841 trace!("create snapshot");
1842 let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new(
1843 self.create_db_snapshot(),
1844 journaled_state.clone(),
1845 evm_env.clone(),
1846 ));
1847 trace!(target: "backend", "Created new snapshot {}", id);
1848 id
1849 }
1850
1851 fn revert_state(
1852 &mut self,
1853 id: U256,
1854 current_state: &JournaledState,
1855 evm_env: &mut EvmEnvFor<FEN>,
1856 caller: Address,
1857 action: RevertStateSnapshotAction,
1858 ) -> Option<JournaledState> {
1859 trace!(?id, "revert snapshot");
1860 if let Some(mut snapshot) = self.inner.state_snapshots.remove_at(id) {
1861 if action.is_keep() {
1863 self.inner.state_snapshots.insert_at(snapshot.clone(), id);
1864 }
1865
1866 if let Some(account) = current_state.state.get(&CHEATCODE_ADDRESS)
1871 && let Some(slot) = account.storage.get(&GLOBAL_FAIL_SLOT)
1872 && !slot.present_value.is_zero()
1873 {
1874 self.set_state_snapshot_failure(true);
1875 }
1876
1877 snapshot.merge(current_state);
1879 let BackendStateSnapshot { db, mut journaled_state, snap_evm_env } = snapshot;
1880 match db {
1881 BackendDatabaseSnapshot::InMemory(mem_db) => {
1882 self.mem_db = mem_db;
1883 }
1884 BackendDatabaseSnapshot::Forked(id, fork_id, idx, mut fork) => {
1885 journaled_state.state.entry(caller).or_insert_with(|| {
1889 let caller_account = current_state
1890 .state
1891 .get(&caller)
1892 .map(|acc| acc.info.clone())
1893 .unwrap_or_default();
1894
1895 if !fork.db.cache.accounts.contains_key(&caller) {
1896 fork.db.insert_account_info(caller, caller_account.clone());
1898 }
1899 caller_account.into()
1900 });
1901 self.inner.revert_state_snapshot(id, fork_id, idx, *fork);
1902 self.active_fork_ids = Some((id, idx))
1903 }
1904 }
1905
1906 *evm_env = snap_evm_env;
1907 trace!(target: "backend", "Reverted snapshot {}", id);
1908
1909 Some(journaled_state)
1910 } else {
1911 warn!(target: "backend", "No snapshot to revert for {}", id);
1912 None
1913 }
1914 }
1915
1916 fn delete_state_snapshot(&mut self, id: U256) -> bool {
1917 self.inner.state_snapshots.remove_at(id).is_some()
1918 }
1919
1920 fn delete_state_snapshots(&mut self) {
1921 self.inner.state_snapshots.clear()
1922 }
1923
1924 fn create_fork(&mut self, create_fork: CreateFork) -> eyre::Result<LocalForkId> {
1925 trace!("create fork");
1926 let ForkResult { id: fork_id, backend: fork, resolved, .. } =
1927 self.forks.create_fork(create_fork)?;
1928 let context = resolved.context();
1929 let block = resolved.block();
1930 let fork_db = ForkDB::new(fork);
1931 let (id, _) = self.inner.insert_new_fork(
1932 fork_id,
1933 block,
1934 context.source_chain_id,
1935 fork_db,
1936 self.fork_init_journaled_state.clone(),
1937 );
1938 Ok(id)
1939 }
1940
1941 fn create_fork_at_transaction(
1942 &mut self,
1943 fork: CreateFork,
1944 transaction: B256,
1945 ) -> eyre::Result<LocalForkId> {
1946 trace!(?transaction, "create fork at transaction");
1947 let id = self.create_fork(fork)?;
1948 let fork_id = self.ensure_fork_id(id).cloned()?;
1949 let mut evm_env = self
1950 .forks
1951 .get_evm_env(fork_id)?
1952 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
1953
1954 self.roll_fork_to_transaction_with_context(
1957 Some(id),
1958 transaction,
1959 &mut evm_env,
1960 None,
1961 &mut self.inner.new_journaled_state(),
1962 )?;
1963
1964 Ok(id)
1965 }
1966
1967 fn select_fork(
1970 &mut self,
1971 id: LocalForkId,
1972 evm_env: &mut EvmEnvFor<FEN>,
1973 tx_env: &mut TxEnvFor<FEN>,
1974 active_journaled_state: &mut JournaledState,
1975 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1976 trace!(?id, "select fork");
1977 if self.is_active_fork(id) {
1978 #[cfg(feature = "monad")]
1980 return Ok(ContextUpdate::Unchanged);
1981 #[cfg(not(feature = "monad"))]
1982 return Ok(std::marker::PhantomData);
1983 }
1984
1985 #[cfg(feature = "monad")]
1986 let chain_context = self.context_for_fork_synthetic_transaction(id, tx_env)?;
1987
1988 if let Some(active_fork_id) = self.active_fork_id() {
1991 self.forks.update_block(
1992 self.ensure_fork_id(active_fork_id).cloned()?,
1993 evm_env.block_env.number(),
1994 evm_env.block_env.timestamp(),
1995 )?;
1996 }
1997
1998 let fork_id = self.ensure_fork_id(id).cloned()?;
1999 let idx = self.inner.ensure_fork_index(&fork_id)?;
2000 let fork_evm_env = self
2001 .forks
2002 .get_evm_env(fork_id)?
2003 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
2004
2005 if let Some(active) = self.active_fork_mut() {
2008 active.journaled_state = active_journaled_state.clone();
2009
2010 let caller = tx_env.caller();
2011 let caller_account = active.journaled_state.state.get(&caller).cloned();
2012 let target_fork = self.inner.get_fork_mut(idx);
2013
2014 if target_fork.journaled_state.depth == 0 {
2016 if let Some(mut acc) = caller_account {
2018 let fork_account = Database::basic(&mut target_fork.db, caller)?
2019 .ok_or(BackendError::MissingAccount(caller))?;
2020
2021 acc.info = fork_account;
2022 target_fork.journaled_state.state.insert(caller, acc);
2023 }
2024 }
2025 } else {
2026 self.set_init_journaled_state(active_journaled_state.clone());
2033 self.prepare_init_journal_state()?;
2034
2035 self.fork_init_journaled_state.depth = 0;
2037 }
2038
2039 {
2040 let mut fork = self.inner.take_fork(idx);
2042
2043 let persistent_accounts = self.inner.persistent_accounts.clone();
2051 if let Some(db) = self.active_fork_db_mut() {
2052 for addr in persistent_accounts {
2053 let Ok(db_account) = db.load_account(addr) else { continue };
2054
2055 let Some(fork_account) = fork.journaled_state.state.get_mut(&addr) else {
2056 continue;
2057 };
2058
2059 for (key, val) in &db_account.storage {
2060 if let Some(fork_storage) = fork_account.storage.get_mut(key) {
2061 fork_storage.present_value = *val;
2062 }
2063 }
2064 }
2065 }
2066
2067 fork.journaled_state.depth = active_journaled_state.depth;
2072
2073 let caller = tx_env.caller();
2077 fork.journaled_state.state.entry(caller).or_insert_with(|| {
2078 let caller_account = active_journaled_state
2079 .state
2080 .get(&caller)
2081 .map(|acc| acc.info.clone())
2082 .unwrap_or_default();
2083
2084 if !fork.db.cache.accounts.contains_key(&caller) {
2085 fork.db.insert_account_info(caller, caller_account.clone());
2087 }
2088 caller_account.into()
2089 });
2090
2091 self.update_fork_db(active_journaled_state, &mut fork);
2092
2093 self.inner.set_fork(idx, fork);
2095 }
2096
2097 self.active_fork_ids = Some((id, idx));
2098 let preserved_spec = evm_env.cfg_env.spec;
2102 tx_env.set_chain_id(Some(fork_evm_env.cfg_env.chain_id));
2103 *evm_env = fork_evm_env;
2104 evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
2105
2106 #[cfg(feature = "monad")]
2107 return Ok(ContextUpdate::Replace(chain_context));
2108 #[cfg(not(feature = "monad"))]
2109 Ok(std::marker::PhantomData)
2110 }
2111
2112 fn roll_fork(
2115 &mut self,
2116 id: Option<LocalForkId>,
2117 block_number: u64,
2118 evm_env: &mut EvmEnvFor<FEN>,
2119 tx_env: &TxEnvFor<FEN>,
2120 journaled_state: &mut JournaledState,
2121 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2122 self.roll_fork_with_context(id, block_number, evm_env, Some(tx_env), journaled_state)
2123 }
2124
2125 fn roll_fork_to_transaction(
2126 &mut self,
2127 id: Option<LocalForkId>,
2128 transaction: B256,
2129 evm_env: &mut EvmEnvFor<FEN>,
2130 tx_env: &TxEnvFor<FEN>,
2131 journaled_state: &mut JournaledState,
2132 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2133 self.roll_fork_to_transaction_with_context(
2134 id,
2135 transaction,
2136 evm_env,
2137 Some(tx_env),
2138 journaled_state,
2139 )
2140 }
2141
2142 fn transact(
2143 &mut self,
2144 maybe_id: Option<LocalForkId>,
2145 transaction: B256,
2146 mut evm_env: EvmEnvFor<FEN>,
2147 _outer_tx_env: &TxEnvFor<FEN>,
2148 journaled_state: &mut JournaledState,
2149 inspector: &mut dyn for<'db> FoundryInspectorExt<
2150 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2151 >,
2152 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2153 trace!(?maybe_id, ?transaction, "execute transaction");
2154 let persistent_accounts = self.inner.persistent_accounts.clone();
2155 let id = self.ensure_fork(maybe_id)?;
2156 let _affects_active = self.is_active_fork(id);
2157 let fork_id = self.ensure_fork_id(id).cloned()?;
2158
2159 let TransactionForkTarget { transaction: tx, block, position, .. } =
2166 self.get_block_number_and_block_for_transaction(id, transaction)?;
2167 let tx_env = TxEnvFor::<FEN>::from_any_rpc_transaction(&tx)?;
2168 let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
2169 update_env_block::<AnyNetwork, _, _>(&mut evm_env, &block, source_chain_id, self.networks);
2170 self.apply_fork_tx_replay_env_changes(id, &mut evm_env)?;
2171
2172 let block_context = if self.networks.is_monad() {
2173 Some(self.block_context_inputs(id, &block)?)
2174 } else {
2175 None
2176 };
2177 let chain_context = if let Some(context) = &block_context {
2178 context.transaction(
2179 position.expect("Monad transaction target includes canonical position").index,
2180 )
2181 } else {
2182 ChainFor::<FEN>::for_transaction(&tx_env)
2183 };
2184
2185 let next_position = if block_context.is_some() {
2186 let position = position.expect("Monad transaction target includes canonical position");
2187 Some(
2188 self.inner
2189 .get_fork_by_id(id)?
2190 .position
2191 .after_transaction(
2192 BlockNumHash::new(block.header().number(), block.header().hash),
2193 block.header().parent_hash(),
2194 position.index,
2195 position.count,
2196 )
2197 .ok_or_else(|| {
2198 eyre::eyre!(
2199 "transaction {transaction} does not immediately follow the active \
2200 fork position"
2201 )
2202 })?,
2203 )
2204 } else {
2205 None
2206 };
2207 #[cfg(feature = "monad")]
2208 let context_update = if _affects_active {
2209 ContextUpdate::Replace(if let Some(context) = block_context {
2210 Self::context_for_block_position(
2211 context,
2212 next_position.expect("block context has a next position"),
2213 _outer_tx_env,
2214 )?
2215 } else {
2216 ChainFor::<FEN>::for_transaction(_outer_tx_env)
2217 })
2218 } else {
2219 ContextUpdate::Rebase
2220 };
2221 #[cfg(not(feature = "monad"))]
2222 let context_update = std::marker::PhantomData;
2223
2224 let fork = self.inner.get_fork_by_id_mut(id)?;
2225 commit_transaction::<FEN>(
2226 TransactionInputs {
2227 evm_env,
2228 tx_env,
2229 chain_context,
2230 rpc_block_number: block.header().number(),
2231 },
2232 journaled_state,
2233 fork,
2234 &fork_id,
2235 self.networks,
2236 &persistent_accounts,
2237 inspector,
2238 )?;
2239 if let Some(position) = next_position {
2240 fork.position = position;
2241 }
2242 Ok(context_update)
2243 }
2244
2245 fn transact_from_tx(
2246 &mut self,
2247 tx_env: TxEnvFor<FEN>,
2248 evm_env: EvmEnvFor<FEN>,
2249 journaled_state: &mut JournaledState,
2250 inspector: &mut dyn for<'db> FoundryInspectorExt<
2251 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2252 >,
2253 ) -> eyre::Result<()> {
2254 trace!("execute signed transaction");
2255
2256 self.commit(journaled_state.state.clone());
2257
2258 let res = {
2259 let mut db = self.clone();
2260 let depth = journaled_state.depth + 1;
2261 let factory = FEN::EvmFactory::default();
2262 let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
2263 let mut evm =
2264 factory.create_foundry_nested_evm(&mut db, evm_env, chain_context, inspector);
2265 evm.journal_inner_mut().depth = depth;
2266 evm.transact_raw(tx_env)?
2267 };
2268
2269 self.commit(res.state);
2270 update_state(&mut journaled_state.state, self, None)?;
2271
2272 Ok(())
2273 }
2274
2275 fn active_fork_id(&self) -> Option<LocalForkId> {
2276 self.active_fork_ids.map(|(id, _)| id)
2277 }
2278
2279 fn active_fork_url(&self) -> Option<String> {
2280 let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
2281 self.forks.get_fork_url(fork.clone()).ok()?
2282 }
2283
2284 fn active_fork_block_number(&self) -> Option<u64> {
2285 if let Some(block_number) = self.fork_block_number_override {
2286 return Some(block_number);
2287 }
2288 let fork = self.inner.get_fork_by_id(self.active_fork_id()?).ok()?;
2289 Some(match fork.position {
2290 ForkPosition::AfterBlock { block } | ForkPosition::BeforeTransaction { block, .. } => {
2291 block.number
2292 }
2293 })
2294 }
2295
2296 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
2297 if let Some(id) = id {
2298 if self.inner.issued_local_fork_ids.contains_key(&id) {
2299 return Ok(id);
2300 }
2301 eyre::bail!("Requested fork `{}` does not exist", id);
2302 }
2303 if let Some(id) = self.active_fork_id() {
2304 Ok(id)
2305 } else {
2306 eyre::bail!("No fork active");
2307 }
2308 }
2309
2310 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
2311 self.inner.ensure_fork_id(id)
2312 }
2313
2314 fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic> {
2315 let active_id = self.active_fork_id()?;
2316 let active_fork = self.active_fork()?;
2317
2318 if self.inner.forks.len() == 1 {
2319 return None;
2322 }
2323
2324 if !active_fork.is_contract(callee) && !is_contract_in_state(evm_state, callee) {
2325 let mut available_on = Vec::new();
2327 for (id, fork) in self.inner.forks_iter().filter(|(id, _)| *id != active_id) {
2328 trace!(?id, address=?callee, "checking if account exists");
2329 if fork.is_contract(callee) {
2330 available_on.push(id);
2331 }
2332 }
2333
2334 return if available_on.is_empty() {
2335 Some(RevertDiagnostic::ContractDoesNotExist {
2336 contract: callee,
2337 active: active_id,
2338 persistent: self.is_persistent(&callee),
2339 })
2340 } else {
2341 Some(RevertDiagnostic::ContractExistsOnOtherForks {
2344 contract: callee,
2345 active: active_id,
2346 available_on,
2347 })
2348 };
2349 }
2350 None
2351 }
2352
2353 fn load_allocs(
2357 &mut self,
2358 allocs: &BTreeMap<Address, GenesisAccount>,
2359 journaled_state: &mut JournaledState,
2360 ) -> Result<(), BackendError> {
2361 for (addr, acc) in allocs {
2363 self.clone_account(acc, addr, journaled_state)?;
2364 }
2365
2366 Ok(())
2367 }
2368
2369 fn clone_account(
2374 &mut self,
2375 source: &GenesisAccount,
2376 target: &Address,
2377 journaled_state: &mut JournaledState,
2378 ) -> Result<(), BackendError> {
2379 let mut state_acc = journaled_state.load_account_mut(self, *target)?;
2382
2383 if let Some(bytecode) = source.code.as_ref() {
2385 let bytecode_hash = keccak256(bytecode);
2386 let bytecode = Bytecode::new_raw(bytecode.0.clone().into());
2387 state_acc.set_code(bytecode_hash, bytecode);
2388 }
2389
2390 state_acc.set_balance(source.balance);
2392
2393 if let Some(acc) = journaled_state.state.get_mut(target) {
2395 if let Some(storage) = source.storage.as_ref() {
2396 for (slot, value) in storage {
2397 let slot = U256::from_be_bytes(slot.0);
2398 acc.storage.insert(
2399 slot,
2400 EvmStorageSlot::new_changed(
2401 acc.storage.get(&slot).map(|s| s.present_value).unwrap_or_default(),
2402 U256::from_be_bytes(value.0),
2403 TransactionId::ZERO,
2404 ),
2405 );
2406 }
2407 }
2408
2409 acc.info.nonce = source.nonce.unwrap_or_default();
2411 };
2412
2413 journaled_state.touch(*target);
2415
2416 Ok(())
2417 }
2418
2419 fn add_persistent_account(&mut self, account: Address) -> bool {
2420 trace!(?account, "add persistent account");
2421 self.inner.persistent_accounts.insert(account)
2422 }
2423
2424 fn refresh_fork_account(
2425 &mut self,
2426 address: Address,
2427 field: ForkAccountField,
2428 journaled_state: &mut JournaledState,
2429 ) -> Result<(), BackendError> {
2430 let Some(fork) = self.active_fork_mut() else { return Ok(()) };
2431 trace!(?address, ?field, "refresh fork account");
2432 fork.db.db.data().accounts.write().remove(&address);
2433
2434 let cache_modified = fork
2435 .db
2436 .cache
2437 .accounts
2438 .get(&address)
2439 .is_some_and(|account| account.account_state != AccountState::None);
2440 if !cache_modified {
2441 fork.db.cache.accounts.remove(&address);
2442 }
2443
2444 if !cache_modified
2445 && !journaled_state.state.contains_key(&address)
2446 && !fork.journaled_state.state.contains_key(&address)
2447 {
2448 return Ok(());
2449 }
2450
2451 let mut refreshed = DatabaseRef::basic_ref(&fork.db.db, address)?.unwrap_or_default();
2452 if matches!(field, ForkAccountField::Code) {
2453 fork.db.insert_contract(&mut refreshed);
2454 }
2455 if let Some(cached) = fork.db.cache.accounts.get_mut(&address) {
2456 field.update(&mut cached.info, &refreshed);
2457 if cached.account_state == AccountState::NotExisting && !refreshed.is_empty() {
2458 cached.account_state = AccountState::None;
2459 }
2460 }
2461 if let Some(journaled_account) = journaled_state.state.get_mut(&address) {
2462 field.update(&mut journaled_account.info, &refreshed);
2463 }
2464 if let Some(journaled_account) = fork.journaled_state.state.get_mut(&address) {
2465 field.update(&mut journaled_account.info, &refreshed);
2466 }
2467 Ok(())
2468 }
2469
2470 fn refresh_fork_storage(
2471 &mut self,
2472 address: Address,
2473 slot: U256,
2474 journaled_state: &mut JournaledState,
2475 ) -> Result<(), BackendError> {
2476 let Some(fork) = self.active_fork_mut() else { return Ok(()) };
2477 trace!(?address, ?slot, "refresh fork storage");
2478 if let Some(storage) = fork.db.db.data().storage.write().get_mut(&address) {
2479 storage.remove(&slot);
2480 }
2481 let cache_modified = fork
2482 .db
2483 .cache
2484 .accounts
2485 .get(&address)
2486 .is_some_and(|account| account.account_state != AccountState::None);
2487 if !cache_modified && let Some(account) = fork.db.cache.accounts.get_mut(&address) {
2488 account.storage.remove(&slot);
2489 }
2490
2491 let outer_loaded = journaled_state
2492 .state
2493 .get(&address)
2494 .is_some_and(|account| account.storage.contains_key(&slot));
2495 let fork_loaded = fork
2496 .journaled_state
2497 .state
2498 .get(&address)
2499 .is_some_and(|account| account.storage.contains_key(&slot));
2500 if !cache_modified && !outer_loaded && !fork_loaded {
2501 return Ok(());
2502 }
2503
2504 let value = DatabaseRef::storage_ref(&fork.db.db, address, slot)?;
2505 if let Some(account) = fork.db.cache.accounts.get_mut(&address) {
2506 account.storage.insert(slot, value);
2507 if account.account_state == AccountState::NotExisting && !value.is_zero() {
2508 account.account_state = AccountState::None;
2509 }
2510 }
2511 if let Some(storage) = journaled_state
2512 .state
2513 .get_mut(&address)
2514 .and_then(|account| account.storage.get_mut(&slot))
2515 {
2516 storage.present_value = value;
2517 }
2518 if let Some(storage) = fork
2519 .journaled_state
2520 .state
2521 .get_mut(&address)
2522 .and_then(|account| account.storage.get_mut(&slot))
2523 {
2524 storage.present_value = value;
2525 }
2526 Ok(())
2527 }
2528
2529 fn remove_persistent_account(&mut self, account: &Address) -> bool {
2530 trace!(?account, "remove persistent account");
2531 self.inner.persistent_accounts.remove(account)
2532 }
2533
2534 fn is_persistent(&self, acc: &Address) -> bool {
2535 self.inner.persistent_accounts.contains(acc)
2536 }
2537
2538 fn allow_cheatcode_access(&mut self, account: Address) -> bool {
2539 trace!(?account, "allow cheatcode access");
2540 self.inner.cheatcode_access_accounts.insert(account)
2541 }
2542
2543 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
2544 trace!(?account, "revoke cheatcode access");
2545 self.inner.cheatcode_access_accounts.remove(account)
2546 }
2547
2548 fn has_cheatcode_access(&self, account: &Address) -> bool {
2549 self.inner.cheatcode_access_accounts.contains(account)
2550 }
2551
2552 fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
2553 if let Some(db) = self.active_fork_db_mut() {
2554 db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
2555 } else {
2556 self.mem_db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
2557 }
2558 }
2559}
2560
2561impl<FEN: FoundryEvmNetwork> DatabaseRef for Backend<FEN> {
2562 type Error = DatabaseError;
2563
2564 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
2565 if let Some(db) = self.active_fork_db() {
2566 db.basic_ref(address)
2567 } else {
2568 Ok(self.mem_db.basic_ref(address)?)
2569 }
2570 }
2571
2572 fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
2573 if let Some(db) = self.active_fork_db() {
2574 db.code_by_hash_ref(code_hash)
2575 } else {
2576 Ok(self.mem_db.code_by_hash_ref(code_hash)?)
2577 }
2578 }
2579
2580 fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
2581 if let Some(db) = self.active_fork_db() {
2582 DatabaseRef::storage_ref(db, address, index)
2583 } else {
2584 Ok(DatabaseRef::storage_ref(&self.mem_db, address, index)?)
2585 }
2586 }
2587
2588 fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
2589 if let Some(db) = self.active_fork_db() {
2590 db.block_hash_ref(number)
2591 } else {
2592 Ok(self.mem_db.block_hash_ref(number)?)
2593 }
2594 }
2595}
2596
2597impl<FEN: FoundryEvmNetwork> DatabaseCommit for Backend<FEN> {
2598 fn commit(&mut self, changes: AddressMap<Account>) {
2599 if let Some(db) = self.active_fork_db_mut() {
2600 db.commit(changes)
2601 } else {
2602 self.mem_db.commit(changes)
2603 }
2604 }
2605}
2606
2607impl<FEN: FoundryEvmNetwork> Database for Backend<FEN> {
2608 type Error = DatabaseError;
2609 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
2610 if let Some(db) = self.active_fork_db_mut() {
2611 Ok(db.basic(address)?)
2612 } else {
2613 Ok(self.mem_db.basic(address)?)
2614 }
2615 }
2616
2617 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
2618 if let Some(db) = self.active_fork_db_mut() {
2619 Ok(db.code_by_hash(code_hash)?)
2620 } else {
2621 Ok(self.mem_db.code_by_hash(code_hash)?)
2622 }
2623 }
2624
2625 fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
2626 if let Some(db) = self.active_fork_db_mut() {
2627 Ok(Database::storage(db, address, index)?)
2628 } else {
2629 Ok(Database::storage(&mut self.mem_db, address, index)?)
2630 }
2631 }
2632
2633 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
2634 if let Some(db) = self.active_fork_db_mut() {
2635 Ok(db.block_hash(number)?)
2636 } else {
2637 Ok(self.mem_db.block_hash(number)?)
2638 }
2639 }
2640}
2641
2642#[derive(Clone, Debug)]
2644pub enum BackendDatabaseSnapshot<N: Network, B: ForkBlockEnv = BlockEnv> {
2645 InMemory(FoundryEvmInMemoryDB),
2647 Forked(LocalForkId, ForkId, ForkLookupIndex, Box<Fork<N, B>>),
2649}
2650
2651#[derive(Clone, Debug)]
2653pub struct Fork<N: Network, B: ForkBlockEnv = BlockEnv> {
2654 db: ForkDB<N, B>,
2655 journaled_state: JournaledState,
2656 source_chain_id: ChainId,
2657 position: ForkPosition,
2658}
2659
2660impl<N: Network, B: ForkBlockEnv> Fork<N, B> {
2661 pub const fn backend(&self) -> &SharedBackend<N, B> {
2663 &self.db.db
2664 }
2665
2666 pub fn is_contract(&self, acc: Address) -> bool {
2668 if let Ok(Some(acc)) = self.db.basic_ref(acc)
2669 && acc.code_hash != KECCAK_EMPTY
2670 {
2671 return true;
2672 }
2673 is_contract_in_state(&self.journaled_state.state, acc)
2674 }
2675
2676 fn refresh_journaled_states(
2679 &mut self,
2680 journaled_state: &mut JournaledState,
2681 persistent_accounts: &HashSet<Address>,
2682 ) -> Result<(), BackendError> {
2683 update_state(&mut journaled_state.state, &mut self.db, Some(persistent_accounts))?;
2684 update_state(&mut self.journaled_state.state, &mut self.db, Some(persistent_accounts))?;
2685 Ok(())
2686 }
2687}
2688
2689pub struct BackendInner<FEN: FoundryEvmNetwork> {
2691 pub launched_with_fork: Option<(ForkId, LocalForkId, ForkLookupIndex)>,
2696 pub issued_local_fork_ids: HashMap<LocalForkId, ForkId>,
2708 pub created_forks: HashMap<ForkId, ForkLookupIndex>,
2711 pub forks: Vec<Option<Fork<AnyNetwork, BlockEnvFor<FEN>>>>,
2714 #[allow(clippy::type_complexity)]
2716 pub state_snapshots: StateSnapshots<
2717 BackendStateSnapshot<
2718 BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
2719 SpecFor<FEN>,
2720 BlockEnvFor<FEN>,
2721 >,
2722 >,
2723 pub has_state_snapshot_failure: bool,
2733 pub caller: Option<Address>,
2735 pub next_fork_id: LocalForkId,
2737 pub persistent_accounts: HashSet<Address>,
2741 pub spec_id: SpecFor<FEN>,
2743 pub cheatcode_access_accounts: HashSet<Address>,
2745}
2746
2747impl<FEN: FoundryEvmNetwork> Clone for BackendInner<FEN> {
2748 fn clone(&self) -> Self {
2749 Self {
2750 launched_with_fork: self.launched_with_fork.clone(),
2751 issued_local_fork_ids: self.issued_local_fork_ids.clone(),
2752 created_forks: self.created_forks.clone(),
2753 forks: self.forks.clone(),
2754 state_snapshots: self.state_snapshots.clone(),
2755 has_state_snapshot_failure: self.has_state_snapshot_failure,
2756 caller: self.caller,
2757 next_fork_id: self.next_fork_id,
2758 persistent_accounts: self.persistent_accounts.clone(),
2759 spec_id: self.spec_id,
2760 cheatcode_access_accounts: self.cheatcode_access_accounts.clone(),
2761 }
2762 }
2763}
2764
2765impl<FEN: FoundryEvmNetwork> Debug for BackendInner<FEN> {
2766 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2767 f.debug_struct("BackendInner")
2768 .field("launched_with_fork", &self.launched_with_fork)
2769 .field("issued_local_fork_ids", &self.issued_local_fork_ids)
2770 .field("created_forks", &self.created_forks)
2771 .field("forks", &self.forks)
2772 .field("state_snapshots", &self.state_snapshots)
2773 .field("has_state_snapshot_failure", &self.has_state_snapshot_failure)
2774 .field("caller", &self.caller)
2775 .field("next_fork_id", &self.next_fork_id)
2776 .field("persistent_accounts", &self.persistent_accounts)
2777 .field("spec_id", &self.spec_id)
2778 .field("cheatcode_access_accounts", &self.cheatcode_access_accounts)
2779 .finish()
2780 }
2781}
2782
2783impl<FEN: FoundryEvmNetwork> BackendInner<FEN> {
2784 pub fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
2785 self.issued_local_fork_ids
2786 .get(&id)
2787 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
2788 }
2789
2790 pub fn ensure_fork_index(&self, id: &ForkId) -> eyre::Result<ForkLookupIndex> {
2791 self.created_forks
2792 .get(id)
2793 .copied()
2794 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
2795 }
2796
2797 pub fn ensure_fork_index_by_local_id(&self, id: LocalForkId) -> eyre::Result<ForkLookupIndex> {
2798 self.ensure_fork_index(self.ensure_fork_id(id)?)
2799 }
2800
2801 #[track_caller]
2803 fn get_fork(&self, idx: ForkLookupIndex) -> &Fork<AnyNetwork, BlockEnvFor<FEN>> {
2804 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
2805 self.forks[idx].as_ref().unwrap()
2806 }
2807
2808 #[track_caller]
2810 fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork<AnyNetwork, BlockEnvFor<FEN>> {
2811 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
2812 self.forks[idx].as_mut().unwrap()
2813 }
2814
2815 #[track_caller]
2817 fn get_fork_by_id_mut(
2818 &mut self,
2819 id: LocalForkId,
2820 ) -> eyre::Result<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
2821 let idx = self.ensure_fork_index_by_local_id(id)?;
2822 Ok(self.get_fork_mut(idx))
2823 }
2824
2825 #[track_caller]
2827 fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
2828 let idx = self.ensure_fork_index_by_local_id(id)?;
2829 Ok(self.get_fork(idx))
2830 }
2831
2832 fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork<AnyNetwork, BlockEnvFor<FEN>> {
2834 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
2835 self.forks[idx].take().unwrap()
2836 }
2837
2838 fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork<AnyNetwork, BlockEnvFor<FEN>>) {
2839 self.forks[idx] = Some(fork)
2840 }
2841
2842 pub fn forks_iter(
2844 &self,
2845 ) -> impl Iterator<Item = (LocalForkId, &Fork<AnyNetwork, BlockEnvFor<FEN>>)> + '_ {
2846 self.issued_local_fork_ids
2847 .iter()
2848 .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id])))
2849 }
2850
2851 pub fn forks_iter_mut(
2853 &mut self,
2854 ) -> impl Iterator<Item = &mut Fork<AnyNetwork, BlockEnvFor<FEN>>> + '_ {
2855 self.forks.iter_mut().filter_map(|f| f.as_mut())
2856 }
2857
2858 pub fn revert_state_snapshot(
2860 &mut self,
2861 id: LocalForkId,
2862 fork_id: ForkId,
2863 idx: ForkLookupIndex,
2864 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
2865 ) {
2866 self.created_forks.insert(fork_id.clone(), idx);
2867 self.issued_local_fork_ids.insert(id, fork_id);
2868 self.set_fork(idx, fork)
2869 }
2870
2871 pub fn update_fork_mapping(
2873 &mut self,
2874 id: LocalForkId,
2875 fork_id: ForkId,
2876 block: BlockNumHash,
2877 source_chain_id: ChainId,
2878 db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
2879 journaled_state: JournaledState,
2880 ) -> ForkLookupIndex {
2881 let idx = self.forks.len();
2882 self.issued_local_fork_ids.insert(id, fork_id.clone());
2883 self.created_forks.insert(fork_id, idx);
2884
2885 let fork = Fork {
2886 db,
2887 journaled_state,
2888 source_chain_id,
2889 position: ForkPosition::AfterBlock { block },
2890 };
2891 self.forks.push(Some(fork));
2892 idx
2893 }
2894
2895 pub fn roll_fork(
2896 &mut self,
2897 id: LocalForkId,
2898 new_fork_id: ForkId,
2899 block: BlockNumHash,
2900 source_chain_id: ChainId,
2901 backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
2902 ) -> eyre::Result<ForkLookupIndex> {
2903 let fork_id = self.ensure_fork_id(id)?;
2904 let idx = self.ensure_fork_index(fork_id)?;
2905
2906 if let Some(active) = self.forks[idx].as_mut() {
2907 let mut new_db = ForkDB::new(backend);
2909 for addr in self.persistent_accounts.iter().copied() {
2910 merge_db_account_data(addr, &active.db, &mut new_db);
2911 }
2912 active.db = new_db;
2913 active.source_chain_id = source_chain_id;
2914 active.position = ForkPosition::AfterBlock { block };
2915 }
2916 self.issued_local_fork_ids.insert(id, new_fork_id.clone());
2917 self.created_forks.insert(new_fork_id, idx);
2918 Ok(idx)
2919 }
2920
2921 #[cfg(feature = "monad")]
2923 fn stage_fork_roll(
2924 &self,
2925 id: LocalForkId,
2926 new_fork_id: ForkId,
2927 block: BlockNumHash,
2928 source_chain_id: ChainId,
2929 backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
2930 journaled_state: JournaledState,
2931 ) -> eyre::Result<StagedForkRoll<FEN>> {
2932 let fork_id = self.ensure_fork_id(id)?;
2933 let idx = self.ensure_fork_index(fork_id)?;
2934 let current = self.get_fork(idx);
2935
2936 let mut new_db = ForkDB::new(backend);
2939 for addr in self.persistent_accounts.iter().copied() {
2940 merge_db_account_data(addr, ¤t.db, &mut new_db);
2941 }
2942
2943 Ok(StagedForkRoll {
2944 local_id: id,
2945 fork_id: new_fork_id,
2946 fork_index: idx,
2947 fork: Fork {
2948 db: new_db,
2949 journaled_state,
2950 source_chain_id,
2951 position: ForkPosition::AfterBlock { block },
2952 },
2953 })
2954 }
2955
2956 #[cfg(feature = "monad")]
2958 fn publish_fork_roll(&mut self, staged: StagedForkRoll<FEN>) -> ForkLookupIndex {
2959 let StagedForkRoll { local_id, fork_id, fork_index, fork } = staged;
2960 self.set_fork(fork_index, fork);
2961 self.issued_local_fork_ids.insert(local_id, fork_id.clone());
2962 self.created_forks.insert(fork_id, fork_index);
2963 fork_index
2964 }
2965
2966 pub fn insert_new_fork(
2970 &mut self,
2971 fork_id: ForkId,
2972 block: BlockNumHash,
2973 source_chain_id: ChainId,
2974 db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
2975 journaled_state: JournaledState,
2976 ) -> (LocalForkId, ForkLookupIndex) {
2977 self.insert_fork(
2978 fork_id,
2979 Fork {
2980 db,
2981 journaled_state,
2982 source_chain_id,
2983 position: ForkPosition::AfterBlock { block },
2984 },
2985 )
2986 }
2987
2988 fn insert_fork(
2990 &mut self,
2991 fork_id: ForkId,
2992 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
2993 ) -> (LocalForkId, ForkLookupIndex) {
2994 let idx = self.forks.len();
2995 self.created_forks.insert(fork_id.clone(), idx);
2996 let id = self.next_id();
2997 self.issued_local_fork_ids.insert(id, fork_id);
2998 self.forks.push(Some(fork));
2999 (id, idx)
3000 }
3001
3002 fn next_id(&mut self) -> U256 {
3003 let id = self.next_fork_id;
3004 self.next_fork_id += U256::from(1);
3005 id
3006 }
3007
3008 pub fn len(&self) -> usize {
3010 self.issued_local_fork_ids.len()
3011 }
3012
3013 pub fn is_empty(&self) -> bool {
3015 self.issued_local_fork_ids.is_empty()
3016 }
3017
3018 pub fn precompile_addresses(&self) -> AddressSet {
3019 let evm = FEN::EvmFactory::default().create_evm(
3020 EmptyDB::default(),
3021 EvmEnv::new(CfgEnv::new_with_spec(self.spec_id), Default::default()),
3022 );
3023 evm.precompiles().addresses().copied().collect()
3024 }
3025
3026 pub fn new_journaled_state(&self) -> JournaledState {
3028 let mut journal = {
3029 let mut journal_inner = JournalInner::new();
3030 journal_inner.set_spec_id(self.spec_id.into());
3031 journal_inner
3032 };
3033 let precompile_addresses = self.precompile_addresses();
3034 journal.warm_addresses.set_precompile_addresses(&precompile_addresses);
3035 journal
3036 }
3037}
3038
3039impl<FEN: FoundryEvmNetwork> Default for BackendInner<FEN> {
3040 fn default() -> Self {
3041 Self {
3042 launched_with_fork: None,
3043 issued_local_fork_ids: Default::default(),
3044 created_forks: Default::default(),
3045 forks: vec![],
3046 state_snapshots: Default::default(),
3047 has_state_snapshot_failure: false,
3048 caller: None,
3049 next_fork_id: Default::default(),
3050 persistent_accounts: Default::default(),
3051 spec_id: SpecFor::<FEN>::default(),
3052 cheatcode_access_accounts: HashSet::from([
3055 CHEATCODE_ADDRESS,
3056 TEST_CONTRACT_ADDRESS,
3057 CALLER,
3058 ]),
3059 }
3060 }
3061}
3062
3063pub(crate) fn merge_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
3066 accounts: impl IntoIterator<Item = Address>,
3067 active: &CacheDB<ExtDB>,
3068 active_journaled_state: &mut JournaledState,
3069 target_fork: &mut Fork<N, B>,
3070) {
3071 for addr in accounts {
3072 merge_db_account_data(addr, active, &mut target_fork.db);
3073 merge_journaled_state_data(addr, active_journaled_state, &mut target_fork.journaled_state);
3074 }
3075
3076 *active_journaled_state = target_fork.journaled_state.clone();
3077}
3078
3079fn merge_journaled_state_data(
3081 addr: Address,
3082 active_journaled_state: &JournaledState,
3083 fork_journaled_state: &mut JournaledState,
3084) {
3085 if let Some(mut acc) = active_journaled_state.state.get(&addr).cloned() {
3086 trace!(?addr, "updating journaled_state account data");
3087 if let Some(fork_account) = fork_journaled_state.state.get_mut(&addr) {
3088 fork_account.storage.extend(std::mem::take(&mut acc.storage));
3090 std::mem::swap(&mut fork_account.storage, &mut acc.storage);
3092 }
3093 fork_journaled_state.state.insert(addr, acc);
3094 }
3095}
3096
3097fn merge_db_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
3099 addr: Address,
3100 active: &CacheDB<ExtDB>,
3101 fork_db: &mut ForkDB<N, B>,
3102) {
3103 trace!(?addr, "merging database data");
3104
3105 let Some(acc) = active.cache.accounts.get(&addr) else { return };
3106
3107 if let Some(code) = active.cache.contracts.get(&acc.info.code_hash) {
3109 trace!("merging contract cache");
3110 fork_db.cache.contracts.insert(acc.info.code_hash, code.clone());
3111 }
3112
3113 use std::collections::hash_map::Entry;
3115 match fork_db.cache.accounts.entry(addr) {
3116 Entry::Vacant(vacant) => {
3117 trace!("target account not present - inserting from active");
3118 vacant.insert(acc.clone());
3121 }
3122 Entry::Occupied(mut occupied) => {
3123 trace!("target account present - merging storage slots");
3124 let fork_account = occupied.get_mut();
3127 fork_account.storage.extend(&acc.storage);
3128 }
3129 }
3130}
3131
3132fn is_contract_in_state(evm_state: &EvmState, acc: Address) -> bool {
3134 evm_state.get(&acc).map(|acc| acc.info.code_hash != KECCAK_EMPTY).unwrap_or_default()
3135}
3136
3137fn update_env_block<N: Network, SPEC: Into<SpecId> + Copy, BLOCK: FoundryBlock>(
3139 evm_env: &mut EvmEnv<SPEC, BLOCK>,
3140 block: &N::BlockResponse,
3141 source_chain_id: ChainId,
3142 networks: NetworkConfigs,
3143) {
3144 let header = block.header();
3145 let block_env = &mut evm_env.block_env;
3146 block_env.set_timestamp(U256::from(header.timestamp()));
3147 block_env.set_beneficiary(header.beneficiary());
3148 block_env.set_difficulty(header.difficulty());
3149 block_env.set_prevrandao(header.mix_hash());
3150 block_env.set_basefee(header.base_fee_per_gas().unwrap_or_default());
3151 block_env.set_gas_limit(header.gas_limit());
3152 block_env.set_number(U256::from(header.number()));
3153
3154 if let Some(excess_blob_gas) = header.excess_blob_gas() {
3155 evm_env.block_env.set_blob_excess_gas_and_price(
3156 excess_blob_gas,
3157 get_blob_base_fee_update_fraction(evm_env.cfg_env.chain_id, header.timestamp()),
3158 );
3159 }
3160
3161 apply_chain_and_block_specific_env_changes_for_chain::<N, _, _>(
3162 evm_env,
3163 block,
3164 source_chain_id,
3165 networks,
3166 );
3167}
3168
3169fn commit_transaction<FEN: FoundryEvmNetwork>(
3172 transaction: TransactionInputs<FEN>,
3173 journaled_state: &mut JournaledState,
3174 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
3175 fork_id: &ForkId,
3176 networks: NetworkConfigs,
3177 persistent_accounts: &HashSet<Address>,
3178 inspector: &mut dyn for<'db> FoundryInspectorExt<
3179 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
3180 >,
3181) -> eyre::Result<()> {
3182 let TransactionInputs { evm_env, tx_env, chain_context, rpc_block_number } = transaction;
3183 let now = Instant::now();
3184 let res = {
3185 let fork = fork.clone();
3186 let journaled_state = journaled_state.clone();
3187 let depth = journaled_state.depth;
3188 let mut db: Backend<FEN> =
3189 Backend::new_with_fork(fork_id, fork, journaled_state, networks)?;
3190 db.fork_block_number_override = Some(rpc_block_number);
3191
3192 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(
3193 &mut db,
3194 evm_env,
3195 chain_context,
3196 inspector,
3197 );
3198 evm.journal_inner_mut().depth = depth + 1;
3199 evm.transact_raw(tx_env).wrap_err("backend: failed committing transaction")?
3200 };
3201 trace!(elapsed = ?now.elapsed(), "transacted transaction");
3202
3203 apply_state_changeset(res.state, journaled_state, fork, persistent_accounts)?;
3204 Ok(())
3205}
3206
3207pub fn update_state<DB: Database>(
3210 state: &mut EvmState,
3211 db: &mut DB,
3212 persistent_accounts: Option<&HashSet<Address>>,
3213) -> Result<(), DB::Error> {
3214 for (addr, acc) in state.iter_mut() {
3215 if persistent_accounts.is_none_or(|accounts| !accounts.contains(addr)) {
3216 acc.info = db.basic(*addr)?.unwrap_or_default();
3217 for (key, val) in &mut acc.storage {
3218 val.present_value = db.storage(*addr, *key)?;
3219 }
3220 }
3221 }
3222
3223 Ok(())
3224}
3225
3226fn apply_state_changeset<N: Network, B: ForkBlockEnv>(
3229 state: EvmState,
3230 journaled_state: &mut JournaledState,
3231 fork: &mut Fork<N, B>,
3232 persistent_accounts: &HashSet<Address>,
3233) -> Result<(), BackendError> {
3234 let mut staged_db = fork.db.clone();
3237 let mut staged_journaled_state = journaled_state.clone();
3238 let mut staged_fork_journaled_state = fork.journaled_state.clone();
3239 staged_db.commit(state);
3240 update_state(&mut staged_journaled_state.state, &mut staged_db, Some(persistent_accounts))?;
3241 update_state(
3242 &mut staged_fork_journaled_state.state,
3243 &mut staged_db,
3244 Some(persistent_accounts),
3245 )?;
3246
3247 fork.db = staged_db;
3248 *journaled_state = staged_journaled_state;
3249 fork.journaled_state = staged_fork_journaled_state;
3250 Ok(())
3251}
3252
3253#[cfg(test)]
3254mod tests {
3255 use super::{
3256 Fork, ForkAccountField, apply_state_changeset, ensure_block_identity, update_env_block,
3257 };
3258 use crate::{
3259 backend::{Backend, DatabaseExt, ForkPosition},
3260 evm::EthEvmNetwork,
3261 opts::EvmOpts,
3262 };
3263 use alloy_consensus::transaction::Recovered;
3264 use alloy_eips::BlockNumHash;
3265 use alloy_evm::EvmEnv;
3266 use alloy_network::{
3267 AnyHeader, AnyNetwork, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope,
3268 AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction,
3269 };
3270 use alloy_primitives::{Address, B256, Bytes, U256, address, keccak256};
3271 use alloy_provider::{Provider, ProviderBuilder, mock::Asserter};
3272 use alloy_rpc_types::{Block, BlockTransactions, Transaction as RpcTransaction};
3273 use alloy_serde::WithOtherFields;
3274 use anvil::{NodeConfig, spawn};
3275 use foundry_common::{SYSTEM_TRANSACTION_TYPE, provider::get_http_provider};
3276 use foundry_config::{Config, NamedChain};
3277 use foundry_evm_networks::NetworkConfigs;
3278 use foundry_fork_db::{
3279 SharedBackend,
3280 cache::{BlockchainDb, BlockchainDbMeta},
3281 };
3282 use revm::{
3283 context::{BlockEnv, JournalInner, TxEnv},
3284 database::{AccountState, CacheDB, DatabaseRef, DbAccount},
3285 primitives::{KECCAK_EMPTY, hardfork::SpecId},
3286 state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
3287 };
3288 use std::collections::HashSet;
3289
3290 fn fork_with_closed_backend() -> Fork<AnyNetwork, BlockEnv> {
3291 let provider =
3292 ProviderBuilder::<_, _, AnyNetwork>::default().connect_mocked_client(Asserter::new());
3293 let db = BlockchainDb::new(
3294 BlockchainDbMeta::new(BlockEnv::default(), "http://localhost".to_string()),
3295 None,
3296 );
3297 let (backend, handler) = SharedBackend::new(provider, db, None);
3298 drop(handler);
3299 Fork {
3300 db: CacheDB::new(backend),
3301 journaled_state: JournalInner::new(),
3302 source_chain_id: 1,
3303 position: ForkPosition::AfterBlock { block: BlockNumHash::default() },
3304 }
3305 }
3306
3307 fn rpc_block(number: u64, hash: B256, parent_hash: B256) -> AnyRpcBlock {
3308 let header = AnyHeader { number, parent_hash, ..Default::default() };
3309 AnyRpcBlock::new(
3310 Block::new(
3311 AnyRpcHeader::from_sealed(header.seal(hash)),
3312 BlockTransactions::Full(Vec::new()),
3313 )
3314 .into(),
3315 )
3316 }
3317
3318 #[test]
3319 fn validates_block_identity() {
3320 let hash = B256::with_last_byte(2);
3321 let block = rpc_block(2, hash, B256::with_last_byte(1));
3322 assert!(ensure_block_identity(&block, BlockNumHash::new(2, hash), "parent").is_ok());
3323
3324 let err =
3325 ensure_block_identity(&block, BlockNumHash::new(2, B256::with_last_byte(3)), "parent")
3326 .unwrap_err();
3327 assert!(err.to_string().contains("parent block changed"));
3328
3329 let err =
3330 ensure_block_identity(&block, BlockNumHash::new(1, hash), "grandparent").unwrap_err();
3331 assert!(err.to_string().contains("grandparent block changed"));
3332 }
3333
3334 #[test]
3335 fn failed_fork_state_refresh_does_not_publish_transaction_changes() {
3336 let mut fork = fork_with_closed_backend();
3337 let externally_loaded = Address::with_last_byte(1);
3338 let fork_loaded = Address::with_last_byte(2);
3339 let committed = Address::with_last_byte(3);
3340 let missing_slot = U256::from(1);
3341
3342 let cached_external = AccountInfo { balance: U256::from(11), ..Default::default() };
3343 let cached_fork = AccountInfo { balance: U256::from(12), ..Default::default() };
3344 fork.db.insert_account_info(externally_loaded, cached_external);
3345 fork.db.insert_account_info(fork_loaded, cached_fork);
3346
3347 let mut journaled_state = JournalInner::new();
3348 let external_account = Account::default()
3349 .with_info(AccountInfo { balance: U256::from(1), ..Default::default() });
3350 journaled_state.state.insert(externally_loaded, external_account);
3351
3352 let mut fork_account = Account::default()
3353 .with_info(AccountInfo { balance: U256::from(2), ..Default::default() });
3354 fork_account
3355 .storage
3356 .insert(missing_slot, EvmStorageSlot::new(U256::ZERO, TransactionId::ZERO));
3357 fork.journaled_state.state.insert(fork_loaded, fork_account);
3358
3359 let mut committed_account = Account::default()
3360 .with_info(AccountInfo { balance: U256::from(13), ..Default::default() });
3361 committed_account.mark_touch();
3362 let mut state = EvmState::default();
3363 state.insert(committed, committed_account);
3364
3365 let result = apply_state_changeset(state, &mut journaled_state, &mut fork, &HashSet::new());
3366 assert!(result.is_err());
3367 assert!(!fork.db.cache.accounts.contains_key(&committed));
3368 assert_eq!(journaled_state.state[&externally_loaded].info.balance, U256::from(1));
3369 assert_eq!(fork.journaled_state.state[&fork_loaded].info.balance, U256::from(2));
3370 }
3371
3372 #[test]
3373 fn failed_fork_state_refresh_preserves_not_existing_account() {
3374 let mut fork = fork_with_closed_backend();
3375 let address = Address::with_last_byte(1);
3376 let missing_slot = U256::from(1);
3377 fork.db.cache.accounts.insert(address, DbAccount::new_not_existing());
3378
3379 let mut journaled_state = JournalInner::new();
3380 let mut journaled_account = Account::default()
3381 .with_info(AccountInfo { balance: U256::from(1), ..Default::default() });
3382 journaled_account
3383 .storage
3384 .insert(missing_slot, EvmStorageSlot::new(U256::from(7), TransactionId::ZERO));
3385 journaled_state.state.insert(address, journaled_account);
3386
3387 let mut touched_account = Account::default()
3388 .with_info(AccountInfo { balance: U256::from(13), ..Default::default() });
3389 touched_account.mark_touch();
3390 let mut state = EvmState::default();
3391 state.insert(address, touched_account);
3392
3393 let result = apply_state_changeset(state, &mut journaled_state, &mut fork, &HashSet::new());
3394 assert!(result.is_err());
3395 assert_eq!(fork.db.cache.accounts[&address].account_state, AccountState::NotExisting);
3396 assert_eq!(journaled_state.state[&address].info.balance, U256::from(1));
3397 assert_eq!(
3398 journaled_state.state[&address].storage[&missing_slot].present_value(),
3399 U256::from(7)
3400 );
3401 }
3402
3403 #[tokio::test(flavor = "multi_thread")]
3404 async fn refresh_fork_account_updates_loaded_journals() {
3405 let (api, handle) = spawn(NodeConfig::test()).await;
3406 let target = address!("0x0000000000000000000000000000000000001331");
3407 let code =
3408 Bytes::from_static(&[0x60, 0x2a, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3]);
3409
3410 let provider = handle.http_provider();
3411 let block_number = provider.get_block_number().await.unwrap();
3412 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
3413 evm_opts.fork_url = Some(handle.http_endpoint());
3414 evm_opts.fork_block_number = Some(block_number);
3415 let fork = evm_opts.get_fork(&Config::default(), 31_337, Some(block_number)).unwrap();
3416 let mut backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
3417
3418 let mut journaled_state = JournalInner::new();
3419 journaled_state.load_account(&mut backend, target).unwrap();
3420 journaled_state.state.get_mut(&target).unwrap().info.balance = U256::from(1);
3421 let fork = backend.active_fork_mut().unwrap();
3422 fork.journaled_state.load_account(&mut fork.db, target).unwrap();
3423 fork.journaled_state.state.get_mut(&target).unwrap().info.balance = U256::from(2);
3424 let cached = fork.db.cache.accounts.get_mut(&target).unwrap();
3425 cached.info.balance = U256::from(3);
3426 cached.account_state = AccountState::Touched;
3427 assert_eq!(journaled_state.state[&target].info.code_hash, KECCAK_EMPTY);
3428 assert_eq!(fork.journaled_state.state[&target].info.code_hash, KECCAK_EMPTY);
3429
3430 api.anvil_set_code(target, code.clone()).await.unwrap();
3431 backend.refresh_fork_account(target, ForkAccountField::Code, &mut journaled_state).unwrap();
3432
3433 let expected_hash = keccak256(&code);
3434 let refreshed = &journaled_state.state[&target].info;
3435 assert_eq!(refreshed.code_hash, expected_hash);
3436 assert_eq!(refreshed.code.as_ref().unwrap().original_bytes(), code);
3437 assert_eq!(refreshed.balance, U256::from(1));
3438 let refreshed = &backend.active_fork().unwrap().journaled_state.state[&target].info;
3439 assert_eq!(refreshed.code_hash, expected_hash);
3440 assert_eq!(refreshed.code.as_ref().unwrap().original_bytes(), code);
3441 assert_eq!(refreshed.balance, U256::from(2));
3442 let cached = &backend.active_fork().unwrap().db.cache.accounts[&target];
3443 assert_eq!(cached.info.code_hash, expected_hash);
3444 assert_eq!(cached.info.balance, U256::from(3));
3445 }
3446
3447 #[test]
3448 fn ethereum_replay_skips_unknown_system_envelopes_before_conversion() {
3449 let transaction = |ty| {
3450 let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
3451 hash: B256::ZERO,
3452 inner: UnknownTypedTransaction {
3453 ty: AnyTxType(ty),
3454 fields: Default::default(),
3455 memo: Default::default(),
3456 },
3457 });
3458 AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
3459 inner: Recovered::new_unchecked(unknown, Address::with_last_byte(0x42)),
3460 block_hash: None,
3461 block_number: None,
3462 transaction_index: None,
3463 effective_gas_price: None,
3464 block_timestamp: None,
3465 }))
3466 };
3467
3468 assert!(
3469 Backend::<EthEvmNetwork>::replay_tx_env(&transaction(SYSTEM_TRANSACTION_TYPE))
3470 .unwrap()
3471 .is_none()
3472 );
3473 assert!(Backend::<EthEvmNetwork>::replay_tx_env(&transaction(0xff)).is_err());
3474 }
3475
3476 #[test]
3477 fn fork_position_advances_from_exact_transaction_predecessor() {
3478 let parent_block = BlockNumHash::new(10, B256::with_last_byte(10));
3479 let block = BlockNumHash::new(11, B256::with_last_byte(11));
3480 let parent = ForkPosition::AfterBlock { block: parent_block };
3481 assert_eq!(
3482 parent.after_transaction(block, parent_block.hash, 0, 2),
3483 Some(ForkPosition::BeforeTransaction { block, transaction_index: 1 })
3484 );
3485 assert_eq!(
3486 parent.after_transaction(block, parent_block.hash, 0, 1),
3487 Some(ForkPosition::AfterBlock { block })
3488 );
3489
3490 let before_first = ForkPosition::BeforeTransaction { block, transaction_index: 0 };
3491 assert_eq!(
3492 before_first.after_transaction(block, parent_block.hash, 0, 2),
3493 Some(ForkPosition::BeforeTransaction { block, transaction_index: 1 })
3494 );
3495
3496 let before_second = ForkPosition::BeforeTransaction { block, transaction_index: 1 };
3497 assert_eq!(
3498 before_second.after_transaction(block, parent_block.hash, 1, 3),
3499 Some(ForkPosition::BeforeTransaction { block, transaction_index: 2 })
3500 );
3501 assert_eq!(
3502 before_second.after_transaction(block, parent_block.hash, 1, 2),
3503 Some(ForkPosition::AfterBlock { block })
3504 );
3505
3506 assert_eq!(parent.after_transaction(block, B256::ZERO, 0, 1), None);
3507 assert_eq!(parent.after_transaction(block, parent_block.hash, 1, 2), None);
3508 assert_eq!(before_second.after_transaction(block, parent_block.hash, 0, 3), None);
3509 assert_eq!(before_second.after_transaction(block, parent_block.hash, 2, 3), None);
3510 assert_eq!(before_second.after_transaction(block, parent_block.hash, 1, 1), None);
3511 assert_eq!(parent.after_transaction(block, parent_block.hash, 0, 0), None);
3512 }
3513
3514 #[test]
3515 fn fork_replay_block_env_preserves_arbitrum_l1_number() {
3516 let header = AnyHeader { number: 75_219_831, ..Default::default() };
3517 let mut block = AnyRpcBlock::new(
3518 Block::new(
3519 AnyRpcHeader::from_sealed(header.seal(B256::ZERO)),
3520 BlockTransactions::Full(Vec::new()),
3521 )
3522 .into(),
3523 );
3524 block.other.insert("l1BlockNumber".to_string(), serde_json::json!("0x10276d3"));
3525 let mut evm_env =
3526 EvmEnv::new(revm::context::CfgEnv::<SpecId>::default(), BlockEnv::default());
3527
3528 update_env_block::<AnyNetwork, _, _>(
3529 &mut evm_env,
3530 &block,
3531 NamedChain::Arbitrum as u64,
3532 NetworkConfigs::default(),
3533 );
3534
3535 assert_eq!(evm_env.block_env.number, U256::from(16_938_707));
3536 }
3537
3538 #[tokio::test(flavor = "multi_thread")]
3539 async fn temporary_backend_preserves_fork_position() {
3540 let (_api, handle) = spawn(NodeConfig::test()).await;
3541 let provider = handle.http_provider();
3542 let block_number = provider.get_block_number().await.unwrap();
3543
3544 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
3545 evm_opts.fork_url = Some(handle.http_endpoint());
3546 evm_opts.fork_block_number = Some(block_number);
3547 let fork = evm_opts.get_fork(&Config::default(), 31_337, Some(block_number)).unwrap();
3548 let mut backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
3549 let id = backend.active_fork_ids.unwrap().0;
3550 let fork_id = backend.inner.ensure_fork_id(id).unwrap().clone();
3551
3552 for position in [
3553 ForkPosition::BeforeTransaction {
3554 block: BlockNumHash::new(block_number + 1, B256::with_last_byte(1)),
3555 transaction_index: 2,
3556 },
3557 ForkPosition::AfterBlock {
3558 block: BlockNumHash::new(block_number + 2, B256::with_last_byte(2)),
3559 },
3560 ] {
3561 backend.inner.get_fork_by_id_mut(id).unwrap().position = position;
3562 let fork = backend.active_fork().unwrap().clone();
3563 let journaled_state = fork.journaled_state.clone();
3564 let mut temporary = Backend::<EthEvmNetwork>::new_with_fork(
3565 &fork_id,
3566 fork,
3567 journaled_state,
3568 NetworkConfigs::default(),
3569 )
3570 .unwrap();
3571
3572 assert_eq!(temporary.active_fork().unwrap().position, position);
3573 let expected = match position {
3574 ForkPosition::AfterBlock { block }
3575 | ForkPosition::BeforeTransaction { block, .. } => block.number,
3576 };
3577 assert_eq!(temporary.active_fork_block_number(), Some(expected));
3578 temporary.fork_block_number_override = Some(expected + 1);
3579 assert_eq!(temporary.active_fork_block_number(), Some(expected + 1));
3580 }
3581 }
3582
3583 #[tokio::test(flavor = "multi_thread")]
3584 #[cfg(feature = "monad")]
3585 async fn fork_factory_boundary_preserves_explicit_execution_overrides() {
3586 async fn pinned_opts(endpoint: String, networks: Option<NetworkConfigs>) -> EvmOpts {
3587 let mut opts = EvmOpts { fork_url: Some(endpoint), ..Default::default() };
3588 if let Some(networks) = networks {
3589 opts.networks = networks;
3590 }
3591 opts.infer_network_from_fork().await.unwrap();
3592 let identity = opts.fork_endpoint.clone().unwrap();
3593 let network_is_inferred = opts.fork_network_is_inferred;
3594 opts.expect_fork_endpoint(identity, network_is_inferred);
3595 opts.pin_fork_block().await.unwrap();
3596 opts
3597 }
3598
3599 fn target_fork(opts: EvmOpts, url: String) -> crate::fork::CreateFork {
3600 crate::fork::CreateFork { url, enable_caching: false, evm_opts: opts, resolved: None }
3601 }
3602
3603 let (ethereum_base_api, ethereum_base) = spawn(NodeConfig::test()).await;
3604 let (_ethereum_target_api, ethereum_target) = spawn(NodeConfig::test()).await;
3605 let (monad_base_api, monad_base) = spawn(NodeConfig::test_monad()).await;
3606 let (_monad_target_api, monad_target) = spawn(NodeConfig::test_monad()).await;
3607 ethereum_base_api.mine_one().await.unwrap();
3608 monad_base_api.mine_one().await.unwrap();
3609
3610 let inferred_ethereum = pinned_opts(ethereum_base.http_endpoint(), None).await;
3611 assert!(inferred_ethereum.fork_network_is_inferred);
3612 let error = Backend::<EthEvmNetwork>::spawn(Some(target_fork(
3613 inferred_ethereum,
3614 monad_target.http_endpoint(),
3615 )))
3616 .unwrap_err();
3617 assert!(
3618 error
3619 .to_string()
3620 .contains("cannot create a `monad` fork with an EVM instantiated for `ethereum`"),
3621 "{error}"
3622 );
3623
3624 let inferred_monad = pinned_opts(monad_base.http_endpoint(), None).await;
3625 assert!(inferred_monad.fork_network_is_inferred);
3626 let error = Backend::<crate::evm::MonadEvmNetwork>::spawn(Some(target_fork(
3627 inferred_monad,
3628 ethereum_target.http_endpoint(),
3629 )))
3630 .unwrap_err();
3631 assert!(
3632 error
3633 .to_string()
3634 .contains("cannot create a `ethereum` fork with an EVM instantiated for `monad`"),
3635 "{error}"
3636 );
3637
3638 let explicit_ethereum =
3639 pinned_opts(ethereum_base.http_endpoint(), Some(NetworkConfigs::with_ethereum())).await;
3640 assert!(!explicit_ethereum.fork_network_is_inferred);
3641 let _backend = Backend::<EthEvmNetwork>::spawn(Some(target_fork(
3642 explicit_ethereum,
3643 monad_target.http_endpoint(),
3644 )))
3645 .unwrap();
3646
3647 let explicit_monad =
3648 pinned_opts(monad_base.http_endpoint(), Some(NetworkConfigs::with_monad())).await;
3649 assert!(!explicit_monad.fork_network_is_inferred);
3650 let _backend = Backend::<crate::evm::MonadEvmNetwork>::spawn(Some(target_fork(
3651 explicit_monad,
3652 ethereum_target.http_endpoint(),
3653 )))
3654 .unwrap();
3655 }
3656
3657 #[tokio::test(flavor = "multi_thread")]
3658 async fn can_read_write_cache() {
3659 let endpoint = &*foundry_test_utils::rpc::next_http_rpc_endpoint();
3660 let provider = get_http_provider(endpoint);
3661
3662 let block_num = provider.get_block_number().await.unwrap();
3663
3664 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
3665 evm_opts.fork_url = Some(endpoint.to_string());
3666 evm_opts.fork_block_number = Some(block_num);
3667
3668 let (evm_env, _, resolved) =
3669 evm_opts.env_resolved::<SpecId, BlockEnv, TxEnv>().await.unwrap();
3670
3671 let fork = evm_opts
3672 .get_fork_resolved(&Config::default(), evm_env.cfg_env.chain_id, resolved.as_ref())
3673 .unwrap();
3674
3675 let resolved = resolved.unwrap();
3676 let fork_hash = resolved.hash();
3677 let source_id = resolved.source_id();
3678 let backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
3679
3680 let address = address!("0x63091244180ae240c87d1f528f5f269134cb07b3");
3682
3683 let num_slots = 5;
3684 let _account = backend.basic_ref(address);
3685 for idx in 0..num_slots {
3686 let _ = backend.storage_ref(address, U256::from(idx));
3687 }
3688 drop(backend);
3689
3690 let meta = BlockchainDbMeta::new(evm_env.block_env, endpoint.to_string())
3691 .with_fork_identity(fork_hash, source_id);
3692
3693 let db = BlockchainDb::new(
3694 meta,
3695 Some(Config::foundry_block_cache_dir(NamedChain::Mainnet, block_num).unwrap()),
3696 );
3697 assert!(db.accounts().read().contains_key(&address));
3698 assert!(db.storage().read().contains_key(&address));
3699 assert_eq!(db.storage().read().get(&address).unwrap().len(), num_slots as usize);
3700 }
3701}