1use crate::{
4 FoundryBlock, FoundryChain, FoundryInspectorExt, FoundryTransaction, FromAnyRpcTransaction,
5 constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS},
6 evm::{
7 BlockEnvFor, ChainFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, FoundryEvmFactory,
8 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,
15 },
16};
17use alloy_chains::Chain;
18use alloy_consensus::{BlockHeader, Typed2718};
19use alloy_eips::BlockNumHash;
20use alloy_evm::{Evm, EvmEnv, EvmFactory, precompiles::PrecompilesMap};
21use alloy_genesis::GenesisAccount;
22use alloy_network::{
23 AnyNetwork, AnyRpcBlock, AnyRpcTransaction, BlockResponse, Network, TransactionResponse,
24};
25use alloy_primitives::{Address, B256, ChainId, TxKind, U256, keccak256, map::AddressSet, uint};
26use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions};
27use eyre::Context;
28use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender};
29use foundry_evm_networks::{NetworkConfigs, apply_bsc_p256_precompile};
30pub use foundry_fork_db::{
31 AccountFetchPolicy, BlockchainDb, ForkBlock, ForkBlockEnv, SharedBackend,
32 cache::BlockchainDbMeta,
33};
34use revm::{
35 Database, DatabaseCommit, JournalEntry,
36 bytecode::Bytecode,
37 context::{Block, BlockEnv, CfgEnv, ContextTr, JournalInner, Transaction},
38 context_interface::{journaled_state::account::JournaledAccountTr, result::ResultAndState},
39 database::{AccountState, CacheDB, DatabaseRef, EmptyDB},
40 database_interface::bal::BalState,
41 primitives::{AddressMap, HashMap as Map, KECCAK_EMPTY, Log, hardfork::SpecId},
42 state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
43};
44use std::{
45 collections::{BTreeMap, HashMap},
46 fmt::Debug,
47 time::Instant,
48};
49
50#[cfg(feature = "monad")]
51use crate::evm::monad::BlockContext;
52
53mod diagnostic;
54pub use diagnostic::RevertDiagnostic;
55
56mod bal;
57
58mod error;
59pub use error::{BackendError, BackendResult, DatabaseError, DatabaseResult};
60
61mod cow;
62pub use cow::CowBackend;
63
64mod in_memory_db;
65pub use in_memory_db::{EmptyDBWrapper, FoundryEvmInMemoryDB, MemDb};
66
67mod snapshot;
68pub use snapshot::{BackendStateSnapshot, RevertStateSnapshotAction, StateSnapshot};
69
70type ForkDB<N, B> = CacheDB<SharedBackend<N, B>>;
72
73pub type LocalForkId = U256;
78
79#[cfg(feature = "monad")]
81pub enum ContextUpdate<C> {
82 Unchanged,
84 Replace(C),
86 Rebase,
88}
89
90#[cfg(feature = "monad")]
95pub type ContextUpdateFor<F> = ContextUpdate<<F as FoundryEvmFactory>::Chain>;
96#[cfg(not(feature = "monad"))]
97pub type ContextUpdateFor<F> = std::marker::PhantomData<F>;
98
99type ForkLookupIndex = usize;
102
103struct TransactionInputs<FEN: FoundryEvmNetwork> {
105 evm_env: EvmEnvFor<FEN>,
106 tx_env: TxEnvFor<FEN>,
107 chain_context: ChainFor<FEN>,
108 rpc_block_number: u64,
109}
110
111struct ReplayInputs<FEN: FoundryEvmNetwork> {
113 fork_id: ForkId,
114 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
115 evm_env: EvmEnvFor<FEN>,
116 networks: NetworkConfigs,
117}
118
119struct TransactionForkTarget {
121 fork_block: BlockNumHash,
122 transaction: AnyRpcTransaction,
123 block: AnyRpcBlock,
124 mined: bool,
125 position: Option<TransactionPosition>,
126}
127
128#[derive(Clone, Copy)]
130struct TransactionPosition {
131 index: usize,
132 #[cfg(feature = "monad")]
133 count: usize,
134}
135
136#[cfg(feature = "monad")]
138struct StagedForkRoll<FEN: FoundryEvmNetwork> {
139 local_id: LocalForkId,
140 fork_id: ForkId,
141 fork_index: ForkLookupIndex,
142 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
143}
144
145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
147enum ForkPosition {
148 AfterBlock { block: BlockNumHash },
150 BeforeTransaction { block: BlockNumHash, transaction_index: usize },
152}
153
154impl ForkPosition {
155 fn after_transaction(
158 self,
159 block: BlockNumHash,
160 parent_hash: B256,
161 transaction_index: usize,
162 transaction_count: usize,
163 ) -> Option<Self> {
164 let is_next = match self {
165 Self::AfterBlock { block: previous } => {
166 transaction_index == 0
167 && previous.number.checked_add(1) == Some(block.number)
168 && previous.hash == parent_hash
169 }
170 Self::BeforeTransaction { block: current, transaction_index: current_index } => {
171 current == block && current_index == transaction_index
172 }
173 };
174 if !is_next {
175 return None;
176 }
177 let next_index = transaction_index.checked_add(1)?;
178 if next_index > transaction_count {
179 return None;
180 }
181
182 Some(if next_index == transaction_count {
183 Self::AfterBlock { block }
184 } else {
185 Self::BeforeTransaction { block, transaction_index: next_index }
186 })
187 }
188}
189
190const DEFAULT_PERSISTENT_ACCOUNTS: [Address; 3] =
192 [CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, CALLER];
193
194pub const GLOBAL_FAIL_SLOT: U256 =
199 uint!(0x6661696c65640000000000000000000000000000000000000000000000000000_U256);
200
201pub type JournaledState = JournalInner<JournalEntry>;
202
203#[derive(Clone, Copy, Debug)]
205pub enum ForkAccountField {
206 Balance,
207 Nonce,
208 Code,
209}
210
211impl ForkAccountField {
212 fn update(self, target: &mut AccountInfo, refreshed: &AccountInfo) {
213 match self {
214 Self::Balance => target.balance = refreshed.balance,
215 Self::Nonce => target.nonce = refreshed.nonce,
216 Self::Code => {
217 target.code_hash = refreshed.code_hash;
218 target.code = refreshed.code.clone();
219 }
220 }
221 }
222}
223
224#[auto_impl::auto_impl(&mut)]
226pub trait DatabaseExt<F: FoundryEvmFactory>:
227 Database<Error = DatabaseError> + DatabaseCommit + Debug
228{
229 fn snapshot_state(
235 &mut self,
236 journaled_state: &JournaledState,
237 evm_env: &EvmEnv<F::Spec, F::BlockEnv>,
238 ) -> U256;
239
240 fn revert_state(
253 &mut self,
254 id: U256,
255 journaled_state: &JournaledState,
256 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
257 caller: Address,
258 action: RevertStateSnapshotAction,
259 ) -> Option<JournaledState>;
260
261 fn delete_state_snapshot(&mut self, id: U256) -> bool;
266
267 fn delete_state_snapshots(&mut self);
269
270 fn create_select_fork(
274 &mut self,
275 fork: CreateFork,
276 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
277 tx_env: &mut F::Tx,
278 journaled_state: &mut JournaledState,
279 ) -> eyre::Result<(LocalForkId, ContextUpdateFor<F>)> {
280 let id = self.create_fork(fork)?;
281 let context = self.select_fork(id, evm_env, tx_env, journaled_state)?;
282 Ok((id, context))
283 }
284
285 fn create_select_fork_at_transaction(
289 &mut self,
290 fork: CreateFork,
291 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
292 tx_env: &mut F::Tx,
293 journaled_state: &mut JournaledState,
294 transaction: B256,
295 ) -> eyre::Result<(LocalForkId, ContextUpdateFor<F>)> {
296 let id = self.create_fork_at_transaction(fork, transaction)?;
297 let context = self.select_fork(id, evm_env, tx_env, journaled_state)?;
298 Ok((id, context))
299 }
300
301 fn create_fork(&mut self, fork: CreateFork) -> eyre::Result<LocalForkId>;
303
304 fn create_fork_at_transaction(
306 &mut self,
307 fork: CreateFork,
308 transaction: B256,
309 ) -> eyre::Result<LocalForkId>;
310
311 fn select_fork(
321 &mut self,
322 id: LocalForkId,
323 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
324 tx_env: &mut F::Tx,
325 journaled_state: &mut JournaledState,
326 ) -> eyre::Result<ContextUpdateFor<F>>;
327
328 fn roll_fork(
336 &mut self,
337 id: Option<LocalForkId>,
338 block_number: u64,
339 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
340 tx_env: &F::Tx,
341 journaled_state: &mut JournaledState,
342 ) -> eyre::Result<ContextUpdateFor<F>>;
343
344 fn roll_fork_to_transaction(
353 &mut self,
354 id: Option<LocalForkId>,
355 transaction: B256,
356 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
357 tx_env: &F::Tx,
358 journaled_state: &mut JournaledState,
359 ) -> eyre::Result<ContextUpdateFor<F>>;
360
361 fn transact(
363 &mut self,
364 id: Option<LocalForkId>,
365 transaction: B256,
366 evm_env: EvmEnv<F::Spec, F::BlockEnv>,
367 outer_tx_env: &F::Tx,
368 journaled_state: &mut JournaledState,
369 inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
370 ) -> eyre::Result<ContextUpdateFor<F>>;
371
372 fn transact_from_tx(
374 &mut self,
375 tx_env: F::Tx,
376 evm_env: EvmEnv<F::Spec, F::BlockEnv>,
377 journaled_state: &mut JournaledState,
378 inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
379 ) -> eyre::Result<()>;
380
381 fn chain_context_for_synthetic_transaction(&self, tx: &F::Tx) -> eyre::Result<F::Chain> {
383 Ok(F::Chain::for_transaction(tx))
384 }
385
386 fn active_fork_id(&self) -> Option<LocalForkId>;
388
389 fn active_fork_url(&self) -> Option<String>;
391
392 fn active_fork_source_chain_id(&self) -> Option<u64>;
394
395 fn active_fork_block_number(&self) -> Option<u64> {
397 None
398 }
399
400 fn is_forked_mode(&self) -> bool {
402 self.active_fork_id().is_some()
403 }
404
405 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId>;
416
417 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId>;
419
420 fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic>;
447
448 fn load_allocs(
452 &mut self,
453 allocs: &BTreeMap<Address, GenesisAccount>,
454 journaled_state: &mut JournaledState,
455 ) -> Result<(), BackendError>;
456
457 fn clone_account(
462 &mut self,
463 source: &GenesisAccount,
464 target: &Address,
465 journaled_state: &mut JournaledState,
466 ) -> Result<(), BackendError>;
467
468 fn is_persistent(&self, acc: &Address) -> bool;
470
471 fn refresh_fork_account(
474 &mut self,
475 address: Address,
476 field: ForkAccountField,
477 journaled_state: &mut JournaledState,
478 ) -> Result<(), BackendError>;
479
480 fn refresh_fork_storage(
482 &mut self,
483 address: Address,
484 slot: U256,
485 journaled_state: &mut JournaledState,
486 ) -> Result<(), BackendError>;
487
488 fn remove_persistent_account(&mut self, account: &Address) -> bool;
490
491 fn add_persistent_account(&mut self, account: Address) -> bool;
493
494 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
496 fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
497 where
498 Self: Sized,
499 {
500 for acc in accounts {
501 self.remove_persistent_account(&acc);
502 }
503 }
504
505 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
507 fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
508 where
509 Self: Sized,
510 {
511 for acc in accounts {
512 self.add_persistent_account(acc);
513 }
514 }
515
516 fn allow_cheatcode_access(&mut self, account: Address) -> bool;
520
521 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool;
525
526 fn has_cheatcode_access(&self, account: &Address) -> bool;
528
529 fn ensure_cheatcode_access(&self, account: &Address) -> Result<(), BackendError> {
533 if !self.has_cheatcode_access(account) {
534 return Err(BackendError::NoCheats(*account));
535 }
536 Ok(())
537 }
538
539 fn ensure_cheatcode_access_forking_mode(&self, account: &Address) -> Result<(), BackendError> {
542 if self.is_forked_mode() {
543 return self.ensure_cheatcode_access(account);
544 }
545 Ok(())
546 }
547
548 fn set_blockhash(&mut self, block_number: U256, block_hash: B256);
563}
564
565#[must_use]
618pub struct Backend<FEN: FoundryEvmNetwork = EthEvmNetwork> {
619 networks: NetworkConfigs,
624 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
626 mem_db: FoundryEvmInMemoryDB,
628 fork_init_journaled_state: JournaledState,
645 active_fork_ids: Option<(LocalForkId, ForkLookupIndex)>,
649 fork_block_number_override: Option<u64>,
651 bal: Option<BalState>,
653 inner: BackendInner<FEN>,
655}
656
657impl<FEN: FoundryEvmNetwork> Clone for Backend<FEN> {
658 fn clone(&self) -> Self {
659 Self {
660 networks: self.networks,
661 forks: self.forks.clone(),
662 mem_db: self.mem_db.clone(),
663 fork_init_journaled_state: self.fork_init_journaled_state.clone(),
664 active_fork_ids: self.active_fork_ids,
665 fork_block_number_override: self.fork_block_number_override,
666 bal: self.bal.clone(),
667 inner: self.inner.clone(),
668 }
669 }
670}
671
672impl<FEN: FoundryEvmNetwork> Debug for Backend<FEN> {
673 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
674 f.debug_struct("Backend")
675 .field("networks", &self.networks)
676 .field("forks", &self.forks)
677 .field("mem_db", &self.mem_db)
678 .field("fork_init_journaled_state", &self.fork_init_journaled_state)
679 .field("active_fork_ids", &self.active_fork_ids)
680 .field("bal", &self.bal)
681 .field("inner", &self.inner)
682 .finish()
683 }
684}
685
686impl<FEN: FoundryEvmNetwork> Backend<FEN> {
687 pub fn spawn(fork: Option<CreateFork>) -> eyre::Result<Self> {
692 Self::new(MultiFork::<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>::spawn(), fork)
693 }
694
695 pub fn new(
702 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
703 fork: Option<CreateFork>,
704 ) -> eyre::Result<Self> {
705 trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend");
706 let persistent_accounts = AddressSet::from_iter(DEFAULT_PERSISTENT_ACCOUNTS);
708 let inner = BackendInner { persistent_accounts, ..Default::default() };
709
710 let mut backend = Self {
711 networks: NetworkConfigs::default(),
712 forks,
713 mem_db: CacheDB::new(Default::default()),
714 fork_init_journaled_state: inner.new_journaled_state(),
715 active_fork_ids: None,
716 fork_block_number_override: None,
717 bal: None,
718 inner,
719 };
720
721 if let Some(fork) = fork {
722 let ForkResult { id: fork_id, backend: fork, resolved, .. } =
723 backend.forks.create_fork(fork)?;
724 let context = resolved.context();
725 let block = resolved.block();
726 let fork_db = ForkDB::new(fork);
727 let fork_ids = backend.inner.insert_new_fork(
728 fork_id.clone(),
729 block,
730 context.source_chain_id,
731 fork_db,
732 backend.inner.new_journaled_state(),
733 );
734 backend.inner.launched_with_fork = Some((fork_id, fork_ids.0, fork_ids.1));
735 backend.active_fork_ids = Some(fork_ids);
736 }
737
738 trace!(target: "backend", forking_mode=? backend.active_fork_ids.is_some(), "created executor backend");
739
740 Ok(backend)
741 }
742
743 pub(crate) fn new_with_fork(
746 id: &ForkId,
747 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
748 journaled_state: JournaledState,
749 networks: NetworkConfigs,
750 ) -> eyre::Result<Self> {
751 Self::new_with_fork_manager(
752 MultiFork::<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>::spawn(),
753 id,
754 fork,
755 journaled_state,
756 networks,
757 )
758 }
759
760 fn new_with_fork_manager(
761 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
762 id: &ForkId,
763 mut fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
764 journaled_state: JournaledState,
765 networks: NetworkConfigs,
766 ) -> eyre::Result<Self> {
767 let mut backend = Self::new(forks, None)?;
768 backend.networks = networks;
769 fork.journaled_state = journaled_state;
770 let fork_ids = backend.inner.insert_fork(id.clone(), fork);
771 backend.inner.launched_with_fork = Some((id.clone(), fork_ids.0, fork_ids.1));
772 backend.active_fork_ids = Some(fork_ids);
773 Ok(backend)
774 }
775
776 pub fn clone_empty(&self) -> Self {
778 Self {
779 networks: self.networks,
780 forks: self.forks.clone(),
781 mem_db: CacheDB::new(Default::default()),
782 fork_init_journaled_state: self.inner.new_journaled_state(),
783 active_fork_ids: None,
784 fork_block_number_override: None,
785 bal: None,
786 inner: Default::default(),
787 }
788 }
789
790 pub const fn networks(&self) -> NetworkConfigs {
792 self.networks
793 }
794
795 pub const fn set_networks(&mut self, networks: NetworkConfigs) {
797 self.networks = networks;
798 }
799
800 pub fn insert_account_info(&mut self, address: Address, account: AccountInfo) {
801 if let Some(db) = self.active_fork_db_mut() {
802 db.insert_account_info(address, account)
803 } else {
804 self.mem_db.insert_account_info(address, account)
805 }
806 }
807
808 pub fn insert_account_storage(
810 &mut self,
811 address: Address,
812 slot: U256,
813 value: U256,
814 ) -> Result<(), DatabaseError> {
815 if let Some(db) = self.active_fork_db_mut() {
816 db.insert_account_storage(address, slot, value)
817 } else {
818 self.mem_db.insert_account_storage(address, slot, value)
819 }
820 }
821
822 pub fn replace_account_storage(
827 &mut self,
828 address: Address,
829 storage: Map<U256, U256>,
830 ) -> Result<(), DatabaseError> {
831 if let Some(db) = self.active_fork_db_mut() {
832 db.replace_account_storage(address, storage.into_iter().collect())
833 } else {
834 self.mem_db.replace_account_storage(address, storage.into_iter().collect())
835 }
836 }
837
838 #[allow(clippy::type_complexity)]
840 pub const fn state_snapshots(
841 &self,
842 ) -> &StateSnapshots<
843 BackendStateSnapshot<
844 BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
845 SpecFor<FEN>,
846 BlockEnvFor<FEN>,
847 >,
848 > {
849 &self.inner.state_snapshots
850 }
851
852 pub fn set_test_contract(&mut self, acc: Address) -> &mut Self {
859 trace!(?acc, "setting test account");
860 self.inner.persistent_accounts.insert(acc);
861 self.inner.cheatcode_access_accounts.insert(acc);
862 self
863 }
864
865 pub fn set_caller(&mut self, acc: Address) -> &mut Self {
867 trace!(?acc, "setting caller account");
868 self.inner.caller = Some(acc);
869 self.inner.cheatcode_access_accounts.insert(acc);
870 self
871 }
872
873 pub fn set_spec_id(&mut self, spec_id: impl Into<SpecFor<FEN>>) -> &mut Self {
875 self.inner.spec_id = spec_id.into();
876 self
877 }
878
879 pub const fn caller_address(&self) -> Option<Address> {
881 self.inner.caller
882 }
883
884 pub const fn has_state_snapshot_failure(&self) -> bool {
890 self.inner.has_state_snapshot_failure
891 }
892
893 pub const fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) {
895 self.inner.has_state_snapshot_failure = has_state_snapshot_failure
896 }
897
898 pub(crate) fn update_fork_db(
900 &self,
901 active_journaled_state: &mut JournaledState,
902 target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
903 ) {
904 self.update_fork_db_contracts(
905 self.inner.persistent_accounts.iter().copied(),
906 active_journaled_state,
907 target_fork,
908 )
909 }
910
911 pub(crate) fn update_fork_db_contracts(
913 &self,
914 accounts: impl IntoIterator<Item = Address>,
915 active_journaled_state: &mut JournaledState,
916 target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
917 ) {
918 if let Some(db) = self.active_fork_db() {
919 merge_account_data(accounts, db, active_journaled_state, target_fork)
920 } else {
921 merge_account_data(accounts, &self.mem_db, active_journaled_state, target_fork)
922 }
923 }
924
925 pub const fn mem_db(&self) -> &FoundryEvmInMemoryDB {
927 &self.mem_db
928 }
929
930 pub fn is_active_fork(&self, id: LocalForkId) -> bool {
932 self.active_fork_ids.map(|(i, _)| i == id).unwrap_or_default()
933 }
934
935 pub fn is_in_forking_mode(&self) -> bool {
937 self.active_fork().is_some()
938 }
939
940 pub fn active_fork(&self) -> Option<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
942 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx))
943 }
944
945 pub fn active_fork_mut(&mut self) -> Option<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
947 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx))
948 }
949
950 pub fn active_fork_db(&self) -> Option<&ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
952 self.active_fork().map(|f| &f.db)
953 }
954
955 pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
957 self.active_fork_mut().map(|f| &mut f.db)
958 }
959
960 pub fn db(&self) -> &dyn Database<Error = DatabaseError> {
962 match self.active_fork_db() {
963 Some(fork_db) => fork_db,
964 None => &self.mem_db,
965 }
966 }
967
968 pub fn db_mut(&mut self) -> &mut dyn Database<Error = DatabaseError> {
970 match self.active_fork_ids.map(|(_, idx)| &mut self.inner.get_fork_mut(idx).db) {
971 Some(fork_db) => fork_db,
972 None => &mut self.mem_db,
973 }
974 }
975
976 pub(crate) fn create_db_snapshot(
978 &self,
979 ) -> BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>> {
980 if let Some((id, idx)) = self.active_fork_ids {
981 let fork = self.inner.get_fork(idx).clone();
982 let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed");
983 BackendDatabaseSnapshot::Forked(id, fork_id, idx, Box::new(fork))
984 } else {
985 BackendDatabaseSnapshot::InMemory(self.mem_db.clone())
986 }
987 }
988
989 pub fn merged_logs(&self, mut logs: Vec<Log>) -> Vec<Log> {
991 if let Some((_, active)) = self.active_fork_ids {
992 let mut all_logs = Vec::with_capacity(logs.len());
993
994 self.inner
995 .forks
996 .iter()
997 .enumerate()
998 .filter_map(|(idx, f)| f.as_ref().map(|f| (idx, f)))
999 .for_each(|(idx, f)| {
1000 if idx == active {
1001 all_logs.append(&mut logs);
1002 } else {
1003 all_logs.extend(f.journaled_state.logs.clone())
1004 }
1005 });
1006 return all_logs;
1007 }
1008
1009 logs
1010 }
1011
1012 pub(crate) fn initialize(
1016 &mut self,
1017 spec_id: impl Into<SpecFor<FEN>>,
1018 caller: Address,
1019 tx_kind: TxKind,
1020 ) {
1021 self.set_caller(caller);
1022 self.set_spec_id(spec_id);
1023
1024 let test_contract = match tx_kind {
1025 TxKind::Call(to) => to,
1026 TxKind::Create => {
1027 let nonce =
1028 self.basic_ref(caller).map(|b| b.unwrap_or_default().nonce).unwrap_or_default();
1029 caller.create(nonce)
1030 }
1031 };
1032 self.set_test_contract(test_contract);
1033 }
1034
1035 #[instrument(name = "inspect", level = "debug", skip_all)]
1040 pub fn inspect<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
1041 &mut self,
1042 evm_env: &mut EvmEnvFor<FEN>,
1043 tx_env: &mut TxEnvFor<FEN>,
1044 inspector: I,
1045 ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
1046 let chain_context = self.chain_context_for_synthetic_transaction(tx_env)?;
1047 self.inspect_with_context(evm_env, tx_env, chain_context, inspector)
1048 }
1049
1050 #[instrument(name = "inspect", level = "debug", skip_all)]
1052 pub fn inspect_with_context<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
1053 &mut self,
1054 evm_env: &mut EvmEnvFor<FEN>,
1055 tx_env: &mut TxEnvFor<FEN>,
1056 chain_context: ChainFor<FEN>,
1057 inspector: I,
1058 ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
1059 self.initialize(evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind());
1060 let factory = FEN::EvmFactory::default();
1061 let mut evm =
1062 factory.create_foundry_evm_with_inspector(self, evm_env.to_owned(), inspector);
1063 *evm.chain_mut() = chain_context;
1064 let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?;
1065
1066 *tx_env = evm.tx().clone();
1067 *evm_env = evm.finish().1;
1068
1069 Ok(res)
1070 }
1071
1072 pub fn is_existing_precompile(&self, addr: &Address) -> bool {
1074 self.inner.precompile_addresses().contains(addr)
1075 }
1076
1077 #[inline]
1079 fn set_init_journaled_state(&mut self, journaled_state: JournaledState) {
1080 trace!("recording fork init journaled_state");
1081 self.fork_init_journaled_state = journaled_state;
1082 }
1083
1084 fn prepare_init_journal_state(&mut self) -> Result<(), BackendError> {
1094 let loaded_accounts = self
1095 .fork_init_journaled_state
1096 .state
1097 .iter()
1098 .filter(|(addr, _)| {
1099 !self.is_existing_precompile(addr)
1100 && !self.inner.persistent_accounts.contains(*addr)
1101 })
1102 .map(|(addr, _)| addr)
1103 .copied()
1104 .collect::<Vec<_>>();
1105
1106 for fork in self.inner.forks_iter_mut() {
1107 let mut journaled_state = self.fork_init_journaled_state.clone();
1108 for loaded_account in loaded_accounts.iter().copied() {
1109 trace!(?loaded_account, "replacing account on init");
1110 let init_account =
1111 journaled_state.state.get_mut(&loaded_account).expect("exists; qed");
1112
1113 if init_account.is_created() {
1117 trace!(?loaded_account, "skipping created account");
1118 continue;
1119 }
1120
1121 let fork_account = Database::basic(&mut fork.db, loaded_account)?
1124 .ok_or(BackendError::MissingAccount(loaded_account))?;
1125 init_account.info = fork_account;
1126 }
1127 fork.journaled_state = journaled_state;
1128 }
1129 Ok(())
1130 }
1131
1132 fn get_block_number_and_block_for_transaction(
1134 &self,
1135 id: LocalForkId,
1136 transaction: B256,
1137 ) -> eyre::Result<TransactionForkTarget> {
1138 let fork = self.inner.get_fork_by_id(id)?;
1139 let tx = fork.backend().get_transaction(transaction)?;
1140
1141 if let Some(tx_block) = tx.block_number() {
1143 let tx_block_hash = tx
1144 .block_hash()
1145 .ok_or_else(|| eyre::eyre!("mined transaction is missing its block hash"))?;
1146 let block = fork.backend().get_full_block(tx_block_hash)?;
1147 eyre::ensure!(
1148 block.header().number() == tx_block && block.header().hash == tx_block_hash,
1149 "transaction block changed: expected {} ({}), got {} ({})",
1150 tx_block,
1151 tx_block_hash,
1152 block.header().number(),
1153 block.header().hash
1154 );
1155 let position = if let BlockTransactions::Full(transactions) = block.transactions() {
1156 let index = transactions.iter().position(|tx| tx.tx_hash() == transaction);
1157 if self.networks.is_monad() && index.is_none() {
1158 eyre::bail!(
1159 "transaction {transaction:?} is missing from block {}",
1160 block.header().number()
1161 );
1162 }
1163 index.map(|index| TransactionPosition {
1164 index,
1165 #[cfg(feature = "monad")]
1166 count: transactions.len(),
1167 })
1168 } else {
1169 if self.networks.is_monad() {
1170 eyre::bail!(
1171 "block {} does not contain full transactions",
1172 block.header().number()
1173 );
1174 }
1175 None
1176 };
1177
1178 let fork_block = BlockNumHash::new(
1181 tx_block.checked_sub(1).ok_or_else(|| {
1182 eyre::eyre!("cannot replay a transaction in the genesis block")
1183 })?,
1184 block.header().parent_hash(),
1185 );
1186 Ok(TransactionForkTarget { fork_block, transaction: tx, block, mined: true, position })
1187 } else {
1188 if self.networks.is_monad() {
1189 eyre::bail!(
1190 "transaction {transaction} is pending and has no canonical block context"
1191 );
1192 }
1193 let block = fork.backend().get_full_block(BlockNumberOrTag::Latest)?;
1194
1195 let fork_block = BlockNumHash::new(block.header().number(), block.header().hash);
1196
1197 Ok(TransactionForkTarget {
1198 fork_block,
1199 transaction: tx,
1200 block,
1201 mined: false,
1202 position: None,
1203 })
1204 }
1205 }
1206
1207 #[cfg(feature = "monad")]
1209 fn full_block_tx_envs(block: &AnyRpcBlock) -> eyre::Result<Vec<TxEnvFor<FEN>>> {
1210 let BlockTransactions::Full(transactions) = block.transactions() else {
1211 eyre::bail!("block {} does not contain full transactions", block.header().number());
1212 };
1213 transactions.iter().map(TxEnvFor::<FEN>::from_any_rpc_transaction).collect()
1214 }
1215
1216 fn replay_tx_env(tx: &AnyRpcTransaction) -> eyre::Result<Option<TxEnvFor<FEN>>> {
1219 let is_system = is_known_system_sender(tx.from()) || tx.ty() == SYSTEM_TRANSACTION_TYPE;
1220 if is_system {
1221 #[cfg(not(feature = "monad"))]
1222 return Ok(None);
1223 #[cfg(feature = "monad")]
1224 return Ok(TxEnvFor::<FEN>::from_any_rpc_transaction(tx).ok());
1225 }
1226
1227 TxEnvFor::<FEN>::from_any_rpc_transaction(tx).map(Some)
1228 }
1229
1230 #[cfg(feature = "monad")]
1232 fn block_context_inputs_from_backend(
1233 backend: &SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
1234 block: &AnyRpcBlock,
1235 ) -> eyre::Result<BlockContext<FEN>> {
1236 let current = Self::full_block_tx_envs(block)?;
1237
1238 let parent_hash = block.header().parent_hash();
1239 let parent_block = if parent_hash.is_zero() {
1240 None
1241 } else {
1242 let parent_number = block.header().number().checked_sub(1).ok_or_else(|| {
1243 eyre::eyre!("genesis block has non-zero parent hash {parent_hash}")
1244 })?;
1245 let parent = backend
1246 .get_full_block(parent_hash)
1247 .wrap_err_with(|| format!("failed to fetch parent block {parent_hash}"))?;
1248 ensure_block_identity(
1249 &parent,
1250 BlockNumHash::new(parent_number, parent_hash),
1251 "parent",
1252 )?;
1253 Some(parent)
1254 };
1255 let parent =
1256 parent_block.as_ref().map(Self::full_block_tx_envs).transpose()?.unwrap_or_default();
1257
1258 let grandparent = if let Some(parent_block) = &parent_block {
1259 let grandparent_hash = parent_block.header().parent_hash();
1260 if grandparent_hash.is_zero() {
1261 Vec::new()
1262 } else {
1263 let block = backend.get_full_block(grandparent_hash).wrap_err_with(|| {
1264 format!("failed to fetch grandparent block {grandparent_hash}")
1265 })?;
1266 let grandparent_number =
1267 parent_block.header().number().checked_sub(1).ok_or_else(|| {
1268 eyre::eyre!("genesis block has non-zero parent hash {grandparent_hash}")
1269 })?;
1270 ensure_block_identity(
1271 &block,
1272 BlockNumHash::new(grandparent_number, grandparent_hash),
1273 "grandparent",
1274 )?;
1275 Self::full_block_tx_envs(&block)?
1276 }
1277 } else {
1278 Vec::new()
1279 };
1280
1281 Ok(BlockContext::new(grandparent, parent, current))
1282 }
1283
1284 #[cfg(feature = "monad")]
1286 fn block_context_inputs(
1287 &self,
1288 id: LocalForkId,
1289 block: &AnyRpcBlock,
1290 ) -> eyre::Result<BlockContext<FEN>> {
1291 let fork = self.inner.get_fork_by_id(id)?;
1292 Self::block_context_inputs_from_backend(fork.backend(), block)
1293 }
1294
1295 #[cfg(feature = "monad")]
1297 fn context_for_block_position(
1298 block_context: BlockContext<FEN>,
1299 position: ForkPosition,
1300 tx: &TxEnvFor<FEN>,
1301 ) -> eyre::Result<ChainFor<FEN>> {
1302 let cursor = match position {
1303 ForkPosition::AfterBlock { .. } => block_context.into_child(),
1304 ForkPosition::BeforeTransaction { transaction_index, .. } => {
1305 block_context.before_transaction(transaction_index)?
1306 }
1307 };
1308 Ok(cursor.next_transaction(tx))
1309 }
1310
1311 #[cfg(feature = "monad")]
1313 fn context_for_fork_synthetic_transaction(
1314 &self,
1315 id: LocalForkId,
1316 tx: &TxEnvFor<FEN>,
1317 ) -> eyre::Result<ChainFor<FEN>> {
1318 if !self.networks.is_monad() {
1319 return Ok(ChainFor::<FEN>::for_transaction(tx));
1320 }
1321
1322 let fork = self.inner.get_fork_by_id(id)?;
1323 let (position_block, position) = match fork.position {
1324 position @ (ForkPosition::AfterBlock { block }
1325 | ForkPosition::BeforeTransaction { block, .. }) => (block, position),
1326 };
1327 let block = fork.backend().get_full_block(position_block.hash).wrap_err_with(|| {
1328 format!(
1329 "failed to fetch fork block {} ({})",
1330 position_block.number, position_block.hash
1331 )
1332 })?;
1333 ensure_block_identity(&block, position_block, "fork")?;
1334 let context = Self::block_context_inputs_from_backend(fork.backend(), &block)?;
1335 Self::context_for_block_position(context, position, tx)
1336 }
1337
1338 #[cfg(feature = "monad")]
1340 pub fn block_context_for_synthetic_transaction(
1341 &self,
1342 ) -> eyre::Result<Option<BlockContext<FEN>>> {
1343 if !self.networks.is_monad() {
1344 return Ok(None);
1345 }
1346 let Some(id) = self.active_fork_id() else {
1347 return Ok(None);
1348 };
1349
1350 let fork = self.inner.get_fork_by_id(id)?;
1351 let (position_block, transaction_index) = match fork.position {
1352 ForkPosition::AfterBlock { block } => (block, None),
1353 ForkPosition::BeforeTransaction { block, transaction_index } => {
1354 (block, Some(transaction_index))
1355 }
1356 };
1357 let block = fork.backend().get_full_block(position_block.hash).wrap_err_with(|| {
1358 format!(
1359 "failed to fetch active fork block {} ({})",
1360 position_block.number, position_block.hash
1361 )
1362 })?;
1363 ensure_block_identity(&block, position_block, "active fork")?;
1364 let context = Self::block_context_inputs_from_backend(fork.backend(), &block)?;
1365
1366 match transaction_index {
1367 Some(index) => context.before_transaction(index).map(Some),
1368 None => Ok(Some(context.into_child())),
1369 }
1370 }
1371
1372 fn apply_fork_tx_replay_env_changes(
1374 &self,
1375 id: LocalForkId,
1376 evm_env: &mut EvmEnvFor<FEN>,
1377 ) -> eyre::Result<()> {
1378 let fork_id = self.inner.ensure_fork_id(id).cloned()?;
1379 let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
1380 self.apply_fork_tx_replay_env_changes_for(&fork_id, source_chain_id, evm_env)
1381 }
1382
1383 fn apply_fork_tx_replay_env_changes_for(
1385 &self,
1386 fork_id: &ForkId,
1387 source_chain_id: ChainId,
1388 evm_env: &mut EvmEnvFor<FEN>,
1389 ) -> eyre::Result<()> {
1390 let fork_evm_env = self
1391 .forks
1392 .get_evm_env(fork_id.clone())?
1393 .ok_or_else(|| eyre::eyre!("Requested fork `{fork_id}` does not exist"))?;
1394 evm_env.cfg_env.chain_id = fork_evm_env.cfg_env.chain_id;
1395 apply_chain_specific_tx_replay_env_changes_for_chain(evm_env, source_chain_id);
1396 Ok(())
1397 }
1398
1399 fn populate_rolled_active_fork(
1401 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1402 persistent_accounts: &AddressSet,
1403 caller: Option<Address>,
1404 journaled_state: &mut JournaledState,
1405 ) {
1406 let mut persistent_addrs = persistent_accounts.clone();
1407 persistent_addrs.extend(caller);
1408
1409 fork.journaled_state.depth = journaled_state.depth;
1410
1411 for addr in persistent_addrs {
1412 merge_journaled_state_data(addr, journaled_state, &mut fork.journaled_state);
1413 }
1414
1415 for (addr, acc) in &journaled_state.state {
1416 if acc.is_created() && acc.is_touched() {
1417 merge_journaled_state_data(*addr, journaled_state, &mut fork.journaled_state);
1418 } else if !acc.is_created() {
1419 let _ = fork.journaled_state.load_account(&mut fork.db, *addr);
1420 }
1421 }
1422
1423 *journaled_state = fork.journaled_state.clone();
1424 }
1425
1426 fn reset_rolled_active_fork(
1428 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1429 fork_init_journaled_state: &JournaledState,
1430 persistent_accounts: &AddressSet,
1431 caller: Option<Address>,
1432 journaled_state: &mut JournaledState,
1433 ) {
1434 fork.journaled_state = fork_init_journaled_state.clone();
1435 Self::populate_rolled_active_fork(fork, persistent_accounts, caller, journaled_state);
1436 }
1437
1438 fn roll_fork_with_context(
1440 &mut self,
1441 id: Option<LocalForkId>,
1442 block_number: u64,
1443 evm_env: &mut EvmEnvFor<FEN>,
1444 tx_env: Option<&TxEnvFor<FEN>>,
1445 journaled_state: &mut JournaledState,
1446 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1447 trace!(?id, ?block_number, "roll fork");
1448 let id = self.ensure_fork(id)?;
1449 let rolled = self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?;
1450 self.apply_rolled_fork_with_context(id, rolled, evm_env, tx_env, journaled_state)
1451 }
1452
1453 fn roll_fork_exact_with_context(
1454 &mut self,
1455 id: LocalForkId,
1456 block: BlockNumHash,
1457 evm_env: &mut EvmEnvFor<FEN>,
1458 tx_env: Option<&TxEnvFor<FEN>>,
1459 journaled_state: &mut JournaledState,
1460 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1461 trace!(?id, ?block, "roll fork to exact block");
1462 let rolled = self.forks.roll_fork_exact(self.inner.ensure_fork_id(id).cloned()?, block)?;
1463 self.apply_rolled_fork_with_context(id, rolled, evm_env, tx_env, journaled_state)
1464 }
1465
1466 fn apply_rolled_fork_with_context(
1467 &mut self,
1468 id: LocalForkId,
1469 rolled: ForkResult<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
1470 evm_env: &mut EvmEnvFor<FEN>,
1471 _tx_env: Option<&TxEnvFor<FEN>>,
1472 journaled_state: &mut JournaledState,
1473 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1474 let ForkResult { id: fork_id, backend, env: fork_env, resolved } = rolled;
1475 let context = resolved.context();
1476 let block = resolved.block();
1477 let _affects_active = self.is_active_fork(id);
1478
1479 #[cfg(feature = "monad")]
1480 let context_update = if _affects_active && let Some(tx) = _tx_env {
1481 let chain_context = if self.networks.is_monad() {
1482 let block_data = backend.get_full_block(block.hash).wrap_err_with(|| {
1483 format!("failed to fetch rolled fork block {} ({})", block.number, block.hash)
1484 })?;
1485 ensure_block_identity(&block_data, block, "rolled fork")?;
1486 let block_context = Self::block_context_inputs_from_backend(&backend, &block_data)?;
1487 block_context.into_child().next_transaction(tx)
1488 } else {
1489 ChainFor::<FEN>::for_transaction(tx)
1490 };
1491 ContextUpdate::Replace(chain_context)
1492 } else {
1493 ContextUpdate::Unchanged
1494 };
1495 #[cfg(not(feature = "monad"))]
1496 let context_update = std::marker::PhantomData;
1497
1498 self.inner.roll_fork(id, fork_id, block, context.source_chain_id, backend)?;
1500
1501 if let Some((active_id, active_idx)) = self.active_fork_ids
1502 && active_id == id
1503 {
1504 let preserved_spec = evm_env.cfg_env.spec;
1505 *evm_env = fork_env;
1506 evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1507
1508 let persistent_accounts = self.inner.persistent_accounts.clone();
1509 let caller = self.inner.caller;
1510 let active = self.inner.get_fork_mut(active_idx);
1511 Self::reset_rolled_active_fork(
1512 active,
1513 &self.fork_init_journaled_state,
1514 &persistent_accounts,
1515 caller,
1516 journaled_state,
1517 );
1518 }
1519
1520 Ok(context_update)
1521 }
1522
1523 fn roll_fork_to_transaction_with_context(
1526 &mut self,
1527 id: Option<LocalForkId>,
1528 transaction: B256,
1529 evm_env: &mut EvmEnvFor<FEN>,
1530 tx_env: Option<&TxEnvFor<FEN>>,
1531 journaled_state: &mut JournaledState,
1532 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1533 if !self.networks.is_monad() {
1534 return self.roll_fork_to_transaction_inner(
1535 id,
1536 transaction,
1537 evm_env,
1538 tx_env,
1539 journaled_state,
1540 );
1541 }
1542
1543 #[cfg(not(feature = "monad"))]
1544 unreachable!("block context is only required when Monad support is enabled");
1545
1546 #[cfg(feature = "monad")]
1547 {
1548 trace!(?id, ?transaction, "roll fork to transaction");
1549 let id = self.ensure_fork(id)?;
1550 let affects_active = self.is_active_fork(id);
1551 let TransactionForkTarget { fork_block, block, position, .. } =
1552 self.get_block_number_and_block_for_transaction(id, transaction)?;
1553 let position = position.expect("Monad transaction target includes canonical position");
1554 let block_context = self.block_context_inputs(id, &block)?;
1555 let context_update = if affects_active && let Some(tx) = tx_env {
1556 let fork_position = ForkPosition::BeforeTransaction {
1557 block: BlockNumHash::new(block.header().number(), block.header().hash),
1558 transaction_index: position.index,
1559 };
1560 ContextUpdate::Replace(Self::context_for_block_position(
1561 block_context.clone(),
1562 fork_position,
1563 tx,
1564 )?)
1565 } else if affects_active {
1566 ContextUpdate::Unchanged
1567 } else {
1568 ContextUpdate::Rebase
1569 };
1570
1571 let current_fork_id = self.inner.ensure_fork_id(id).cloned()?;
1572 let ForkResult { id: fork_id, backend, env: fork_env, resolved } =
1573 self.forks.roll_fork_exact(current_fork_id, fork_block)?;
1574 let staged_fork_journaled_state = if affects_active {
1575 self.fork_init_journaled_state.clone()
1576 } else {
1577 self.inner.get_fork_by_id(id)?.journaled_state.clone()
1578 };
1579 let mut staged_fork = self.inner.stage_fork_roll(
1580 id,
1581 fork_id,
1582 fork_block,
1583 resolved.context().source_chain_id,
1584 backend,
1585 staged_fork_journaled_state,
1586 )?;
1587 let mut staged_evm_env = evm_env.clone();
1588 let mut staged_journaled_state = journaled_state.clone();
1589
1590 if affects_active {
1591 let preserved_spec = staged_evm_env.cfg_env.spec;
1592 staged_evm_env = fork_env;
1593 staged_evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1594 Self::populate_rolled_active_fork(
1595 &mut staged_fork.fork,
1596 &self.inner.persistent_accounts,
1597 self.inner.caller,
1598 &mut staged_journaled_state,
1599 );
1600 }
1601
1602 update_env_block::<AnyNetwork, _, _>(
1603 &mut staged_evm_env,
1604 &block,
1605 staged_fork.fork.source_chain_id,
1606 self.networks,
1607 );
1608 let mut replay_env = staged_evm_env.clone();
1609 self.apply_fork_tx_replay_env_changes_for(
1610 &staged_fork.fork_id,
1611 staged_fork.fork.source_chain_id,
1612 &mut replay_env,
1613 )?;
1614 let target = Self::replay_until(
1615 &mut staged_fork.fork,
1616 ReplayInputs {
1617 fork_id: staged_fork.fork_id.clone(),
1618 forks: self.forks.clone(),
1619 evm_env: replay_env,
1620 networks: self.networks,
1621 },
1622 &block,
1623 Some(&block_context),
1624 transaction,
1625 &mut staged_journaled_state,
1626 &self.inner.persistent_accounts,
1627 )?;
1628 eyre::ensure!(
1629 target.is_some(),
1630 "transaction {transaction:?} is missing from block {}",
1631 block.header().number()
1632 );
1633 staged_fork.fork.position = ForkPosition::BeforeTransaction {
1634 block: BlockNumHash::new(block.header().number(), block.header().hash),
1635 transaction_index: position.index,
1636 };
1637
1638 self.forks
1640 .update_block_env(staged_fork.fork_id.clone(), staged_evm_env.block_env.clone())?;
1641 self.inner.publish_fork_roll(staged_fork);
1642 *evm_env = staged_evm_env;
1643 *journaled_state = staged_journaled_state;
1644 Ok(context_update)
1645 }
1646 }
1647
1648 fn roll_fork_to_transaction_inner(
1650 &mut self,
1651 id: Option<LocalForkId>,
1652 transaction: B256,
1653 evm_env: &mut EvmEnvFor<FEN>,
1654 _tx_env: Option<&TxEnvFor<FEN>>,
1655 journaled_state: &mut JournaledState,
1656 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1657 trace!(?id, ?transaction, "roll fork to transaction");
1658 let id = self.ensure_fork(id)?;
1659 let _affects_active = self.is_active_fork(id);
1660
1661 let TransactionForkTarget { fork_block, block, mined, position, .. } =
1662 self.get_block_number_and_block_for_transaction(id, transaction)?;
1663 #[cfg(feature = "monad")]
1664 let block_context = if self.networks.is_monad() {
1665 Some(self.block_context_inputs(id, &block)?)
1666 } else {
1667 None
1668 };
1669 #[cfg(feature = "monad")]
1670 let context_update = if _affects_active && let Some(tx) = _tx_env {
1671 let chain_context = if let Some(context) = &block_context {
1672 let fork_position = ForkPosition::BeforeTransaction {
1673 block: BlockNumHash::new(block.header().number(), block.header().hash),
1674 transaction_index: position
1675 .expect("Monad transaction target includes canonical position")
1676 .index,
1677 };
1678 Self::context_for_block_position(context.clone(), fork_position, tx)?
1679 } else {
1680 ChainFor::<FEN>::for_transaction(tx)
1681 };
1682 ContextUpdate::Replace(chain_context)
1683 } else if _affects_active {
1684 ContextUpdate::Unchanged
1685 } else {
1686 ContextUpdate::Rebase
1687 };
1688 #[cfg(not(feature = "monad"))]
1689 let context_update = std::marker::PhantomData;
1690
1691 self.roll_fork_exact_with_context(id, fork_block, evm_env, None, journaled_state)?;
1693
1694 let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
1695 update_env_block::<AnyNetwork, _, _>(evm_env, &block, source_chain_id, self.networks);
1696
1697 let mut replay_env = evm_env.clone();
1698 self.apply_fork_tx_replay_env_changes(id, &mut replay_env)?;
1699 let persistent_accounts = self.inner.persistent_accounts.clone();
1700 let target = if mined {
1701 let fork_id = self.inner.ensure_fork_id(id)?.clone();
1702 let forks = self.forks.clone();
1703 let fork = self.inner.get_fork_by_id_mut(id)?;
1704 Self::replay_until(
1705 fork,
1706 ReplayInputs { fork_id, forks, evm_env: replay_env, networks: self.networks },
1707 &block,
1708 #[cfg(feature = "monad")]
1709 block_context.as_ref(),
1710 transaction,
1711 journaled_state,
1712 &persistent_accounts,
1713 )?
1714 } else {
1715 None
1716 };
1717 if target.is_some()
1718 && let Some(position) = position
1719 {
1720 self.inner.get_fork_by_id_mut(id)?.position = ForkPosition::BeforeTransaction {
1721 block: BlockNumHash::new(block.header().number(), block.header().hash),
1722 transaction_index: position.index,
1723 };
1724 }
1725
1726 let fork_id = self.inner.ensure_fork_id(id).cloned().expect("fork was resolved above");
1729 let _ = self.forks.update_block_env(fork_id, evm_env.block_env.clone());
1730
1731 Ok(context_update)
1732 }
1733
1734 fn replay_until(
1738 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1739 replay: ReplayInputs<FEN>,
1740 full_block: &AnyRpcBlock,
1741 #[cfg(feature = "monad")] block_context: Option<&BlockContext<FEN>>,
1742 tx_hash: B256,
1743 journaled_state: &mut JournaledState,
1744 persistent_accounts: &AddressSet,
1745 ) -> eyre::Result<Option<AnyRpcTransaction>> {
1746 let ReplayInputs { fork_id, forks, evm_env, networks } = replay;
1747 trace!(?tx_hash, "replay until transaction");
1748 #[cfg(feature = "monad")]
1749 eyre::ensure!(
1750 !networks.is_monad() || block_context.is_some(),
1751 "block context is required to replay transactions for this network"
1752 );
1753
1754 let BlockTransactions::Full(transactions) = full_block.transactions() else {
1755 eyre::bail!(
1756 "block {} does not contain full transactions",
1757 full_block.header().number()
1758 );
1759 };
1760 let Some(target_index) = transactions.iter().position(|tx| tx.tx_hash() == tx_hash) else {
1761 return Ok(None);
1762 };
1763 if networks.is_monad() {
1764 eyre::ensure!(
1765 fork.position
1766 .after_transaction(
1767 BlockNumHash::new(full_block.header().number(), full_block.header().hash),
1768 full_block.header().parent_hash(),
1769 0,
1770 transactions.len(),
1771 )
1772 .is_some(),
1773 "block {} does not immediately follow the active fork position",
1774 full_block.header().number()
1775 );
1776 }
1777 let target_tx = transactions[target_index].clone();
1778 let factory = FEN::EvmFactory::default();
1779 let mut txs_to_replay = Vec::with_capacity(target_index);
1780 for (index, tx) in transactions[..target_index].iter().enumerate() {
1781 let Some(tx_env) = Self::replay_tx_env(tx)? else { continue };
1782 let is_system = is_known_system_sender(tx.from()) || tx.ty() == SYSTEM_TRANSACTION_TYPE;
1783 txs_to_replay.push((index, tx.clone(), tx_env, is_system));
1784 }
1785
1786 if !txs_to_replay.is_empty() {
1788 let now = Instant::now();
1789
1790 let chain_id = evm_env.cfg_env.chain_id;
1793 let timestamp = evm_env.block_env.timestamp().saturating_to();
1794 let mut replay_backend = Self::new_with_fork_manager(
1795 forks,
1796 &fork_id,
1797 fork.clone(),
1798 journaled_state.clone(),
1799 networks,
1800 )?;
1801
1802 #[cfg(feature = "monad")]
1803 if let Some(context) = block_context {
1804 for (index, tx, tx_env, is_system) in &txs_to_replay {
1805 let mut evm = factory.create_nested_evm(&mut replay_backend, evm_env.clone());
1806 *evm.chain_mut() = context.transaction(*index);
1807 inject_replay_precompiles(networks, evm.precompiles_mut(), chain_id, timestamp);
1808 trace!(tx=?tx.tx_hash(), "committing transaction");
1809 let result = evm
1810 .transact_replay(tx_env.clone(), *is_system)
1811 .wrap_err("backend: failed replaying transaction")?;
1812 drop(evm);
1813 if let Some(result) = result {
1814 replay_backend.commit(result.state);
1815 }
1816 }
1817 }
1818 #[cfg(feature = "monad")]
1819 let replay_without_context = block_context.is_none();
1820 #[cfg(not(feature = "monad"))]
1821 let replay_without_context = true;
1822 if replay_without_context {
1823 let mut evm = factory.create_evm(&mut replay_backend, evm_env.clone());
1826 inject_replay_precompiles(networks, evm.precompiles_mut(), chain_id, timestamp);
1827 for (_, tx, tx_env, is_system) in &txs_to_replay {
1828 trace!(tx=?tx.tx_hash(), "committing transaction");
1829 let state = if *is_system {
1830 let mut replay =
1831 factory.create_nested_evm(&mut **evm.db_mut(), evm_env.clone());
1832 inject_replay_precompiles(
1833 networks,
1834 replay.precompiles_mut(),
1835 chain_id,
1836 timestamp,
1837 );
1838 let Some(result) = replay
1839 .transact_replay(tx_env.clone(), true)
1840 .wrap_err("backend: failed replaying system transaction")?
1841 else {
1842 continue;
1843 };
1844 result.state
1845 } else {
1846 evm.transact(tx_env.clone())
1847 .wrap_err("backend: failed replaying transaction")?
1848 .state
1849 };
1850 evm.db_mut().commit(state);
1851 }
1852 }
1853
1854 let (_, index) = replay_backend.active_fork_ids.expect("replay fork is active");
1855 fork.db = replay_backend.inner.take_fork(index).db;
1856
1857 fork.refresh_journaled_states(journaled_state, persistent_accounts)?;
1860
1861 trace!(elapsed=?now.elapsed(), count=txs_to_replay.len(), "replayed transactions");
1862 }
1863
1864 Ok(Some(target_tx))
1865 }
1866}
1867
1868#[cfg(feature = "monad")]
1869fn ensure_block_identity(
1870 block: &AnyRpcBlock,
1871 expected: BlockNumHash,
1872 relation: &str,
1873) -> eyre::Result<()> {
1874 eyre::ensure!(
1875 block.header().number() == expected.number && block.header().hash == expected.hash,
1876 "{relation} block changed: expected {} ({}), got {} ({})",
1877 expected.number,
1878 expected.hash,
1879 block.header().number(),
1880 block.header().hash
1881 );
1882 Ok(())
1883}
1884
1885impl<FEN: FoundryEvmNetwork> DatabaseExt<FEN::EvmFactory> for Backend<FEN> {
1886 fn chain_context_for_synthetic_transaction(
1887 &self,
1888 tx: &TxEnvFor<FEN>,
1889 ) -> eyre::Result<ChainFor<FEN>> {
1890 #[cfg(feature = "monad")]
1891 if let Some(context) = self.block_context_for_synthetic_transaction()? {
1892 return Ok(context.next_transaction(tx));
1893 }
1894 Ok(ChainFor::<FEN>::for_transaction(tx))
1895 }
1896
1897 fn snapshot_state(
1898 &mut self,
1899 journaled_state: &JournaledState,
1900 evm_env: &EvmEnvFor<FEN>,
1901 ) -> U256 {
1902 trace!("create snapshot");
1903 let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new(
1904 self.create_db_snapshot(),
1905 journaled_state.clone(),
1906 evm_env.clone(),
1907 ));
1908 trace!(target: "backend", "Created new snapshot {}", id);
1909 id
1910 }
1911
1912 fn revert_state(
1913 &mut self,
1914 id: U256,
1915 current_state: &JournaledState,
1916 evm_env: &mut EvmEnvFor<FEN>,
1917 caller: Address,
1918 action: RevertStateSnapshotAction,
1919 ) -> Option<JournaledState> {
1920 trace!(?id, "revert snapshot");
1921 if let Some(mut snapshot) = self.inner.state_snapshots.remove_at(id) {
1922 if action.is_keep() {
1924 self.inner.state_snapshots.insert_at(snapshot.clone(), id);
1925 }
1926
1927 if let Some(account) = current_state.state.get(&CHEATCODE_ADDRESS)
1932 && let Some(slot) = account.storage.get(&GLOBAL_FAIL_SLOT)
1933 && !slot.present_value.is_zero()
1934 {
1935 self.set_state_snapshot_failure(true);
1936 }
1937
1938 snapshot.merge(current_state);
1940 let BackendStateSnapshot { db, mut journaled_state, snap_evm_env } = snapshot;
1941 match db {
1942 BackendDatabaseSnapshot::InMemory(mem_db) => {
1943 self.mem_db = mem_db;
1944 }
1945 BackendDatabaseSnapshot::Forked(id, fork_id, idx, mut fork) => {
1946 journaled_state.state.entry(caller).or_insert_with(|| {
1950 let caller_account = current_state
1951 .state
1952 .get(&caller)
1953 .map(|acc| acc.info.clone())
1954 .unwrap_or_default();
1955
1956 if !fork.db.cache.accounts.contains_key(&caller) {
1957 fork.db.insert_account_info(caller, caller_account.clone());
1959 }
1960 caller_account.into()
1961 });
1962 self.inner.revert_state_snapshot(id, fork_id, idx, *fork);
1963 self.active_fork_ids = Some((id, idx))
1964 }
1965 }
1966
1967 *evm_env = snap_evm_env;
1968 trace!(target: "backend", "Reverted snapshot {}", id);
1969
1970 Some(journaled_state)
1971 } else {
1972 warn!(target: "backend", "No snapshot to revert for {}", id);
1973 None
1974 }
1975 }
1976
1977 fn delete_state_snapshot(&mut self, id: U256) -> bool {
1978 self.inner.state_snapshots.remove_at(id).is_some()
1979 }
1980
1981 fn delete_state_snapshots(&mut self) {
1982 self.inner.state_snapshots.clear()
1983 }
1984
1985 fn create_fork(&mut self, create_fork: CreateFork) -> eyre::Result<LocalForkId> {
1986 trace!("create fork");
1987 let ForkResult { id: fork_id, backend: fork, resolved, .. } =
1988 self.forks.create_fork(create_fork)?;
1989 let context = resolved.context();
1990 let block = resolved.block();
1991 let fork_db = ForkDB::new(fork);
1992 let (id, _) = self.inner.insert_new_fork(
1993 fork_id,
1994 block,
1995 context.source_chain_id,
1996 fork_db,
1997 self.fork_init_journaled_state.clone(),
1998 );
1999 Ok(id)
2000 }
2001
2002 fn create_fork_at_transaction(
2003 &mut self,
2004 fork: CreateFork,
2005 transaction: B256,
2006 ) -> eyre::Result<LocalForkId> {
2007 trace!(?transaction, "create fork at transaction");
2008 let id = self.create_fork(fork)?;
2009 let fork_id = self.ensure_fork_id(id).cloned()?;
2010 let mut evm_env = self
2011 .forks
2012 .get_evm_env(fork_id)?
2013 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
2014
2015 self.roll_fork_to_transaction_with_context(
2018 Some(id),
2019 transaction,
2020 &mut evm_env,
2021 None,
2022 &mut self.inner.new_journaled_state(),
2023 )?;
2024
2025 Ok(id)
2026 }
2027
2028 fn select_fork(
2031 &mut self,
2032 id: LocalForkId,
2033 evm_env: &mut EvmEnvFor<FEN>,
2034 tx_env: &mut TxEnvFor<FEN>,
2035 active_journaled_state: &mut JournaledState,
2036 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2037 trace!(?id, "select fork");
2038 if self.is_active_fork(id) {
2039 #[cfg(feature = "monad")]
2041 return Ok(ContextUpdate::Unchanged);
2042 #[cfg(not(feature = "monad"))]
2043 return Ok(std::marker::PhantomData);
2044 }
2045
2046 #[cfg(feature = "monad")]
2047 let chain_context = self.context_for_fork_synthetic_transaction(id, tx_env)?;
2048
2049 if let Some(active_fork_id) = self.active_fork_id() {
2052 self.forks.update_block(
2053 self.ensure_fork_id(active_fork_id).cloned()?,
2054 evm_env.block_env.number(),
2055 evm_env.block_env.timestamp(),
2056 )?;
2057 }
2058
2059 let fork_id = self.ensure_fork_id(id).cloned()?;
2060 let idx = self.inner.ensure_fork_index(&fork_id)?;
2061 let fork_evm_env = self
2062 .forks
2063 .get_evm_env(fork_id)?
2064 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
2065
2066 if let Some(active) = self.active_fork_mut() {
2069 active.journaled_state = active_journaled_state.clone();
2070
2071 let caller = tx_env.caller();
2072 let caller_account = active.journaled_state.state.get(&caller).cloned();
2073 let target_fork = self.inner.get_fork_mut(idx);
2074
2075 if target_fork.journaled_state.depth == 0 {
2077 if let Some(mut acc) = caller_account {
2079 let fork_account = Database::basic(&mut target_fork.db, caller)?
2080 .ok_or(BackendError::MissingAccount(caller))?;
2081
2082 acc.info = fork_account;
2083 target_fork.journaled_state.state.insert(caller, acc);
2084 }
2085 }
2086 } else {
2087 self.set_init_journaled_state(active_journaled_state.clone());
2094 self.prepare_init_journal_state()?;
2095
2096 self.fork_init_journaled_state.depth = 0;
2098 }
2099
2100 {
2101 let mut fork = self.inner.take_fork(idx);
2103
2104 let persistent_accounts = self.inner.persistent_accounts.clone();
2112 if let Some(db) = self.active_fork_db_mut() {
2113 for addr in persistent_accounts {
2114 let Ok(db_account) = db.load_account(addr) else { continue };
2115
2116 let Some(fork_account) = fork.journaled_state.state.get_mut(&addr) else {
2117 continue;
2118 };
2119
2120 for (key, val) in &db_account.storage {
2121 if let Some(fork_storage) = fork_account.storage.get_mut(key) {
2122 fork_storage.present_value = *val;
2123 }
2124 }
2125 }
2126 }
2127
2128 fork.journaled_state.depth = active_journaled_state.depth;
2133
2134 let caller = tx_env.caller();
2138 fork.journaled_state.state.entry(caller).or_insert_with(|| {
2139 let caller_account = active_journaled_state
2140 .state
2141 .get(&caller)
2142 .map(|acc| acc.info.clone())
2143 .unwrap_or_default();
2144
2145 if !fork.db.cache.accounts.contains_key(&caller) {
2146 fork.db.insert_account_info(caller, caller_account.clone());
2148 }
2149 caller_account.into()
2150 });
2151
2152 self.update_fork_db(active_journaled_state, &mut fork);
2153
2154 self.inner.set_fork(idx, fork);
2156 }
2157
2158 self.active_fork_ids = Some((id, idx));
2159 let preserved_spec = evm_env.cfg_env.spec;
2163 tx_env.set_chain_id(Some(fork_evm_env.cfg_env.chain_id));
2164 *evm_env = fork_evm_env;
2165 evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
2166
2167 #[cfg(feature = "monad")]
2168 return Ok(ContextUpdate::Replace(chain_context));
2169 #[cfg(not(feature = "monad"))]
2170 Ok(std::marker::PhantomData)
2171 }
2172
2173 fn roll_fork(
2176 &mut self,
2177 id: Option<LocalForkId>,
2178 block_number: u64,
2179 evm_env: &mut EvmEnvFor<FEN>,
2180 tx_env: &TxEnvFor<FEN>,
2181 journaled_state: &mut JournaledState,
2182 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2183 self.roll_fork_with_context(id, block_number, evm_env, Some(tx_env), journaled_state)
2184 }
2185
2186 fn roll_fork_to_transaction(
2187 &mut self,
2188 id: Option<LocalForkId>,
2189 transaction: B256,
2190 evm_env: &mut EvmEnvFor<FEN>,
2191 tx_env: &TxEnvFor<FEN>,
2192 journaled_state: &mut JournaledState,
2193 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2194 self.roll_fork_to_transaction_with_context(
2195 id,
2196 transaction,
2197 evm_env,
2198 Some(tx_env),
2199 journaled_state,
2200 )
2201 }
2202
2203 fn transact(
2204 &mut self,
2205 maybe_id: Option<LocalForkId>,
2206 transaction: B256,
2207 mut evm_env: EvmEnvFor<FEN>,
2208 _outer_tx_env: &TxEnvFor<FEN>,
2209 journaled_state: &mut JournaledState,
2210 inspector: &mut dyn for<'db> FoundryInspectorExt<
2211 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2212 >,
2213 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2214 trace!(?maybe_id, ?transaction, "execute transaction");
2215 let persistent_accounts = self.inner.persistent_accounts.clone();
2216 let id = self.ensure_fork(maybe_id)?;
2217 let _affects_active = self.is_active_fork(id);
2218 let fork_id = self.ensure_fork_id(id).cloned()?;
2219
2220 let TransactionForkTarget {
2227 transaction: tx,
2228 block,
2229 #[cfg(feature = "monad")]
2230 position,
2231 ..
2232 } = self.get_block_number_and_block_for_transaction(id, transaction)?;
2233 let tx_env = TxEnvFor::<FEN>::from_any_rpc_transaction(&tx)?;
2234 let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
2235 update_env_block::<AnyNetwork, _, _>(&mut evm_env, &block, source_chain_id, self.networks);
2236 self.apply_fork_tx_replay_env_changes(id, &mut evm_env)?;
2237
2238 #[cfg(feature = "monad")]
2239 let block_context = if self.networks.is_monad() {
2240 Some(self.block_context_inputs(id, &block)?)
2241 } else {
2242 None
2243 };
2244 #[cfg(feature = "monad")]
2245 let chain_context = if let Some(context) = &block_context {
2246 context.transaction(
2247 position.expect("Monad transaction target includes canonical position").index,
2248 )
2249 } else {
2250 ChainFor::<FEN>::for_transaction(&tx_env)
2251 };
2252
2253 #[cfg(not(feature = "monad"))]
2254 let chain_context = ChainFor::<FEN>::for_transaction(&tx_env);
2255
2256 #[cfg(feature = "monad")]
2257 let next_position = if block_context.is_some() {
2258 let position = position.expect("Monad transaction target includes canonical position");
2259 Some(
2260 self.inner
2261 .get_fork_by_id(id)?
2262 .position
2263 .after_transaction(
2264 BlockNumHash::new(block.header().number(), block.header().hash),
2265 block.header().parent_hash(),
2266 position.index,
2267 position.count,
2268 )
2269 .ok_or_else(|| {
2270 eyre::eyre!(
2271 "transaction {transaction} does not immediately follow the active \
2272 fork position"
2273 )
2274 })?,
2275 )
2276 } else {
2277 None
2278 };
2279 #[cfg(feature = "monad")]
2280 let context_update = if _affects_active {
2281 ContextUpdate::Replace(if let Some(context) = block_context {
2282 Self::context_for_block_position(
2283 context,
2284 next_position.expect("block context has a next position"),
2285 _outer_tx_env,
2286 )?
2287 } else {
2288 ChainFor::<FEN>::for_transaction(_outer_tx_env)
2289 })
2290 } else {
2291 ContextUpdate::Rebase
2292 };
2293 #[cfg(not(feature = "monad"))]
2294 let context_update = std::marker::PhantomData;
2295
2296 let fork = self.inner.get_fork_by_id_mut(id)?;
2297 commit_transaction::<FEN>(
2298 TransactionInputs {
2299 evm_env,
2300 tx_env,
2301 chain_context,
2302 rpc_block_number: block.header().number(),
2303 },
2304 journaled_state,
2305 fork,
2306 &fork_id,
2307 self.networks,
2308 &persistent_accounts,
2309 inspector,
2310 )?;
2311 #[cfg(feature = "monad")]
2312 if let Some(position) = next_position {
2313 fork.position = position;
2314 }
2315 Ok(context_update)
2316 }
2317
2318 fn transact_from_tx(
2319 &mut self,
2320 tx_env: TxEnvFor<FEN>,
2321 evm_env: EvmEnvFor<FEN>,
2322 journaled_state: &mut JournaledState,
2323 inspector: &mut dyn for<'db> FoundryInspectorExt<
2324 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2325 >,
2326 ) -> eyre::Result<()> {
2327 trace!("execute signed transaction");
2328
2329 self.commit(journaled_state.state.clone());
2330
2331 let res = {
2332 let mut db = self.clone();
2333 let depth = journaled_state.depth + 1;
2334 let factory = FEN::EvmFactory::default();
2335 let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
2336 let mut evm = factory.create_nested_evm_with_inspector(&mut db, evm_env, inspector);
2337 *evm.chain_mut() = chain_context;
2338 evm.journal_inner_mut().depth = depth;
2339 evm.transact_raw(tx_env)?
2340 };
2341
2342 self.commit(res.state);
2343 update_state(&mut journaled_state.state, self, None)?;
2344
2345 Ok(())
2346 }
2347
2348 fn active_fork_id(&self) -> Option<LocalForkId> {
2349 self.active_fork_ids.map(|(id, _)| id)
2350 }
2351
2352 fn active_fork_url(&self) -> Option<String> {
2353 let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
2354 self.forks.get_fork_url(fork.clone()).ok()?
2355 }
2356
2357 fn active_fork_source_chain_id(&self) -> Option<u64> {
2358 Some(self.inner.get_fork_by_id(self.active_fork_id()?).ok()?.source_chain_id)
2359 }
2360
2361 fn active_fork_block_number(&self) -> Option<u64> {
2362 if let Some(block_number) = self.fork_block_number_override {
2363 return Some(block_number);
2364 }
2365 let fork = self.inner.get_fork_by_id(self.active_fork_id()?).ok()?;
2366 Some(match fork.position {
2367 ForkPosition::AfterBlock { block } | ForkPosition::BeforeTransaction { block, .. } => {
2368 block.number
2369 }
2370 })
2371 }
2372
2373 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
2374 if let Some(id) = id {
2375 if self.inner.issued_local_fork_ids.contains_key(&id) {
2376 return Ok(id);
2377 }
2378 eyre::bail!("Requested fork `{}` does not exist", id);
2379 }
2380 if let Some(id) = self.active_fork_id() {
2381 Ok(id)
2382 } else {
2383 eyre::bail!("No fork active");
2384 }
2385 }
2386
2387 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
2388 self.inner.ensure_fork_id(id)
2389 }
2390
2391 fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic> {
2392 let active_id = self.active_fork_id()?;
2393 let active_fork = self.active_fork()?;
2394
2395 if self.inner.forks.len() == 1 {
2396 return None;
2399 }
2400
2401 if !active_fork.is_contract(callee) && !is_contract_in_state(evm_state, callee) {
2402 let mut available_on = Vec::new();
2404 for (id, fork) in self.inner.forks_iter().filter(|(id, _)| *id != active_id) {
2405 trace!(?id, address=?callee, "checking if account exists");
2406 if fork.is_contract(callee) {
2407 available_on.push(id);
2408 }
2409 }
2410
2411 return if available_on.is_empty() {
2412 Some(RevertDiagnostic::ContractDoesNotExist {
2413 contract: callee,
2414 active: active_id,
2415 persistent: self.is_persistent(&callee),
2416 })
2417 } else {
2418 Some(RevertDiagnostic::ContractExistsOnOtherForks {
2421 contract: callee,
2422 active: active_id,
2423 available_on,
2424 })
2425 };
2426 }
2427 None
2428 }
2429
2430 fn load_allocs(
2434 &mut self,
2435 allocs: &BTreeMap<Address, GenesisAccount>,
2436 journaled_state: &mut JournaledState,
2437 ) -> Result<(), BackendError> {
2438 for (addr, acc) in allocs {
2440 self.clone_account(acc, addr, journaled_state)?;
2441 }
2442
2443 Ok(())
2444 }
2445
2446 fn clone_account(
2451 &mut self,
2452 source: &GenesisAccount,
2453 target: &Address,
2454 journaled_state: &mut JournaledState,
2455 ) -> Result<(), BackendError> {
2456 let mut state_acc = journaled_state.load_account_mut(self, *target)?;
2459
2460 if let Some(bytecode) = source.code.as_ref() {
2462 let bytecode_hash = keccak256(bytecode);
2463 let bytecode = Bytecode::new_raw(bytecode.0.clone().into());
2464 state_acc.set_code(bytecode_hash, bytecode);
2465 }
2466
2467 state_acc.set_balance(source.balance);
2469
2470 if let Some(acc) = journaled_state.state.get_mut(target) {
2472 if let Some(storage) = source.storage.as_ref() {
2473 for (slot, value) in storage {
2474 let slot = U256::from_be_bytes(slot.0);
2475 acc.storage.insert(
2476 slot,
2477 EvmStorageSlot::new_changed(
2478 acc.storage.get(&slot).map(|s| s.present_value).unwrap_or_default(),
2479 U256::from_be_bytes(value.0),
2480 TransactionId::ZERO,
2481 ),
2482 );
2483 }
2484 }
2485
2486 acc.info.nonce = source.nonce.unwrap_or_default();
2488 };
2489
2490 journaled_state.touch(*target);
2492
2493 Ok(())
2494 }
2495
2496 fn add_persistent_account(&mut self, account: Address) -> bool {
2497 trace!(?account, "add persistent account");
2498 self.inner.persistent_accounts.insert(account)
2499 }
2500
2501 fn refresh_fork_account(
2502 &mut self,
2503 address: Address,
2504 field: ForkAccountField,
2505 journaled_state: &mut JournaledState,
2506 ) -> Result<(), BackendError> {
2507 let Some(fork) = self.active_fork_mut() else { return Ok(()) };
2508 trace!(?address, ?field, "refresh fork account");
2509 fork.db.db.data().accounts.write().remove(&address);
2510
2511 let cache_modified = fork
2512 .db
2513 .cache
2514 .accounts
2515 .get(&address)
2516 .is_some_and(|account| account.account_state != AccountState::None);
2517 if !cache_modified {
2518 fork.db.cache.accounts.remove(&address);
2519 }
2520
2521 if !cache_modified
2522 && !journaled_state.state.contains_key(&address)
2523 && !fork.journaled_state.state.contains_key(&address)
2524 {
2525 return Ok(());
2526 }
2527
2528 let mut refreshed = DatabaseRef::basic_ref(&fork.db.db, address)?.unwrap_or_default();
2529 if matches!(field, ForkAccountField::Code) {
2530 fork.db.insert_contract(&mut refreshed);
2531 }
2532 if let Some(cached) = fork.db.cache.accounts.get_mut(&address) {
2533 field.update(&mut cached.info, &refreshed);
2534 if cached.account_state == AccountState::NotExisting && !refreshed.is_empty() {
2535 cached.account_state = AccountState::None;
2536 }
2537 }
2538 if let Some(journaled_account) = journaled_state.state.get_mut(&address) {
2539 field.update(&mut journaled_account.info, &refreshed);
2540 }
2541 if let Some(journaled_account) = fork.journaled_state.state.get_mut(&address) {
2542 field.update(&mut journaled_account.info, &refreshed);
2543 }
2544 Ok(())
2545 }
2546
2547 fn refresh_fork_storage(
2548 &mut self,
2549 address: Address,
2550 slot: U256,
2551 journaled_state: &mut JournaledState,
2552 ) -> Result<(), BackendError> {
2553 let Some(fork) = self.active_fork_mut() else { return Ok(()) };
2554 trace!(?address, ?slot, "refresh fork storage");
2555 if let Some(storage) = fork.db.db.data().storage.write().get_mut(&address) {
2556 storage.remove(&slot);
2557 }
2558 let cache_modified = fork
2559 .db
2560 .cache
2561 .accounts
2562 .get(&address)
2563 .is_some_and(|account| account.account_state != AccountState::None);
2564 if !cache_modified && let Some(account) = fork.db.cache.accounts.get_mut(&address) {
2565 account.storage.remove(&slot);
2566 }
2567
2568 let outer_loaded = journaled_state
2569 .state
2570 .get(&address)
2571 .is_some_and(|account| account.storage.contains_key(&slot));
2572 let fork_loaded = fork
2573 .journaled_state
2574 .state
2575 .get(&address)
2576 .is_some_and(|account| account.storage.contains_key(&slot));
2577 if !cache_modified && !outer_loaded && !fork_loaded {
2578 return Ok(());
2579 }
2580
2581 let value = DatabaseRef::storage_ref(&fork.db.db, address, slot)?;
2582 if let Some(account) = fork.db.cache.accounts.get_mut(&address) {
2583 account.storage.insert(slot, value);
2584 if account.account_state == AccountState::NotExisting && !value.is_zero() {
2585 account.account_state = AccountState::None;
2586 }
2587 }
2588 if let Some(storage) = journaled_state
2589 .state
2590 .get_mut(&address)
2591 .and_then(|account| account.storage.get_mut(&slot))
2592 {
2593 storage.present_value = value;
2594 }
2595 if let Some(storage) = fork
2596 .journaled_state
2597 .state
2598 .get_mut(&address)
2599 .and_then(|account| account.storage.get_mut(&slot))
2600 {
2601 storage.present_value = value;
2602 }
2603 Ok(())
2604 }
2605
2606 fn remove_persistent_account(&mut self, account: &Address) -> bool {
2607 trace!(?account, "remove persistent account");
2608 self.inner.persistent_accounts.remove(account)
2609 }
2610
2611 fn is_persistent(&self, acc: &Address) -> bool {
2612 self.inner.persistent_accounts.contains(acc)
2613 }
2614
2615 fn allow_cheatcode_access(&mut self, account: Address) -> bool {
2616 trace!(?account, "allow cheatcode access");
2617 self.inner.cheatcode_access_accounts.insert(account)
2618 }
2619
2620 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
2621 trace!(?account, "revoke cheatcode access");
2622 self.inner.cheatcode_access_accounts.remove(account)
2623 }
2624
2625 fn has_cheatcode_access(&self, account: &Address) -> bool {
2626 self.inner.cheatcode_access_accounts.contains(account)
2627 }
2628
2629 fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
2630 if let Some(db) = self.active_fork_db_mut() {
2631 db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
2632 } else {
2633 self.mem_db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
2634 }
2635 }
2636}
2637
2638impl<FEN: FoundryEvmNetwork> DatabaseRef for Backend<FEN> {
2639 type Error = DatabaseError;
2640
2641 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
2642 let mut account = if let Some(db) = self.active_fork_db() {
2643 db.basic_ref(address)?
2644 } else {
2645 self.mem_db.basic_ref(address)?
2646 };
2647 self.apply_bal_account(address, &mut account)?;
2648 Ok(account)
2649 }
2650
2651 fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
2652 if let Some(db) = self.active_fork_db() {
2653 db.code_by_hash_ref(code_hash)
2654 } else {
2655 Ok(self.mem_db.code_by_hash_ref(code_hash)?)
2656 }
2657 }
2658
2659 fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
2660 if let Some(value) = self.bal_storage(address, index)? {
2661 return Ok(value);
2662 }
2663 if let Some(db) = self.active_fork_db() {
2664 DatabaseRef::storage_ref(db, address, index)
2665 } else {
2666 Ok(DatabaseRef::storage_ref(&self.mem_db, address, index)?)
2667 }
2668 }
2669
2670 fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
2671 if let Some(db) = self.active_fork_db() {
2672 db.block_hash_ref(number)
2673 } else {
2674 Ok(self.mem_db.block_hash_ref(number)?)
2675 }
2676 }
2677}
2678
2679impl<FEN: FoundryEvmNetwork> DatabaseCommit for Backend<FEN> {
2680 fn commit(&mut self, changes: AddressMap<Account>) {
2681 if self.bal.take().is_some() {
2682 for (address, account) in &changes {
2685 if !account.is_touched() && !account.is_loaded_as_not_existing() {
2686 self.insert_account_info(*address, account.info.clone());
2687 }
2688 }
2689 }
2690 if let Some(db) = self.active_fork_db_mut() {
2691 db.commit(changes)
2692 } else {
2693 self.mem_db.commit(changes)
2694 }
2695 }
2696}
2697
2698impl<FEN: FoundryEvmNetwork> Database for Backend<FEN> {
2699 type Error = DatabaseError;
2700 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
2701 let mut account = if let Some(db) = self.active_fork_db_mut() {
2702 db.basic(address)?
2703 } else {
2704 self.mem_db.basic(address)?
2705 };
2706 self.apply_bal_account(address, &mut account)?;
2707 Ok(account)
2708 }
2709
2710 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
2711 if let Some(db) = self.active_fork_db_mut() {
2712 Ok(db.code_by_hash(code_hash)?)
2713 } else {
2714 Ok(self.mem_db.code_by_hash(code_hash)?)
2715 }
2716 }
2717
2718 fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
2719 if let Some(value) = self.bal_storage(address, index)? {
2720 return Ok(value);
2721 }
2722 if let Some(db) = self.active_fork_db_mut() {
2723 Ok(Database::storage(db, address, index)?)
2724 } else {
2725 Ok(Database::storage(&mut self.mem_db, address, index)?)
2726 }
2727 }
2728
2729 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
2730 if let Some(db) = self.active_fork_db_mut() {
2731 Ok(db.block_hash(number)?)
2732 } else {
2733 Ok(self.mem_db.block_hash(number)?)
2734 }
2735 }
2736}
2737
2738#[derive(Clone, Debug)]
2740pub enum BackendDatabaseSnapshot<N: Network, B: ForkBlockEnv = BlockEnv> {
2741 InMemory(FoundryEvmInMemoryDB),
2743 Forked(LocalForkId, ForkId, ForkLookupIndex, Box<Fork<N, B>>),
2745}
2746
2747#[derive(Clone, Debug)]
2749pub struct Fork<N: Network, B: ForkBlockEnv = BlockEnv> {
2750 db: ForkDB<N, B>,
2751 journaled_state: JournaledState,
2752 source_chain_id: ChainId,
2753 position: ForkPosition,
2754}
2755
2756impl<N: Network, B: ForkBlockEnv> Fork<N, B> {
2757 pub const fn backend(&self) -> &SharedBackend<N, B> {
2759 &self.db.db
2760 }
2761
2762 pub fn is_contract(&self, acc: Address) -> bool {
2764 if let Ok(Some(acc)) = self.db.basic_ref(acc)
2765 && acc.code_hash != KECCAK_EMPTY
2766 {
2767 return true;
2768 }
2769 is_contract_in_state(&self.journaled_state.state, acc)
2770 }
2771
2772 fn refresh_journaled_states(
2775 &mut self,
2776 journaled_state: &mut JournaledState,
2777 persistent_accounts: &AddressSet,
2778 ) -> Result<(), BackendError> {
2779 update_state(&mut journaled_state.state, &mut self.db, Some(persistent_accounts))?;
2780 update_state(&mut self.journaled_state.state, &mut self.db, Some(persistent_accounts))?;
2781 Ok(())
2782 }
2783}
2784
2785pub struct BackendInner<FEN: FoundryEvmNetwork> {
2787 pub launched_with_fork: Option<(ForkId, LocalForkId, ForkLookupIndex)>,
2792 pub issued_local_fork_ids: HashMap<LocalForkId, ForkId>,
2804 pub created_forks: HashMap<ForkId, ForkLookupIndex>,
2807 pub forks: Vec<Option<Fork<AnyNetwork, BlockEnvFor<FEN>>>>,
2810 #[allow(clippy::type_complexity)]
2812 pub state_snapshots: StateSnapshots<
2813 BackendStateSnapshot<
2814 BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
2815 SpecFor<FEN>,
2816 BlockEnvFor<FEN>,
2817 >,
2818 >,
2819 pub has_state_snapshot_failure: bool,
2829 pub caller: Option<Address>,
2831 pub next_fork_id: LocalForkId,
2833 pub persistent_accounts: AddressSet,
2837 pub spec_id: SpecFor<FEN>,
2839 pub cheatcode_access_accounts: AddressSet,
2841}
2842
2843impl<FEN: FoundryEvmNetwork> Clone for BackendInner<FEN> {
2844 fn clone(&self) -> Self {
2845 Self {
2846 launched_with_fork: self.launched_with_fork.clone(),
2847 issued_local_fork_ids: self.issued_local_fork_ids.clone(),
2848 created_forks: self.created_forks.clone(),
2849 forks: self.forks.clone(),
2850 state_snapshots: self.state_snapshots.clone(),
2851 has_state_snapshot_failure: self.has_state_snapshot_failure,
2852 caller: self.caller,
2853 next_fork_id: self.next_fork_id,
2854 persistent_accounts: self.persistent_accounts.clone(),
2855 spec_id: self.spec_id,
2856 cheatcode_access_accounts: self.cheatcode_access_accounts.clone(),
2857 }
2858 }
2859}
2860
2861impl<FEN: FoundryEvmNetwork> Debug for BackendInner<FEN> {
2862 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2863 f.debug_struct("BackendInner")
2864 .field("launched_with_fork", &self.launched_with_fork)
2865 .field("issued_local_fork_ids", &self.issued_local_fork_ids)
2866 .field("created_forks", &self.created_forks)
2867 .field("forks", &self.forks)
2868 .field("state_snapshots", &self.state_snapshots)
2869 .field("has_state_snapshot_failure", &self.has_state_snapshot_failure)
2870 .field("caller", &self.caller)
2871 .field("next_fork_id", &self.next_fork_id)
2872 .field("persistent_accounts", &self.persistent_accounts)
2873 .field("spec_id", &self.spec_id)
2874 .field("cheatcode_access_accounts", &self.cheatcode_access_accounts)
2875 .finish()
2876 }
2877}
2878
2879impl<FEN: FoundryEvmNetwork> BackendInner<FEN> {
2880 pub fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
2881 self.issued_local_fork_ids
2882 .get(&id)
2883 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
2884 }
2885
2886 pub fn ensure_fork_index(&self, id: &ForkId) -> eyre::Result<ForkLookupIndex> {
2887 self.created_forks
2888 .get(id)
2889 .copied()
2890 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
2891 }
2892
2893 pub fn ensure_fork_index_by_local_id(&self, id: LocalForkId) -> eyre::Result<ForkLookupIndex> {
2894 self.ensure_fork_index(self.ensure_fork_id(id)?)
2895 }
2896
2897 #[track_caller]
2899 fn get_fork(&self, idx: ForkLookupIndex) -> &Fork<AnyNetwork, BlockEnvFor<FEN>> {
2900 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
2901 self.forks[idx].as_ref().unwrap()
2902 }
2903
2904 #[track_caller]
2906 fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork<AnyNetwork, BlockEnvFor<FEN>> {
2907 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
2908 self.forks[idx].as_mut().unwrap()
2909 }
2910
2911 #[track_caller]
2913 fn get_fork_by_id_mut(
2914 &mut self,
2915 id: LocalForkId,
2916 ) -> eyre::Result<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
2917 let idx = self.ensure_fork_index_by_local_id(id)?;
2918 Ok(self.get_fork_mut(idx))
2919 }
2920
2921 #[track_caller]
2923 fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
2924 let idx = self.ensure_fork_index_by_local_id(id)?;
2925 Ok(self.get_fork(idx))
2926 }
2927
2928 fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork<AnyNetwork, BlockEnvFor<FEN>> {
2930 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
2931 self.forks[idx].take().unwrap()
2932 }
2933
2934 fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork<AnyNetwork, BlockEnvFor<FEN>>) {
2935 self.forks[idx] = Some(fork)
2936 }
2937
2938 pub fn forks_iter(
2940 &self,
2941 ) -> impl Iterator<Item = (LocalForkId, &Fork<AnyNetwork, BlockEnvFor<FEN>>)> + '_ {
2942 self.issued_local_fork_ids
2943 .iter()
2944 .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id])))
2945 }
2946
2947 pub fn forks_iter_mut(
2949 &mut self,
2950 ) -> impl Iterator<Item = &mut Fork<AnyNetwork, BlockEnvFor<FEN>>> + '_ {
2951 self.forks.iter_mut().filter_map(|f| f.as_mut())
2952 }
2953
2954 pub fn revert_state_snapshot(
2956 &mut self,
2957 id: LocalForkId,
2958 fork_id: ForkId,
2959 idx: ForkLookupIndex,
2960 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
2961 ) {
2962 self.created_forks.insert(fork_id.clone(), idx);
2963 self.issued_local_fork_ids.insert(id, fork_id);
2964 self.set_fork(idx, fork)
2965 }
2966
2967 pub fn update_fork_mapping(
2969 &mut self,
2970 id: LocalForkId,
2971 fork_id: ForkId,
2972 block: BlockNumHash,
2973 source_chain_id: ChainId,
2974 db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
2975 journaled_state: JournaledState,
2976 ) -> ForkLookupIndex {
2977 let idx = self.forks.len();
2978 self.issued_local_fork_ids.insert(id, fork_id.clone());
2979 self.created_forks.insert(fork_id, idx);
2980
2981 let fork = Fork {
2982 db,
2983 journaled_state,
2984 source_chain_id,
2985 position: ForkPosition::AfterBlock { block },
2986 };
2987 self.forks.push(Some(fork));
2988 idx
2989 }
2990
2991 pub fn roll_fork(
2992 &mut self,
2993 id: LocalForkId,
2994 new_fork_id: ForkId,
2995 block: BlockNumHash,
2996 source_chain_id: ChainId,
2997 backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
2998 ) -> eyre::Result<ForkLookupIndex> {
2999 let fork_id = self.ensure_fork_id(id)?;
3000 let idx = self.ensure_fork_index(fork_id)?;
3001
3002 if let Some(active) = self.forks[idx].as_mut() {
3003 let mut new_db = ForkDB::new(backend);
3005 for addr in self.persistent_accounts.iter().copied() {
3006 merge_db_account_data(addr, &active.db, &mut new_db);
3007 }
3008 active.db = new_db;
3009 active.source_chain_id = source_chain_id;
3010 active.position = ForkPosition::AfterBlock { block };
3011 }
3012 self.issued_local_fork_ids.insert(id, new_fork_id.clone());
3013 self.created_forks.insert(new_fork_id, idx);
3014 Ok(idx)
3015 }
3016
3017 #[cfg(feature = "monad")]
3019 fn stage_fork_roll(
3020 &self,
3021 id: LocalForkId,
3022 new_fork_id: ForkId,
3023 block: BlockNumHash,
3024 source_chain_id: ChainId,
3025 backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
3026 journaled_state: JournaledState,
3027 ) -> eyre::Result<StagedForkRoll<FEN>> {
3028 let fork_id = self.ensure_fork_id(id)?;
3029 let idx = self.ensure_fork_index(fork_id)?;
3030 let current = self.get_fork(idx);
3031
3032 let mut new_db = ForkDB::new(backend);
3035 for addr in self.persistent_accounts.iter().copied() {
3036 merge_db_account_data(addr, ¤t.db, &mut new_db);
3037 }
3038
3039 Ok(StagedForkRoll {
3040 local_id: id,
3041 fork_id: new_fork_id,
3042 fork_index: idx,
3043 fork: Fork {
3044 db: new_db,
3045 journaled_state,
3046 source_chain_id,
3047 position: ForkPosition::AfterBlock { block },
3048 },
3049 })
3050 }
3051
3052 #[cfg(feature = "monad")]
3054 fn publish_fork_roll(&mut self, staged: StagedForkRoll<FEN>) -> ForkLookupIndex {
3055 let StagedForkRoll { local_id, fork_id, fork_index, fork } = staged;
3056 self.set_fork(fork_index, fork);
3057 self.issued_local_fork_ids.insert(local_id, fork_id.clone());
3058 self.created_forks.insert(fork_id, fork_index);
3059 fork_index
3060 }
3061
3062 pub fn insert_new_fork(
3066 &mut self,
3067 fork_id: ForkId,
3068 block: BlockNumHash,
3069 source_chain_id: ChainId,
3070 db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
3071 journaled_state: JournaledState,
3072 ) -> (LocalForkId, ForkLookupIndex) {
3073 self.insert_fork(
3074 fork_id,
3075 Fork {
3076 db,
3077 journaled_state,
3078 source_chain_id,
3079 position: ForkPosition::AfterBlock { block },
3080 },
3081 )
3082 }
3083
3084 fn insert_fork(
3086 &mut self,
3087 fork_id: ForkId,
3088 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
3089 ) -> (LocalForkId, ForkLookupIndex) {
3090 let idx = self.forks.len();
3091 self.created_forks.insert(fork_id.clone(), idx);
3092 let id = self.next_id();
3093 self.issued_local_fork_ids.insert(id, fork_id);
3094 self.forks.push(Some(fork));
3095 (id, idx)
3096 }
3097
3098 fn next_id(&mut self) -> U256 {
3099 let id = self.next_fork_id;
3100 self.next_fork_id += U256::from(1);
3101 id
3102 }
3103
3104 pub fn len(&self) -> usize {
3106 self.issued_local_fork_ids.len()
3107 }
3108
3109 pub fn is_empty(&self) -> bool {
3111 self.issued_local_fork_ids.is_empty()
3112 }
3113
3114 pub fn precompile_addresses(&self) -> AddressSet {
3115 let evm = FEN::EvmFactory::default().create_evm(
3116 EmptyDB::default(),
3117 EvmEnv::new(CfgEnv::new_with_spec(self.spec_id), Default::default()),
3118 );
3119 evm.precompiles().addresses().copied().collect()
3120 }
3121
3122 pub fn new_journaled_state(&self) -> JournaledState {
3124 let mut journal = {
3125 let mut journal_inner = JournalInner::new();
3126 journal_inner.set_spec_id(self.spec_id.into());
3127 journal_inner
3128 };
3129 let precompile_addresses = self.precompile_addresses();
3130 journal.warm_addresses.set_precompile_addresses(&precompile_addresses);
3131 journal
3132 }
3133}
3134
3135impl<FEN: FoundryEvmNetwork> Default for BackendInner<FEN> {
3136 fn default() -> Self {
3137 Self {
3138 launched_with_fork: None,
3139 issued_local_fork_ids: Default::default(),
3140 created_forks: Default::default(),
3141 forks: vec![],
3142 state_snapshots: Default::default(),
3143 has_state_snapshot_failure: false,
3144 caller: None,
3145 next_fork_id: Default::default(),
3146 persistent_accounts: Default::default(),
3147 spec_id: SpecFor::<FEN>::default(),
3148 cheatcode_access_accounts: AddressSet::from_iter([
3151 CHEATCODE_ADDRESS,
3152 TEST_CONTRACT_ADDRESS,
3153 CALLER,
3154 ]),
3155 }
3156 }
3157}
3158
3159pub(crate) fn merge_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
3162 accounts: impl IntoIterator<Item = Address>,
3163 active: &CacheDB<ExtDB>,
3164 active_journaled_state: &mut JournaledState,
3165 target_fork: &mut Fork<N, B>,
3166) {
3167 for addr in accounts {
3168 merge_db_account_data(addr, active, &mut target_fork.db);
3169 merge_journaled_state_data(addr, active_journaled_state, &mut target_fork.journaled_state);
3170 }
3171
3172 *active_journaled_state = target_fork.journaled_state.clone();
3173}
3174
3175pub fn account_fetch_policy_for_source(
3180 source_chain_id: ChainId,
3181 network_profile: NetworkConfigs,
3182) -> AccountFetchPolicy {
3183 let source_chain = Chain::from_id(source_chain_id);
3184 let is_tempo =
3185 source_chain.is_tempo() || (source_chain.named().is_none() && network_profile.is_tempo());
3186 if is_tempo { AccountFetchPolicy::RequireAccountInfo } else { AccountFetchPolicy::Auto }
3187}
3188
3189fn merge_journaled_state_data(
3191 addr: Address,
3192 active_journaled_state: &JournaledState,
3193 fork_journaled_state: &mut JournaledState,
3194) {
3195 if let Some(mut acc) = active_journaled_state.state.get(&addr).cloned() {
3196 trace!(?addr, "updating journaled_state account data");
3197 if let Some(fork_account) = fork_journaled_state.state.get_mut(&addr) {
3198 fork_account.storage.extend(std::mem::take(&mut acc.storage));
3200 std::mem::swap(&mut fork_account.storage, &mut acc.storage);
3202 }
3203 fork_journaled_state.state.insert(addr, acc);
3204 }
3205}
3206
3207fn merge_db_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
3209 addr: Address,
3210 active: &CacheDB<ExtDB>,
3211 fork_db: &mut ForkDB<N, B>,
3212) {
3213 trace!(?addr, "merging database data");
3214
3215 let Some(acc) = active.cache.accounts.get(&addr) else { return };
3216
3217 if let Some(code) = active.cache.contracts.get(&acc.info.code_hash) {
3219 trace!("merging contract cache");
3220 fork_db.cache.contracts.insert(acc.info.code_hash, code.clone());
3221 }
3222
3223 use std::collections::hash_map::Entry;
3225 match fork_db.cache.accounts.entry(addr) {
3226 Entry::Vacant(vacant) => {
3227 trace!("target account not present - inserting from active");
3228 vacant.insert(acc.clone());
3231 }
3232 Entry::Occupied(mut occupied) => {
3233 trace!("target account present - merging storage slots");
3234 let fork_account = occupied.get_mut();
3237 fork_account.storage.extend(&acc.storage);
3238 }
3239 }
3240}
3241
3242fn is_contract_in_state(evm_state: &EvmState, acc: Address) -> bool {
3244 evm_state.get(&acc).map(|acc| acc.info.code_hash != KECCAK_EMPTY).unwrap_or_default()
3245}
3246
3247fn update_env_block<N: Network, SPEC: Into<SpecId> + Copy, BLOCK: FoundryBlock>(
3249 evm_env: &mut EvmEnv<SPEC, BLOCK>,
3250 block: &N::BlockResponse,
3251 source_chain_id: ChainId,
3252 networks: NetworkConfigs,
3253) {
3254 let header = block.header();
3255 let block_env = &mut evm_env.block_env;
3256 block_env.set_timestamp(U256::from(header.timestamp()));
3257 block_env.set_beneficiary(header.beneficiary());
3258 block_env.set_difficulty(header.difficulty());
3259 block_env.set_prevrandao(header.mix_hash());
3260 block_env.set_basefee(header.base_fee_per_gas().unwrap_or_default());
3261 block_env.set_gas_limit(header.gas_limit());
3262 block_env.set_number(U256::from(header.number()));
3263 block_env.set_slot_num(header.slot_number().unwrap_or_default());
3264
3265 apply_chain_and_block_specific_env_changes_for_chain::<N, _, _>(
3266 evm_env,
3267 block,
3268 source_chain_id,
3269 networks,
3270 );
3271}
3272
3273fn commit_transaction<FEN: FoundryEvmNetwork>(
3276 transaction: TransactionInputs<FEN>,
3277 journaled_state: &mut JournaledState,
3278 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
3279 fork_id: &ForkId,
3280 networks: NetworkConfigs,
3281 persistent_accounts: &AddressSet,
3282 inspector: &mut dyn for<'db> FoundryInspectorExt<
3283 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
3284 >,
3285) -> eyre::Result<()> {
3286 let TransactionInputs { evm_env, tx_env, chain_context, rpc_block_number } = transaction;
3287 let now = Instant::now();
3288 let res = {
3289 let fork = fork.clone();
3290 let journaled_state = journaled_state.clone();
3291 let depth = journaled_state.depth;
3292 let mut db: Backend<FEN> =
3293 Backend::new_with_fork(fork_id, fork, journaled_state, networks)?;
3294 db.fork_block_number_override = Some(rpc_block_number);
3295
3296 let mut evm = FEN::EvmFactory::default()
3297 .create_nested_evm_with_inspector(&mut db, evm_env, inspector);
3298 *evm.chain_mut() = chain_context;
3299 evm.journal_inner_mut().depth = depth + 1;
3300 evm.transact_raw(tx_env).wrap_err("backend: failed committing transaction")?
3301 };
3302 trace!(elapsed = ?now.elapsed(), "transacted transaction");
3303
3304 apply_state_changeset(res.state, journaled_state, fork, persistent_accounts)?;
3305 Ok(())
3306}
3307
3308pub fn update_state<DB: Database>(
3311 state: &mut EvmState,
3312 db: &mut DB,
3313 persistent_accounts: Option<&AddressSet>,
3314) -> Result<(), DB::Error> {
3315 for (addr, acc) in state.iter_mut() {
3316 if persistent_accounts.is_none_or(|accounts| !accounts.contains(addr)) {
3317 acc.info = db.basic(*addr)?.unwrap_or_default();
3318 for (key, val) in &mut acc.storage {
3319 val.present_value = db.storage(*addr, *key)?;
3320 }
3321 }
3322 }
3323
3324 Ok(())
3325}
3326
3327fn apply_state_changeset<N: Network, B: ForkBlockEnv>(
3330 state: EvmState,
3331 journaled_state: &mut JournaledState,
3332 fork: &mut Fork<N, B>,
3333 persistent_accounts: &AddressSet,
3334) -> Result<(), BackendError> {
3335 let mut staged_db = fork.db.clone();
3338 let mut staged_journaled_state = journaled_state.clone();
3339 let mut staged_fork_journaled_state = fork.journaled_state.clone();
3340 staged_db.commit(state);
3341 update_state(&mut staged_journaled_state.state, &mut staged_db, Some(persistent_accounts))?;
3342 update_state(
3343 &mut staged_fork_journaled_state.state,
3344 &mut staged_db,
3345 Some(persistent_accounts),
3346 )?;
3347
3348 fork.db = staged_db;
3349 *journaled_state = staged_journaled_state;
3350 fork.journaled_state = staged_fork_journaled_state;
3351 Ok(())
3352}
3353
3354fn inject_replay_precompiles(
3355 networks: NetworkConfigs,
3356 precompiles: &mut PrecompilesMap,
3357 chain_id: ChainId,
3358 timestamp: u64,
3359) {
3360 networks.inject_precompiles(precompiles);
3361 apply_bsc_p256_precompile(precompiles, chain_id, timestamp);
3362}
3363
3364#[cfg(test)]
3365mod tests {
3366 use super::{Fork, ForkAccountField, ReplayInputs, apply_state_changeset, update_env_block};
3367 use crate::{
3368 backend::{Backend, DatabaseExt, ForkPosition},
3369 evm::EthEvmNetwork,
3370 fork::{CreateFork, ForkId, MultiFork},
3371 opts::EvmOpts,
3372 };
3373 use alloy_consensus::{Signed, TxEnvelope, TxLegacy, transaction::Recovered};
3374 use alloy_eips::BlockNumHash;
3375 use alloy_evm::EvmEnv;
3376 use alloy_network::{
3377 AnyHeader, AnyNetwork, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope,
3378 AnyTxType, TransactionBuilder, UnknownTxEnvelope, UnknownTypedTransaction,
3379 };
3380 use alloy_primitives::{
3381 Address, B256, Bytes, Signature, TxKind, U256, address, keccak256, map::AddressSet,
3382 };
3383 use alloy_provider::{Provider, ProviderBuilder, mock::Asserter};
3384 use alloy_rpc_types::{
3385 Block, BlockTransactions, Transaction as RpcTransaction, TransactionRequest,
3386 };
3387 use alloy_serde::WithOtherFields;
3388 use alloy_sol_types::SolValue;
3389 use anvil::{NodeConfig, spawn};
3390 use foundry_common::{SYSTEM_TRANSACTION_TYPE, provider::get_http_provider};
3391 use foundry_config::{Config, NamedChain};
3392 use foundry_evm_networks::{NetworkConfigs, celo::transfer::CELO_TRANSFER_ADDRESS};
3393 use foundry_fork_db::{
3394 SharedBackend,
3395 cache::{BlockchainDb, BlockchainDbMeta},
3396 };
3397 use revm::{
3398 context::{BlockEnv, JournalInner, TxEnv},
3399 database::{AccountState, CacheDB, DatabaseRef, DbAccount},
3400 primitives::{KECCAK_EMPTY, hardfork::SpecId},
3401 state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
3402 };
3403
3404 #[cfg(feature = "monad")]
3405 use super::ensure_block_identity;
3406 #[cfg(feature = "monad")]
3407 use crate::evm::monad::BlockContext;
3408 #[cfg(feature = "monad")]
3409 use monad_revm::{
3410 MonadHardfork,
3411 api::block::syscall_snapshot_calldata,
3412 staking::{STAKING_ADDRESS, constants::SYSTEM_ADDRESS},
3413 };
3414
3415 fn fork_with_closed_backend() -> Fork<AnyNetwork, BlockEnv> {
3416 let provider =
3417 ProviderBuilder::<_, _, AnyNetwork>::default().connect_mocked_client(Asserter::new());
3418 let db = BlockchainDb::new(
3419 BlockchainDbMeta::new(BlockEnv::default(), "http://localhost".to_string()),
3420 None,
3421 );
3422 let (backend, handler) = SharedBackend::new(provider, db, None);
3423 drop(handler);
3424 Fork {
3425 db: CacheDB::new(backend),
3426 journaled_state: JournalInner::new(),
3427 source_chain_id: 1,
3428 position: ForkPosition::AfterBlock { block: BlockNumHash::default() },
3429 }
3430 }
3431
3432 fn rpc_block(number: u64, hash: B256, parent_hash: B256) -> AnyRpcBlock {
3433 let header = AnyHeader { number, parent_hash, ..Default::default() };
3434 AnyRpcBlock::new(
3435 Block::new(
3436 AnyRpcHeader::from_sealed(header.seal(hash)),
3437 BlockTransactions::Full(Vec::new()),
3438 )
3439 .into(),
3440 )
3441 }
3442
3443 fn rpc_transaction(
3444 caller: Address,
3445 nonce: u64,
3446 value: u64,
3447 gas_limit: u64,
3448 recipient: Address,
3449 hash: B256,
3450 ) -> AnyRpcTransaction {
3451 let tx = TxLegacy {
3452 nonce,
3453 gas_limit,
3454 to: TxKind::Call(recipient),
3455 value: U256::from(value),
3456 ..Default::default()
3457 };
3458 let signed =
3459 Signed::new_unchecked(tx, Signature::new(U256::from(1), U256::from(1), false), hash);
3460 AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
3461 inner: Recovered::new_unchecked(
3462 AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(signed)),
3463 caller,
3464 ),
3465 block_hash: None,
3466 block_number: None,
3467 transaction_index: None,
3468 effective_gas_price: None,
3469 block_timestamp: None,
3470 }))
3471 }
3472
3473 #[test]
3474 fn fork_replay_backend_reuses_manager_on_current_thread_runtime() {
3475 let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
3476
3477 let forks = runtime.block_on(async {
3478 let forks = MultiFork::<AnyNetwork, SpecId, BlockEnv>::spawn();
3479 let sender = Address::with_last_byte(0x42);
3480 let recipient = Address::with_last_byte(0x43);
3481 let target = B256::with_last_byte(2);
3482 let mut fork = fork_with_closed_backend();
3483 fork.db.insert_account_info(
3484 sender,
3485 AccountInfo { balance: U256::from(100), ..Default::default() },
3486 );
3487 fork.db.insert_account_info(recipient, AccountInfo::default());
3488 fork.db.insert_account_info(Address::ZERO, AccountInfo::default());
3489 let mut block = rpc_block(1, B256::with_last_byte(1), B256::ZERO);
3490 block.inner.transactions = BlockTransactions::Full(vec![
3491 rpc_transaction(sender, 0, 7, 21_000, recipient, B256::with_last_byte(1)),
3492 rpc_transaction(sender, 1, 0, 21_000, recipient, target),
3493 ]);
3494
3495 let result = Backend::<EthEvmNetwork>::replay_until(
3496 &mut fork,
3497 ReplayInputs {
3498 fork_id: ForkId::new("http://localhost", Some(0)),
3499 forks: forks.clone(),
3500 evm_env: EvmEnv::default(),
3501 networks: NetworkConfigs::default(),
3502 },
3503 &block,
3504 #[cfg(feature = "monad")]
3505 None,
3506 target,
3507 &mut JournalInner::new(),
3508 &AddressSet::default(),
3509 )
3510 .unwrap();
3511
3512 assert!(result.is_some());
3513 assert_eq!(fork.db.basic_ref(recipient).unwrap().unwrap().balance, U256::from(7));
3514 assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().nonce, 1);
3515 forks
3516 });
3517 drop(runtime);
3518 drop(forks);
3519 }
3520
3521 #[test]
3522 fn fork_replay_keeps_system_skips_and_prefix_atomicity() {
3523 let sender = Address::with_last_byte(0x42);
3524 let recipient = Address::with_last_byte(0x43);
3525 let system = address!("6f49a8f621353f12378d0046e7d7e4b9b249dc9e");
3526 let target = B256::with_last_byte(4);
3527
3528 #[cfg(not(feature = "monad"))]
3529 let contexts = [false];
3530 #[cfg(feature = "monad")]
3531 let contexts = [false, true];
3532 for _with_context in contexts {
3533 for invalid_nonce in [false, true] {
3534 let mut fork = fork_with_closed_backend();
3535 fork.db.insert_account_info(
3536 sender,
3537 AccountInfo { balance: U256::from(100), ..Default::default() },
3538 );
3539 fork.db.insert_account_info(recipient, AccountInfo::default());
3540 fork.db.insert_account_info(Address::ZERO, AccountInfo::default());
3541 let block = AnyRpcBlock::new(
3542 Block::new(
3543 AnyRpcHeader::from_sealed(
3544 AnyHeader { number: 1, ..Default::default() }
3545 .seal(B256::with_last_byte(1)),
3546 ),
3547 BlockTransactions::Full(vec![
3548 rpc_transaction(
3549 sender,
3550 0,
3551 7,
3552 21_000,
3553 recipient,
3554 B256::with_last_byte(1),
3555 ),
3556 rpc_transaction(system, 0, 0, 0, recipient, B256::with_last_byte(2)),
3559 rpc_transaction(
3560 sender,
3561 if invalid_nonce { 0 } else { 1 },
3562 5,
3563 21_000,
3564 recipient,
3565 B256::with_last_byte(3),
3566 ),
3567 rpc_transaction(sender, 2, 2, 21_000, recipient, target),
3568 ]),
3569 )
3570 .into(),
3571 );
3572 let networks = NetworkConfigs::default();
3573 #[cfg(feature = "monad")]
3574 let networks = if _with_context { NetworkConfigs::with_monad() } else { networks };
3575 #[cfg(feature = "monad")]
3576 let context = _with_context.then(|| {
3577 BlockContext::<EthEvmNetwork>::new(Vec::new(), Vec::new(), Vec::new())
3578 });
3579 let result = Backend::<EthEvmNetwork>::replay_until(
3580 &mut fork,
3581 ReplayInputs {
3582 fork_id: ForkId::new("http://localhost", Some(0)),
3583 forks: MultiFork::spawn(),
3584 evm_env: EvmEnv::default(),
3585 networks,
3586 },
3587 &block,
3588 #[cfg(feature = "monad")]
3589 context.as_ref(),
3590 target,
3591 &mut JournalInner::new(),
3592 &AddressSet::default(),
3593 );
3594 if invalid_nonce {
3595 assert!(result.is_err());
3596 assert_eq!(fork.db.basic_ref(recipient).unwrap().unwrap().balance, U256::ZERO);
3597 assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().nonce, 0);
3598 } else {
3599 assert!(result.unwrap().is_some());
3600 assert_eq!(
3601 fork.db.basic_ref(recipient).unwrap().unwrap().balance,
3602 U256::from(12)
3603 );
3604 assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().nonce, 2);
3605 }
3606 }
3607 }
3608 }
3609
3610 #[test]
3611 #[cfg(feature = "monad")]
3612 fn fork_replay_executes_monad_system_prefix_with_or_without_profile() {
3613 let transaction = |nonce, hash| {
3614 let tx = TxLegacy {
3615 nonce,
3616 to: TxKind::Call(STAKING_ADDRESS),
3617 input: syscall_snapshot_calldata(),
3618 ..Default::default()
3619 };
3620 let signed = Signed::new_unchecked(
3621 tx,
3622 Signature::new(U256::from(1), U256::from(1), false),
3623 hash,
3624 );
3625 AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
3626 inner: Recovered::new_unchecked(
3627 AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(signed)),
3628 SYSTEM_ADDRESS,
3629 ),
3630 block_hash: None,
3631 block_number: None,
3632 transaction_index: None,
3633 effective_gas_price: None,
3634 block_timestamp: None,
3635 }))
3636 };
3637 let target = B256::with_last_byte(2);
3638 let mut block = rpc_block(1, B256::with_last_byte(1), B256::ZERO);
3639 block.inner.transactions = BlockTransactions::Full(vec![
3640 transaction(3, B256::with_last_byte(1)),
3641 transaction(4, target),
3642 ]);
3643 for with_context in [false, true] {
3644 let mut fork = fork_with_closed_backend();
3645 fork.db.insert_account_info(
3646 SYSTEM_ADDRESS,
3647 AccountInfo { nonce: 3, ..Default::default() },
3648 );
3649 fork.db.insert_account_info(STAKING_ADDRESS, AccountInfo::default());
3650 fork.db.replace_account_storage(STAKING_ADDRESS, Default::default()).unwrap();
3651 let context = with_context.then(|| {
3652 BlockContext::<crate::evm::MonadEvmNetwork>::new(Vec::new(), Vec::new(), Vec::new())
3653 });
3654 let result = Backend::<crate::evm::MonadEvmNetwork>::replay_until(
3655 &mut fork,
3656 ReplayInputs {
3657 fork_id: ForkId::new("http://localhost", Some(0)),
3658 forks: MultiFork::spawn(),
3659 evm_env: EvmEnv::new(
3660 revm::context::CfgEnv::new_with_spec(MonadHardfork::MonadNine),
3661 BlockEnv::default(),
3662 ),
3663 networks: if with_context {
3664 NetworkConfigs::with_monad()
3665 } else {
3666 NetworkConfigs::default()
3667 },
3668 },
3669 &block,
3670 context.as_ref(),
3671 target,
3672 &mut JournalInner::new(),
3673 &AddressSet::default(),
3674 );
3675 assert!(result.unwrap().is_some());
3676 assert_eq!(fork.db.basic_ref(SYSTEM_ADDRESS).unwrap().unwrap().nonce, 4);
3677 }
3678 }
3679
3680 #[test]
3681 #[cfg(feature = "monad")]
3682 fn validates_block_identity() {
3683 let hash = B256::with_last_byte(2);
3684 let block = rpc_block(2, hash, B256::with_last_byte(1));
3685 assert!(ensure_block_identity(&block, BlockNumHash::new(2, hash), "parent").is_ok());
3686
3687 let err =
3688 ensure_block_identity(&block, BlockNumHash::new(2, B256::with_last_byte(3)), "parent")
3689 .unwrap_err();
3690 assert!(err.to_string().contains("parent block changed"));
3691
3692 let err =
3693 ensure_block_identity(&block, BlockNumHash::new(1, hash), "grandparent").unwrap_err();
3694 assert!(err.to_string().contains("grandparent block changed"));
3695 }
3696
3697 #[test]
3698 fn failed_fork_state_refresh_does_not_publish_transaction_changes() {
3699 let mut fork = fork_with_closed_backend();
3700 let externally_loaded = Address::with_last_byte(1);
3701 let fork_loaded = Address::with_last_byte(2);
3702 let committed = Address::with_last_byte(3);
3703 let missing_slot = U256::from(1);
3704
3705 let cached_external = AccountInfo { balance: U256::from(11), ..Default::default() };
3706 let cached_fork = AccountInfo { balance: U256::from(12), ..Default::default() };
3707 fork.db.insert_account_info(externally_loaded, cached_external);
3708 fork.db.insert_account_info(fork_loaded, cached_fork);
3709
3710 let mut journaled_state = JournalInner::new();
3711 let external_account = Account::default()
3712 .with_info(AccountInfo { balance: U256::from(1), ..Default::default() });
3713 journaled_state.state.insert(externally_loaded, external_account);
3714
3715 let mut fork_account = Account::default()
3716 .with_info(AccountInfo { balance: U256::from(2), ..Default::default() });
3717 fork_account
3718 .storage
3719 .insert(missing_slot, EvmStorageSlot::new(U256::ZERO, TransactionId::ZERO));
3720 fork.journaled_state.state.insert(fork_loaded, fork_account);
3721
3722 let mut committed_account = Account::default()
3723 .with_info(AccountInfo { balance: U256::from(13), ..Default::default() });
3724 committed_account.mark_touch();
3725 let mut state = EvmState::default();
3726 state.insert(committed, committed_account);
3727
3728 let result =
3729 apply_state_changeset(state, &mut journaled_state, &mut fork, &AddressSet::default());
3730 assert!(result.is_err());
3731 assert!(!fork.db.cache.accounts.contains_key(&committed));
3732 assert_eq!(journaled_state.state[&externally_loaded].info.balance, U256::from(1));
3733 assert_eq!(fork.journaled_state.state[&fork_loaded].info.balance, U256::from(2));
3734 }
3735
3736 #[test]
3737 fn failed_fork_state_refresh_preserves_not_existing_account() {
3738 let mut fork = fork_with_closed_backend();
3739 let address = Address::with_last_byte(1);
3740 let missing_slot = U256::from(1);
3741 fork.db.cache.accounts.insert(address, DbAccount::new_not_existing());
3742
3743 let mut journaled_state = JournalInner::new();
3744 let mut journaled_account = Account::default()
3745 .with_info(AccountInfo { balance: U256::from(1), ..Default::default() });
3746 journaled_account
3747 .storage
3748 .insert(missing_slot, EvmStorageSlot::new(U256::from(7), TransactionId::ZERO));
3749 journaled_state.state.insert(address, journaled_account);
3750
3751 let mut touched_account = Account::default()
3752 .with_info(AccountInfo { balance: U256::from(13), ..Default::default() });
3753 touched_account.mark_touch();
3754 let mut state = EvmState::default();
3755 state.insert(address, touched_account);
3756
3757 let result =
3758 apply_state_changeset(state, &mut journaled_state, &mut fork, &AddressSet::default());
3759 assert!(result.is_err());
3760 assert_eq!(fork.db.cache.accounts[&address].account_state, AccountState::NotExisting);
3761 assert_eq!(journaled_state.state[&address].info.balance, U256::from(1));
3762 assert_eq!(
3763 journaled_state.state[&address].storage[&missing_slot].present_value(),
3764 U256::from(7)
3765 );
3766 }
3767
3768 #[tokio::test(flavor = "multi_thread")]
3769 async fn refresh_fork_account_updates_loaded_journals() {
3770 let (api, handle) = spawn(NodeConfig::test()).await;
3771 let target = address!("0x0000000000000000000000000000000000001331");
3772 let code =
3773 Bytes::from_static(&[0x60, 0x2a, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3]);
3774
3775 let provider = handle.http_provider();
3776 let block_number = provider.get_block_number().await.unwrap();
3777 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
3778 evm_opts.fork_url = Some(handle.http_endpoint());
3779 evm_opts.fork_block_number = Some(block_number);
3780 let fork = evm_opts.get_fork(&Config::default(), 31_337, Some(block_number)).unwrap();
3781 let mut backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
3782
3783 let mut journaled_state = JournalInner::new();
3784 journaled_state.load_account(&mut backend, target).unwrap();
3785 journaled_state.state.get_mut(&target).unwrap().info.balance = U256::from(1);
3786 let fork = backend.active_fork_mut().unwrap();
3787 fork.journaled_state.load_account(&mut fork.db, target).unwrap();
3788 fork.journaled_state.state.get_mut(&target).unwrap().info.balance = U256::from(2);
3789 let cached = fork.db.cache.accounts.get_mut(&target).unwrap();
3790 cached.info.balance = U256::from(3);
3791 cached.account_state = AccountState::Touched;
3792 assert_eq!(journaled_state.state[&target].info.code_hash, KECCAK_EMPTY);
3793 assert_eq!(fork.journaled_state.state[&target].info.code_hash, KECCAK_EMPTY);
3794
3795 api.anvil_set_code(target, code.clone()).await.unwrap();
3796 backend.refresh_fork_account(target, ForkAccountField::Code, &mut journaled_state).unwrap();
3797
3798 let expected_hash = keccak256(&code);
3799 let refreshed = &journaled_state.state[&target].info;
3800 assert_eq!(refreshed.code_hash, expected_hash);
3801 assert_eq!(refreshed.code.as_ref().unwrap().original_bytes(), code);
3802 assert_eq!(refreshed.balance, U256::from(1));
3803 let refreshed = &backend.active_fork().unwrap().journaled_state.state[&target].info;
3804 assert_eq!(refreshed.code_hash, expected_hash);
3805 assert_eq!(refreshed.code.as_ref().unwrap().original_bytes(), code);
3806 assert_eq!(refreshed.balance, U256::from(2));
3807 let cached = &backend.active_fork().unwrap().db.cache.accounts[&target];
3808 assert_eq!(cached.info.code_hash, expected_hash);
3809 assert_eq!(cached.info.balance, U256::from(3));
3810 }
3811
3812 #[test]
3813 fn ethereum_replay_skips_unknown_system_envelopes_before_conversion() {
3814 let transaction = |ty| {
3815 let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
3816 hash: B256::ZERO,
3817 inner: UnknownTypedTransaction {
3818 ty: AnyTxType(ty),
3819 fields: Default::default(),
3820 memo: Default::default(),
3821 },
3822 });
3823 AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
3824 inner: Recovered::new_unchecked(unknown, Address::with_last_byte(0x42)),
3825 block_hash: None,
3826 block_number: None,
3827 transaction_index: None,
3828 effective_gas_price: None,
3829 block_timestamp: None,
3830 }))
3831 };
3832
3833 assert!(
3834 Backend::<EthEvmNetwork>::replay_tx_env(&transaction(SYSTEM_TRANSACTION_TYPE))
3835 .unwrap()
3836 .is_none()
3837 );
3838 assert!(Backend::<EthEvmNetwork>::replay_tx_env(&transaction(0xff)).is_err());
3839 }
3840
3841 #[tokio::test(flavor = "multi_thread")]
3842 async fn celo_transaction_hash_fork_replays_transfer_precompile() {
3843 let networks = NetworkConfigs::with_celo();
3844 let (api, handle) = spawn(
3845 NodeConfig::test().with_chain_id(Some(NamedChain::Celo as u64)).with_networks(networks),
3846 )
3847 .await;
3848 let provider = handle.http_provider();
3849 let sender = provider.get_accounts().await.unwrap()[0];
3850 let recipient = Address::with_last_byte(0x99);
3851 let transfer_amount = U256::from(1_000);
3852 let target_amount = U256::from(1);
3853 let nonce = provider.get_transaction_count(sender).await.unwrap();
3854 let gas_price = provider.get_gas_price().await.unwrap();
3855
3856 api.anvil_set_auto_mine(false).await.unwrap();
3857 api.send_transaction(WithOtherFields::new(
3858 TransactionRequest::default()
3859 .with_from(sender)
3860 .with_to(CELO_TRANSFER_ADDRESS)
3861 .with_nonce(nonce)
3862 .with_gas_limit(100_000)
3863 .with_gas_price(gas_price)
3864 .with_input(Bytes::from((sender, recipient, transfer_amount).abi_encode())),
3865 ))
3866 .await
3867 .unwrap();
3868 let target_hash = api
3869 .send_transaction(WithOtherFields::new(
3870 TransactionRequest::default()
3871 .with_from(sender)
3872 .with_to(recipient)
3873 .with_nonce(nonce + 1)
3874 .with_gas_limit(21_000)
3875 .with_gas_price(gas_price)
3876 .with_value(target_amount),
3877 ))
3878 .await
3879 .unwrap();
3880 api.mine_one().await.unwrap();
3881
3882 assert_eq!(provider.get_balance(recipient).await.unwrap(), transfer_amount + target_amount);
3883
3884 let endpoint = handle.http_endpoint();
3885 let fork_block_number = provider.get_block_number().await.unwrap();
3886 let evm_opts = EvmOpts {
3887 fork_url: Some(endpoint.clone()),
3888 fork_block_number: Some(fork_block_number),
3889 networks,
3890 ..Default::default()
3891 };
3892 let fork = CreateFork { url: endpoint, enable_caching: false, evm_opts, resolved: None };
3893 let mut backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
3894 backend.set_networks(networks);
3895
3896 let fork_id = backend.create_fork_at_transaction(fork, target_hash).unwrap();
3897 let fork = backend.inner.get_fork_by_id(fork_id).unwrap();
3898 assert!(matches!(
3899 fork.position,
3900 ForkPosition::BeforeTransaction { transaction_index: 1, .. }
3901 ));
3902 assert_eq!(
3903 fork.db.basic_ref(recipient).unwrap().unwrap_or_default().balance,
3904 transfer_amount
3905 );
3906 }
3907
3908 #[test]
3909 fn fork_position_advances_from_exact_transaction_predecessor() {
3910 let parent_block = BlockNumHash::new(10, B256::with_last_byte(10));
3911 let block = BlockNumHash::new(11, B256::with_last_byte(11));
3912 let parent = ForkPosition::AfterBlock { block: parent_block };
3913 assert_eq!(
3914 parent.after_transaction(block, parent_block.hash, 0, 2),
3915 Some(ForkPosition::BeforeTransaction { block, transaction_index: 1 })
3916 );
3917 assert_eq!(
3918 parent.after_transaction(block, parent_block.hash, 0, 1),
3919 Some(ForkPosition::AfterBlock { block })
3920 );
3921
3922 let before_first = ForkPosition::BeforeTransaction { block, transaction_index: 0 };
3923 assert_eq!(
3924 before_first.after_transaction(block, parent_block.hash, 0, 2),
3925 Some(ForkPosition::BeforeTransaction { block, transaction_index: 1 })
3926 );
3927
3928 let before_second = ForkPosition::BeforeTransaction { block, transaction_index: 1 };
3929 assert_eq!(
3930 before_second.after_transaction(block, parent_block.hash, 1, 3),
3931 Some(ForkPosition::BeforeTransaction { block, transaction_index: 2 })
3932 );
3933 assert_eq!(
3934 before_second.after_transaction(block, parent_block.hash, 1, 2),
3935 Some(ForkPosition::AfterBlock { block })
3936 );
3937
3938 assert_eq!(parent.after_transaction(block, B256::ZERO, 0, 1), None);
3939 assert_eq!(parent.after_transaction(block, parent_block.hash, 1, 2), None);
3940 assert_eq!(before_second.after_transaction(block, parent_block.hash, 0, 3), None);
3941 assert_eq!(before_second.after_transaction(block, parent_block.hash, 2, 3), None);
3942 assert_eq!(before_second.after_transaction(block, parent_block.hash, 1, 1), None);
3943 assert_eq!(parent.after_transaction(block, parent_block.hash, 0, 0), None);
3944 }
3945
3946 #[test]
3947 fn fork_block_env_updates_slot_number() {
3948 let mut evm_env =
3949 EvmEnv::new(revm::context::CfgEnv::<SpecId>::default(), BlockEnv::default());
3950 for slot_number in [Some(42), Some(u64::MAX), None, Some(0)] {
3951 let header = AnyHeader { slot_number, ..Default::default() };
3952 let block = AnyRpcBlock::new(
3953 Block::new(
3954 AnyRpcHeader::from_sealed(header.seal(B256::ZERO)),
3955 BlockTransactions::Full(Vec::new()),
3956 )
3957 .into(),
3958 );
3959 update_env_block::<AnyNetwork, _, _>(
3960 &mut evm_env,
3961 &block,
3962 NamedChain::Mainnet as u64,
3963 NetworkConfigs::default(),
3964 );
3965 assert_eq!(evm_env.block_env.slot_num, slot_number.unwrap_or_default());
3966 }
3967 }
3968
3969 #[test]
3970 fn fork_replay_block_env_preserves_arbitrum_l1_number() {
3971 let header = AnyHeader { number: 75_219_831, ..Default::default() };
3972 let mut block = AnyRpcBlock::new(
3973 Block::new(
3974 AnyRpcHeader::from_sealed(header.seal(B256::ZERO)),
3975 BlockTransactions::Full(Vec::new()),
3976 )
3977 .into(),
3978 );
3979 block.other.insert("l1BlockNumber".to_string(), serde_json::json!("0x10276d3"));
3980 let mut evm_env =
3981 EvmEnv::new(revm::context::CfgEnv::<SpecId>::default(), BlockEnv::default());
3982
3983 update_env_block::<AnyNetwork, _, _>(
3984 &mut evm_env,
3985 &block,
3986 NamedChain::Arbitrum as u64,
3987 NetworkConfigs::default(),
3988 );
3989
3990 assert_eq!(evm_env.block_env.number, U256::from(16_938_707));
3991 }
3992
3993 #[tokio::test(flavor = "multi_thread")]
3994 async fn temporary_backend_preserves_fork_position() {
3995 let (_api, handle) = spawn(NodeConfig::test()).await;
3996 let provider = handle.http_provider();
3997 let block_number = provider.get_block_number().await.unwrap();
3998
3999 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
4000 evm_opts.fork_url = Some(handle.http_endpoint());
4001 evm_opts.fork_block_number = Some(block_number);
4002 let fork = evm_opts.get_fork(&Config::default(), 31_337, Some(block_number)).unwrap();
4003 let mut backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
4004 let id = backend.active_fork_ids.unwrap().0;
4005 let fork_id = backend.inner.ensure_fork_id(id).unwrap().clone();
4006
4007 for position in [
4008 ForkPosition::BeforeTransaction {
4009 block: BlockNumHash::new(block_number + 1, B256::with_last_byte(1)),
4010 transaction_index: 2,
4011 },
4012 ForkPosition::AfterBlock {
4013 block: BlockNumHash::new(block_number + 2, B256::with_last_byte(2)),
4014 },
4015 ] {
4016 backend.inner.get_fork_by_id_mut(id).unwrap().position = position;
4017 let fork = backend.active_fork().unwrap().clone();
4018 let journaled_state = fork.journaled_state.clone();
4019 let mut temporary = Backend::<EthEvmNetwork>::new_with_fork(
4020 &fork_id,
4021 fork,
4022 journaled_state,
4023 NetworkConfigs::default(),
4024 )
4025 .unwrap();
4026
4027 assert_eq!(temporary.active_fork().unwrap().position, position);
4028 let expected = match position {
4029 ForkPosition::AfterBlock { block }
4030 | ForkPosition::BeforeTransaction { block, .. } => block.number,
4031 };
4032 assert_eq!(temporary.active_fork_block_number(), Some(expected));
4033 temporary.fork_block_number_override = Some(expected + 1);
4034 assert_eq!(temporary.active_fork_block_number(), Some(expected + 1));
4035 }
4036 }
4037
4038 #[tokio::test(flavor = "multi_thread")]
4039 #[cfg(feature = "monad")]
4040 async fn fork_factory_boundary_preserves_explicit_execution_overrides() {
4041 async fn pinned_opts(endpoint: String, networks: Option<NetworkConfigs>) -> EvmOpts {
4042 let mut opts = EvmOpts { fork_url: Some(endpoint), ..Default::default() };
4043 if let Some(networks) = networks {
4044 opts.networks = networks;
4045 }
4046 opts.infer_network_from_fork().await.unwrap();
4047 let identity = opts.fork_endpoint.clone().unwrap();
4048 let network_is_inferred = opts.fork_network_is_inferred;
4049 opts.expect_fork_endpoint(identity, network_is_inferred);
4050 opts.pin_fork_block().await.unwrap();
4051 opts
4052 }
4053
4054 fn target_fork(opts: EvmOpts, url: String) -> crate::fork::CreateFork {
4055 crate::fork::CreateFork { url, enable_caching: false, evm_opts: opts, resolved: None }
4056 }
4057
4058 let (ethereum_base_api, ethereum_base) = spawn(NodeConfig::test()).await;
4059 let (_ethereum_target_api, ethereum_target) = spawn(NodeConfig::test()).await;
4060 let (monad_base_api, monad_base) = spawn(NodeConfig::test_monad()).await;
4061 let (_monad_target_api, monad_target) = spawn(NodeConfig::test_monad()).await;
4062 ethereum_base_api.mine_one().await.unwrap();
4063 monad_base_api.mine_one().await.unwrap();
4064
4065 let inferred_ethereum = pinned_opts(ethereum_base.http_endpoint(), None).await;
4066 assert!(inferred_ethereum.fork_network_is_inferred);
4067 let error = Backend::<EthEvmNetwork>::spawn(Some(target_fork(
4068 inferred_ethereum,
4069 monad_target.http_endpoint(),
4070 )))
4071 .unwrap_err();
4072 assert!(
4073 error
4074 .to_string()
4075 .contains("cannot create a `monad` fork with an EVM instantiated for `ethereum`"),
4076 "{error}"
4077 );
4078
4079 let inferred_monad = pinned_opts(monad_base.http_endpoint(), None).await;
4080 assert!(inferred_monad.fork_network_is_inferred);
4081 let error = Backend::<crate::evm::MonadEvmNetwork>::spawn(Some(target_fork(
4082 inferred_monad,
4083 ethereum_target.http_endpoint(),
4084 )))
4085 .unwrap_err();
4086 assert!(
4087 error
4088 .to_string()
4089 .contains("cannot create a `ethereum` fork with an EVM instantiated for `monad`"),
4090 "{error}"
4091 );
4092
4093 let explicit_ethereum =
4094 pinned_opts(ethereum_base.http_endpoint(), Some(NetworkConfigs::with_ethereum())).await;
4095 assert!(!explicit_ethereum.fork_network_is_inferred);
4096 let _backend = Backend::<EthEvmNetwork>::spawn(Some(target_fork(
4097 explicit_ethereum,
4098 monad_target.http_endpoint(),
4099 )))
4100 .unwrap();
4101
4102 let explicit_monad =
4103 pinned_opts(monad_base.http_endpoint(), Some(NetworkConfigs::with_monad())).await;
4104 assert!(!explicit_monad.fork_network_is_inferred);
4105 let _backend = Backend::<crate::evm::MonadEvmNetwork>::spawn(Some(target_fork(
4106 explicit_monad,
4107 ethereum_target.http_endpoint(),
4108 )))
4109 .unwrap();
4110 }
4111
4112 #[tokio::test(flavor = "multi_thread")]
4113 async fn can_read_write_cache() {
4114 let endpoint = &*foundry_test_utils::rpc::next_http_rpc_endpoint();
4115 let provider = get_http_provider(endpoint);
4116
4117 let block_num = provider.get_block_number().await.unwrap();
4118
4119 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
4120 evm_opts.fork_url = Some(endpoint.to_string());
4121 evm_opts.fork_block_number = Some(block_num);
4122
4123 let (evm_env, _, resolved) =
4124 evm_opts.env_resolved::<SpecId, BlockEnv, TxEnv>().await.unwrap();
4125
4126 let fork = evm_opts
4127 .get_fork_resolved(&Config::default(), evm_env.cfg_env.chain_id, resolved.as_ref())
4128 .unwrap();
4129
4130 let resolved = resolved.unwrap();
4131 let fork_hash = resolved.hash();
4132 let source_id = resolved.source_id();
4133 let backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
4134
4135 let address = address!("0x63091244180ae240c87d1f528f5f269134cb07b3");
4137
4138 let num_slots = 5;
4139 let _account = backend.basic_ref(address);
4140 for idx in 0..num_slots {
4141 let _ = backend.storage_ref(address, U256::from(idx));
4142 }
4143 drop(backend);
4144
4145 let meta = BlockchainDbMeta::new(evm_env.block_env, endpoint.to_string())
4146 .with_fork_identity(fork_hash, source_id);
4147
4148 let db = BlockchainDb::new(
4149 meta,
4150 Some(Config::foundry_block_cache_dir(NamedChain::Mainnet, block_num).unwrap()),
4151 );
4152 assert!(db.accounts().read().contains_key(&address));
4153 assert!(db.storage().read().contains_key(&address));
4154 assert_eq!(db.storage().read().get(&address).unwrap().len(), num_slots as usize);
4155 }
4156}