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, Fork as RemoteFork, ForkId, ForkResult, MultiFork},
11 opts::EvmOpts,
12 state_snapshot::StateSnapshots,
13 utils::{
14 apply_chain_and_block_specific_env_changes_for_chain,
15 apply_chain_specific_tx_replay_env_changes_for_chain,
16 },
17};
18use alloy_chains::Chain;
19use alloy_consensus::{BlockHeader, Typed2718};
20use alloy_eips::BlockNumHash;
21use alloy_evm::{Evm, EvmEnv, EvmFactory, precompiles::PrecompilesMap};
22use alloy_genesis::GenesisAccount;
23use alloy_network::{
24 AnyNetwork, AnyRpcBlock, AnyRpcTransaction, BlockResponse, Network, TransactionResponse,
25};
26use alloy_primitives::{Address, B256, ChainId, TxKind, U256, keccak256, map::AddressSet, uint};
27use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions};
28use eyre::Context;
29use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender};
30use foundry_evm_networks::{NetworkConfigs, apply_bsc_p256_precompile};
31pub use foundry_fork_db::{
32 AccountFetchPolicy, BlockchainDb, ForkBlock, ForkBlockEnv, SharedBackend,
33 cache::BlockchainDbMeta,
34};
35use revm::{
36 Database, DatabaseCommit, JournalEntry,
37 bytecode::Bytecode,
38 context::{Block, BlockEnv, CfgEnv, ContextTr, JournalInner, Transaction},
39 context_interface::{journaled_state::account::JournaledAccountTr, result::ResultAndState},
40 database::{AccountState, CacheDB, DatabaseRef, EmptyDB},
41 database_interface::bal::BalState,
42 inspector::NoOpInspector,
43 primitives::{AddressMap, HashMap as Map, Log, hardfork::SpecId},
44 state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
45};
46use std::{
47 collections::{BTreeMap, HashMap},
48 fmt::Debug,
49 time::Instant,
50};
51
52#[cfg(feature = "monad")]
53use crate::evm::monad::BlockContext;
54
55mod diagnostic;
56pub use diagnostic::RevertDiagnostic;
57
58mod bal;
59
60mod error;
61pub use error::{BackendError, BackendResult, DatabaseError, DatabaseResult};
62
63mod cow;
64pub use cow::CowBackend;
65
66mod in_memory_db;
67pub use in_memory_db::{EmptyDBWrapper, FoundryEvmInMemoryDB, MemDb};
68
69mod snapshot;
70pub use snapshot::{BackendStateSnapshot, RevertStateSnapshotAction, StateSnapshot};
71
72type ForkDB<N, B> = CacheDB<SharedBackend<N, B>>;
74
75pub type LocalForkId = U256;
80
81#[cfg(feature = "monad")]
83pub enum ContextUpdate<C> {
84 Unchanged,
86 Replace(C),
88 Rebase,
90}
91
92#[cfg(feature = "monad")]
97pub type ContextUpdateFor<F> = ContextUpdate<<F as FoundryEvmFactory>::Chain>;
98#[cfg(not(feature = "monad"))]
99pub type ContextUpdateFor<F> = std::marker::PhantomData<F>;
100
101type ForkLookupIndex = usize;
104
105struct TransactionInputs<FEN: FoundryEvmNetwork> {
107 evm_env: EvmEnvFor<FEN>,
108 tx_env: TxEnvFor<FEN>,
109 chain_context: ChainFor<FEN>,
110 rpc_block_number: u64,
111 journaled_state: JournaledState,
112}
113
114struct ReplayInputs<FEN: FoundryEvmNetwork> {
116 fork_id: ForkId,
117 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
118 evm_env: EvmEnvFor<FEN>,
119 networks: NetworkConfigs,
120}
121
122struct TransactionForkTarget {
124 fork_block: BlockNumHash,
125 transaction: AnyRpcTransaction,
126 block: AnyRpcBlock,
127 mined: bool,
128 position: Option<TransactionPosition>,
129}
130
131#[derive(Clone, Copy)]
133struct TransactionPosition {
134 index: usize,
135 #[cfg(feature = "monad")]
136 count: usize,
137}
138
139#[cfg(feature = "monad")]
141struct StagedForkRoll<FEN: FoundryEvmNetwork> {
142 local_id: LocalForkId,
143 fork_id: ForkId,
144 fork_index: ForkLookupIndex,
145 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
146}
147
148#[derive(Clone, Copy, Debug, PartialEq, Eq)]
150enum ForkPosition {
151 AfterBlock { block: BlockNumHash },
153 BeforeTransaction { block: BlockNumHash, transaction_index: usize },
155}
156
157impl ForkPosition {
158 fn after_transaction(
161 self,
162 block: BlockNumHash,
163 parent_hash: B256,
164 transaction_index: usize,
165 transaction_count: usize,
166 ) -> Option<Self> {
167 let is_next = match self {
168 Self::AfterBlock { block: previous } => {
169 transaction_index == 0
170 && previous.number.checked_add(1) == Some(block.number)
171 && previous.hash == parent_hash
172 }
173 Self::BeforeTransaction { block: current, transaction_index: current_index } => {
174 current == block && current_index == transaction_index
175 }
176 };
177 if !is_next {
178 return None;
179 }
180 let next_index = transaction_index.checked_add(1)?;
181 if next_index > transaction_count {
182 return None;
183 }
184
185 Some(if next_index == transaction_count {
186 Self::AfterBlock { block }
187 } else {
188 Self::BeforeTransaction { block, transaction_index: next_index }
189 })
190 }
191}
192
193const DEFAULT_PERSISTENT_ACCOUNTS: [Address; 3] =
195 [CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, CALLER];
196
197pub const GLOBAL_FAIL_SLOT: U256 =
202 uint!(0x6661696c65640000000000000000000000000000000000000000000000000000_U256);
203
204pub type JournaledState = JournalInner<JournalEntry>;
205
206#[derive(Clone, Copy, Debug)]
208pub enum ForkAccountField {
209 Balance,
210 Nonce,
211 Code,
212}
213
214impl ForkAccountField {
215 fn update(self, target: &mut AccountInfo, refreshed: &AccountInfo) {
216 match self {
217 Self::Balance => target.balance = refreshed.balance,
218 Self::Nonce => target.nonce = refreshed.nonce,
219 Self::Code => {
220 target.code_hash = refreshed.code_hash();
221 target.code = refreshed.code.clone();
222 }
223 }
224 }
225}
226
227#[auto_impl::auto_impl(&mut)]
229pub trait DatabaseExt<F: FoundryEvmFactory>:
230 Database<Error = DatabaseError> + DatabaseCommit + Debug
231{
232 fn snapshot_state(
238 &mut self,
239 journaled_state: &JournaledState,
240 evm_env: &EvmEnv<F::Spec, F::BlockEnv>,
241 ) -> U256;
242
243 fn revert_state(
256 &mut self,
257 id: U256,
258 journaled_state: &JournaledState,
259 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
260 caller: Address,
261 action: RevertStateSnapshotAction,
262 ) -> Option<JournaledState>;
263
264 fn delete_state_snapshot(&mut self, id: U256) -> bool;
269
270 fn delete_state_snapshots(&mut self);
272
273 fn create_select_fork(
277 &mut self,
278 fork: CreateFork,
279 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
280 tx_env: &mut F::Tx,
281 journaled_state: &mut JournaledState,
282 ) -> eyre::Result<(LocalForkId, ContextUpdateFor<F>)> {
283 let id = self.create_fork(fork)?;
284 let context = self.select_fork(id, evm_env, tx_env, journaled_state)?;
285 Ok((id, context))
286 }
287
288 fn create_select_fork_at_transaction(
292 &mut self,
293 fork: CreateFork,
294 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
295 tx_env: &mut F::Tx,
296 journaled_state: &mut JournaledState,
297 transaction: B256,
298 ) -> eyre::Result<(LocalForkId, ContextUpdateFor<F>)> {
299 let id = self.create_fork_at_transaction(fork, transaction)?;
300 let context = self.select_fork(id, evm_env, tx_env, journaled_state)?;
301 Ok((id, context))
302 }
303
304 fn create_fork(&mut self, fork: CreateFork) -> eyre::Result<LocalForkId>;
306
307 fn create_fork_at_transaction(
309 &mut self,
310 fork: CreateFork,
311 transaction: B256,
312 ) -> eyre::Result<LocalForkId>;
313
314 fn select_fork(
324 &mut self,
325 id: LocalForkId,
326 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
327 tx_env: &mut F::Tx,
328 journaled_state: &mut JournaledState,
329 ) -> eyre::Result<ContextUpdateFor<F>>;
330
331 fn roll_fork(
339 &mut self,
340 id: Option<LocalForkId>,
341 block_number: u64,
342 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
343 tx_env: &F::Tx,
344 journaled_state: &mut JournaledState,
345 ) -> eyre::Result<ContextUpdateFor<F>>;
346
347 fn roll_fork_to_transaction(
356 &mut self,
357 id: Option<LocalForkId>,
358 transaction: B256,
359 evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
360 tx_env: &F::Tx,
361 journaled_state: &mut JournaledState,
362 ) -> eyre::Result<ContextUpdateFor<F>>;
363
364 fn transact(
366 &mut self,
367 id: Option<LocalForkId>,
368 transaction: B256,
369 evm_env: EvmEnv<F::Spec, F::BlockEnv>,
370 outer_tx_env: &F::Tx,
371 journaled_state: &mut JournaledState,
372 inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
373 ) -> eyre::Result<ContextUpdateFor<F>>;
374
375 fn transact_from_tx(
377 &mut self,
378 tx_env: F::Tx,
379 evm_env: EvmEnv<F::Spec, F::BlockEnv>,
380 journaled_state: &mut JournaledState,
381 inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
382 ) -> eyre::Result<()>;
383
384 fn chain_context_for_synthetic_transaction(&self, tx: &F::Tx) -> eyre::Result<F::Chain> {
386 Ok(F::Chain::for_transaction(tx))
387 }
388
389 fn active_fork_id(&self) -> Option<LocalForkId>;
391
392 fn active_fork_url(&self) -> Option<String>;
394
395 fn active_fork_options(&self) -> Option<CreateFork>;
397
398 fn active_fork_source_chain_id(&self) -> Option<u64>;
400
401 fn active_fork_block_number(&self) -> Option<u64> {
403 None
404 }
405
406 fn is_forked_mode(&self) -> bool {
408 self.active_fork_id().is_some()
409 }
410
411 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId>;
422
423 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId>;
425
426 fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic>;
453
454 fn load_allocs(
458 &mut self,
459 allocs: &BTreeMap<Address, GenesisAccount>,
460 journaled_state: &mut JournaledState,
461 ) -> Result<(), BackendError>;
462
463 fn clone_account(
468 &mut self,
469 source: &GenesisAccount,
470 target: &Address,
471 journaled_state: &mut JournaledState,
472 ) -> Result<(), BackendError>;
473
474 fn is_persistent(&self, acc: &Address) -> bool;
476
477 fn refresh_fork_account(
480 &mut self,
481 address: Address,
482 field: ForkAccountField,
483 journaled_state: &mut JournaledState,
484 ) -> Result<(), BackendError>;
485
486 fn refresh_fork_storage(
488 &mut self,
489 address: Address,
490 slot: U256,
491 journaled_state: &mut JournaledState,
492 ) -> Result<(), BackendError>;
493
494 fn remove_persistent_account(&mut self, account: &Address) -> bool;
496
497 fn add_persistent_account(&mut self, account: Address) -> bool;
499
500 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
502 fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
503 where
504 Self: Sized,
505 {
506 for acc in accounts {
507 self.remove_persistent_account(&acc);
508 }
509 }
510
511 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
513 fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
514 where
515 Self: Sized,
516 {
517 for acc in accounts {
518 self.add_persistent_account(acc);
519 }
520 }
521
522 fn allow_cheatcode_access(&mut self, account: Address) -> bool;
526
527 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool;
531
532 fn has_cheatcode_access(&self, account: &Address) -> bool;
534
535 fn ensure_cheatcode_access(&self, account: &Address) -> Result<(), BackendError> {
539 if !self.has_cheatcode_access(account) {
540 return Err(BackendError::NoCheats(*account));
541 }
542 Ok(())
543 }
544
545 fn ensure_cheatcode_access_forking_mode(&self, account: &Address) -> Result<(), BackendError> {
548 if self.is_forked_mode() {
549 return self.ensure_cheatcode_access(account);
550 }
551 Ok(())
552 }
553
554 fn set_blockhash(&mut self, block_number: U256, block_hash: B256);
569}
570
571#[must_use]
624pub struct Backend<FEN: FoundryEvmNetwork = EthEvmNetwork> {
625 networks: NetworkConfigs,
630 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
632 mem_db: FoundryEvmInMemoryDB,
634 fork_init_journaled_state: JournaledState,
651 active_fork_ids: Option<(LocalForkId, ForkLookupIndex)>,
655 fork_block_number_override: Option<u64>,
657 bal: Option<BalState>,
659 inner: BackendInner<FEN>,
661}
662
663impl<FEN: FoundryEvmNetwork> Clone for Backend<FEN> {
664 fn clone(&self) -> Self {
665 Self {
666 networks: self.networks,
667 forks: self.forks.clone(),
668 mem_db: self.mem_db.clone(),
669 fork_init_journaled_state: self.fork_init_journaled_state.clone(),
670 active_fork_ids: self.active_fork_ids,
671 fork_block_number_override: self.fork_block_number_override,
672 bal: self.bal.clone(),
673 inner: self.inner.clone(),
674 }
675 }
676}
677
678impl<FEN: FoundryEvmNetwork> Debug for Backend<FEN> {
679 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
680 f.debug_struct("Backend")
681 .field("networks", &self.networks)
682 .field("forks", &self.forks)
683 .field("mem_db", &self.mem_db)
684 .field("fork_init_journaled_state", &self.fork_init_journaled_state)
685 .field("active_fork_ids", &self.active_fork_ids)
686 .field("bal", &self.bal)
687 .field("inner", &self.inner)
688 .finish()
689 }
690}
691
692impl<FEN: FoundryEvmNetwork> Backend<FEN> {
693 pub fn spawn(fork: Option<CreateFork>) -> eyre::Result<Self> {
698 Self::new(MultiFork::<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>::spawn(), fork)
699 }
700
701 pub fn clone_with_fork_scope(&self) -> eyre::Result<Self> {
705 let mut backend = self.clone();
706 backend.forks = self.forks.scoped()?;
707 Ok(backend)
708 }
709
710 pub fn fork(&self) -> eyre::Result<Option<RemoteFork>> {
712 let Some(id) = self.active_fork_id() else { return Ok(None) };
713 let id = self.inner.issued_local_fork_ids.get(&id).expect("active fork is registered");
714 self.forks.get_fork_info(id.clone())
715 }
716
717 pub async fn env(&self, opts: &EvmOpts) -> eyre::Result<(EvmEnvFor<FEN>, TxEnvFor<FEN>)> {
719 opts.env_at_fork(self.fork()?.as_ref()).await
720 }
721
722 pub async fn can_use_create2_deployer(&self, deployer: Address) -> eyre::Result<bool> {
724 let Some(fork) = self.fork()? else {
725 return Ok(deployer == DEFAULT_CREATE2_DEPLOYER);
726 };
727 let mut request = self
728 .active_fork_options()
729 .ok_or_else(|| eyre::eyre!("active fork configuration is unavailable"))?;
730 request.evm_opts.create2_deployer = deployer;
731 request.evm_opts.can_use_create2_deployer_at(Some(&fork)).await
732 }
733
734 pub async fn transaction_count(&self, sender: Address) -> eyre::Result<u64> {
736 let request =
737 self.active_fork_options().ok_or_else(|| eyre::eyre!("backend is not forked"))?;
738 let fork = self.fork()?.expect("active backend owns a fork");
739 request.evm_opts.transaction_count_at_fork(sender, &fork).await
740 }
741
742 pub fn replace_fork(&mut self, fork: Option<CreateFork>) -> eyre::Result<()> {
744 *self = Self::new(self.forks.clone(), fork)?;
745 Ok(())
746 }
747
748 pub fn new(
755 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
756 fork: Option<CreateFork>,
757 ) -> eyre::Result<Self> {
758 trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend");
759 let persistent_accounts = AddressSet::from_iter(DEFAULT_PERSISTENT_ACCOUNTS);
761 let inner = BackendInner { persistent_accounts, ..Default::default() };
762
763 let mut backend = Self {
764 networks: NetworkConfigs::default(),
765 forks,
766 mem_db: CacheDB::new(Default::default()),
767 fork_init_journaled_state: inner.new_journaled_state(),
768 active_fork_ids: None,
769 fork_block_number_override: None,
770 bal: None,
771 inner,
772 };
773
774 if let Some(fork) = fork {
775 let ForkResult { id: fork_id, backend: fork, fork: resolved, .. } =
776 backend.forks.create_fork(fork)?;
777 let context = resolved.context();
778 let block = resolved.block();
779 let fork_db = ForkDB::new(fork);
780 let fork_ids = backend.inner.insert_new_fork(
781 fork_id.clone(),
782 block,
783 context.source_chain_id,
784 fork_db,
785 backend.inner.new_journaled_state(),
786 );
787 backend.inner.launched_with_fork = Some((fork_id, fork_ids.0, fork_ids.1));
788 backend.active_fork_ids = Some(fork_ids);
789 }
790
791 trace!(target: "backend", forking_mode=? backend.active_fork_ids.is_some(), "created executor backend");
792
793 Ok(backend)
794 }
795
796 pub(crate) fn new_with_fork(
799 id: &ForkId,
800 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
801 journaled_state: JournaledState,
802 networks: NetworkConfigs,
803 ) -> eyre::Result<Self> {
804 Self::new_with_fork_manager(
805 MultiFork::<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>::spawn(),
806 id,
807 fork,
808 journaled_state,
809 networks,
810 )
811 }
812
813 fn new_with_fork_manager(
814 forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
815 id: &ForkId,
816 mut fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
817 journaled_state: JournaledState,
818 networks: NetworkConfigs,
819 ) -> eyre::Result<Self> {
820 let mut backend = Self::new(forks, None)?;
821 backend.networks = networks;
822 fork.journaled_state = journaled_state;
823 let fork_ids = backend.inner.insert_fork(id.clone(), fork);
824 backend.inner.launched_with_fork = Some((id.clone(), fork_ids.0, fork_ids.1));
825 backend.active_fork_ids = Some(fork_ids);
826 Ok(backend)
827 }
828
829 pub fn clone_empty(&self) -> Self {
831 Self {
832 networks: self.networks,
833 forks: self.forks.clone(),
834 mem_db: CacheDB::new(Default::default()),
835 fork_init_journaled_state: self.inner.new_journaled_state(),
836 active_fork_ids: None,
837 fork_block_number_override: None,
838 bal: None,
839 inner: Default::default(),
840 }
841 }
842
843 pub const fn networks(&self) -> NetworkConfigs {
845 self.networks
846 }
847
848 pub const fn set_networks(&mut self, networks: NetworkConfigs) {
850 self.networks = networks;
851 }
852
853 pub const fn set_fork_block_number_override(&mut self, block_number: u64) {
856 self.fork_block_number_override = Some(block_number);
857 }
858
859 pub fn insert_account_info(&mut self, address: Address, account: AccountInfo) {
860 if let Some(db) = self.active_fork_db_mut() {
861 db.insert_account_info(address, account)
862 } else {
863 self.mem_db.insert_account_info(address, account)
864 }
865 }
866
867 pub fn insert_account_storage(
869 &mut self,
870 address: Address,
871 slot: U256,
872 value: U256,
873 ) -> Result<(), DatabaseError> {
874 if let Some(db) = self.active_fork_db_mut() {
875 db.insert_account_storage(address, slot, value)
876 } else {
877 self.mem_db.insert_account_storage(address, slot, value)
878 }
879 }
880
881 pub fn replace_account_storage(
886 &mut self,
887 address: Address,
888 storage: Map<U256, U256>,
889 ) -> Result<(), DatabaseError> {
890 if let Some(db) = self.active_fork_db_mut() {
891 db.replace_account_storage(address, storage.into_iter().collect())
892 } else {
893 self.mem_db.replace_account_storage(address, storage.into_iter().collect())
894 }
895 }
896
897 #[allow(clippy::type_complexity)]
899 pub const fn state_snapshots(
900 &self,
901 ) -> &StateSnapshots<
902 BackendStateSnapshot<
903 BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
904 SpecFor<FEN>,
905 BlockEnvFor<FEN>,
906 >,
907 > {
908 &self.inner.state_snapshots
909 }
910
911 pub fn set_test_contract(&mut self, acc: Address) -> &mut Self {
918 trace!(?acc, "setting test account");
919 self.inner.persistent_accounts.insert(acc);
920 self.inner.cheatcode_access_accounts.insert(acc);
921 self
922 }
923
924 pub fn set_caller(&mut self, acc: Address) -> &mut Self {
926 trace!(?acc, "setting caller account");
927 self.inner.caller = Some(acc);
928 self.inner.cheatcode_access_accounts.insert(acc);
929 self
930 }
931
932 pub fn set_spec_id(&mut self, spec_id: impl Into<SpecFor<FEN>>) -> &mut Self {
934 self.inner.spec_id = spec_id.into();
935 self
936 }
937
938 pub const fn caller_address(&self) -> Option<Address> {
940 self.inner.caller
941 }
942
943 pub const fn has_state_snapshot_failure(&self) -> bool {
949 self.inner.has_state_snapshot_failure
950 }
951
952 pub const fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) {
954 self.inner.has_state_snapshot_failure = has_state_snapshot_failure
955 }
956
957 pub(crate) fn update_fork_db(
959 &self,
960 active_journaled_state: &mut JournaledState,
961 target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
962 ) {
963 self.update_fork_db_contracts(
964 self.inner.persistent_accounts.iter().copied(),
965 active_journaled_state,
966 target_fork,
967 )
968 }
969
970 pub(crate) fn update_fork_db_contracts(
972 &self,
973 accounts: impl IntoIterator<Item = Address>,
974 active_journaled_state: &mut JournaledState,
975 target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
976 ) {
977 if let Some(db) = self.active_fork_db() {
978 merge_account_data(accounts, db, active_journaled_state, target_fork)
979 } else {
980 merge_account_data(accounts, &self.mem_db, active_journaled_state, target_fork)
981 }
982 }
983
984 pub const fn mem_db(&self) -> &FoundryEvmInMemoryDB {
986 &self.mem_db
987 }
988
989 pub fn is_active_fork(&self, id: LocalForkId) -> bool {
991 self.active_fork_ids.is_some_and(|(i, _)| i == id)
992 }
993
994 pub fn is_in_forking_mode(&self) -> bool {
996 self.active_fork().is_some()
997 }
998
999 pub fn active_fork(&self) -> Option<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
1001 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx))
1002 }
1003
1004 pub fn active_fork_mut(&mut self) -> Option<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
1006 self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx))
1007 }
1008
1009 pub fn active_fork_db(&self) -> Option<&ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
1011 self.active_fork().map(|f| &f.db)
1012 }
1013
1014 pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
1016 self.active_fork_mut().map(|f| &mut f.db)
1017 }
1018
1019 pub fn db(&self) -> &dyn Database<Error = DatabaseError> {
1021 match self.active_fork_db() {
1022 Some(fork_db) => fork_db,
1023 None => &self.mem_db,
1024 }
1025 }
1026
1027 pub fn db_mut(&mut self) -> &mut dyn Database<Error = DatabaseError> {
1029 match self.active_fork_ids.map(|(_, idx)| &mut self.inner.get_fork_mut(idx).db) {
1030 Some(fork_db) => fork_db,
1031 None => &mut self.mem_db,
1032 }
1033 }
1034
1035 pub(crate) fn create_db_snapshot(
1037 &self,
1038 ) -> BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>> {
1039 if let Some((id, idx)) = self.active_fork_ids {
1040 let fork = self.inner.get_fork(idx).clone();
1041 let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed");
1042 BackendDatabaseSnapshot::Forked(id, fork_id, idx, Box::new(fork))
1043 } else {
1044 BackendDatabaseSnapshot::InMemory(self.mem_db.clone())
1045 }
1046 }
1047
1048 pub fn merged_logs(&self, mut logs: Vec<Log>) -> Vec<Log> {
1050 if let Some((_, active)) = self.active_fork_ids {
1051 let mut all_logs = Vec::with_capacity(logs.len());
1052
1053 self.inner
1054 .forks
1055 .iter()
1056 .enumerate()
1057 .filter_map(|(idx, f)| f.as_ref().map(|f| (idx, f)))
1058 .for_each(|(idx, f)| {
1059 if idx == active {
1060 all_logs.append(&mut logs);
1061 } else {
1062 all_logs.extend(f.journaled_state.logs.clone())
1063 }
1064 });
1065 return all_logs;
1066 }
1067
1068 logs
1069 }
1070
1071 pub(crate) fn initialize(
1075 &mut self,
1076 spec_id: impl Into<SpecFor<FEN>>,
1077 caller: Address,
1078 tx_kind: TxKind,
1079 ) {
1080 self.set_caller(caller);
1081 self.set_spec_id(spec_id);
1082
1083 let test_contract = match tx_kind {
1084 TxKind::Call(to) => to,
1085 TxKind::Create => {
1086 let nonce =
1087 self.basic_ref(caller).map(|b| b.unwrap_or_default().nonce).unwrap_or_default();
1088 caller.create(nonce)
1089 }
1090 };
1091 self.set_test_contract(test_contract);
1092 }
1093
1094 #[instrument(name = "inspect", level = "debug", skip_all)]
1099 pub fn inspect<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
1100 &mut self,
1101 evm_env: &mut EvmEnvFor<FEN>,
1102 tx_env: &mut TxEnvFor<FEN>,
1103 inspector: I,
1104 ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
1105 let chain_context = self.chain_context_for_synthetic_transaction(tx_env)?;
1106 self.inspect_with_context(evm_env, tx_env, chain_context, inspector)
1107 }
1108
1109 #[instrument(name = "inspect", level = "debug", skip_all)]
1111 pub fn inspect_with_context<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
1112 &mut self,
1113 evm_env: &mut EvmEnvFor<FEN>,
1114 tx_env: &mut TxEnvFor<FEN>,
1115 chain_context: ChainFor<FEN>,
1116 inspector: I,
1117 ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
1118 self.initialize(evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind());
1119 let factory = FEN::EvmFactory::default();
1120 let mut evm =
1121 factory.create_foundry_evm_with_inspector(self, evm_env.to_owned(), inspector);
1122 *evm.chain_mut() = chain_context;
1123 let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?;
1124
1125 *tx_env = evm.tx().clone();
1126 *evm_env = evm.finish().1;
1127
1128 Ok(res)
1129 }
1130
1131 pub fn is_existing_precompile(&self, addr: &Address) -> bool {
1133 self.inner.precompile_addresses().contains(addr)
1134 }
1135
1136 #[inline]
1138 fn set_init_journaled_state(&mut self, journaled_state: JournaledState) {
1139 trace!("recording fork init journaled_state");
1140 self.fork_init_journaled_state = journaled_state;
1141 }
1142
1143 fn prepare_init_journal_state(&mut self) -> Result<(), BackendError> {
1153 let loaded_accounts = self
1154 .fork_init_journaled_state
1155 .state
1156 .iter()
1157 .filter(|(addr, _)| {
1158 !self.is_existing_precompile(addr)
1159 && !self.inner.persistent_accounts.contains(*addr)
1160 })
1161 .map(|(addr, _)| addr)
1162 .copied()
1163 .collect::<Vec<_>>();
1164
1165 for fork in self.inner.forks_iter_mut() {
1166 let mut journaled_state = self.fork_init_journaled_state.clone();
1167 for loaded_account in loaded_accounts.iter().copied() {
1168 trace!(?loaded_account, "replacing account on init");
1169 let init_account =
1170 journaled_state.state.get_mut(&loaded_account).expect("exists; qed");
1171
1172 if init_account.is_created() {
1176 trace!(?loaded_account, "skipping created account");
1177 continue;
1178 }
1179
1180 let fork_account = Database::basic(&mut fork.db, loaded_account)?
1183 .ok_or(BackendError::MissingAccount(loaded_account))?;
1184 init_account.info = fork_account;
1185 }
1186 fork.journaled_state = journaled_state;
1187 }
1188 Ok(())
1189 }
1190
1191 fn get_block_number_and_block_for_transaction(
1193 &self,
1194 id: LocalForkId,
1195 transaction: B256,
1196 ) -> eyre::Result<TransactionForkTarget> {
1197 let fork = self.inner.get_fork_by_id(id)?;
1198 let tx = fork.backend().get_transaction(transaction)?;
1199
1200 if let Some(tx_block) = tx.block_number() {
1202 let tx_block_hash = tx
1203 .block_hash()
1204 .ok_or_else(|| eyre::eyre!("mined transaction is missing its block hash"))?;
1205 let block = fork.backend().get_full_block(tx_block_hash)?;
1206 eyre::ensure!(
1207 block.header().number() == tx_block && block.header().hash == tx_block_hash,
1208 "transaction block changed: expected {} ({}), got {} ({})",
1209 tx_block,
1210 tx_block_hash,
1211 block.header().number(),
1212 block.header().hash
1213 );
1214 let position = if let BlockTransactions::Full(transactions) = block.transactions() {
1215 let index = transactions.iter().position(|tx| tx.tx_hash() == transaction);
1216 if self.networks.is_monad() && index.is_none() {
1217 eyre::bail!(
1218 "transaction {transaction:?} is missing from block {}",
1219 block.header().number()
1220 );
1221 }
1222 index.map(|index| TransactionPosition {
1223 index,
1224 #[cfg(feature = "monad")]
1225 count: transactions.len(),
1226 })
1227 } else {
1228 if self.networks.is_monad() {
1229 eyre::bail!(
1230 "block {} does not contain full transactions",
1231 block.header().number()
1232 );
1233 }
1234 None
1235 };
1236
1237 let fork_block = BlockNumHash::new(
1240 tx_block.checked_sub(1).ok_or_else(|| {
1241 eyre::eyre!("cannot replay a transaction in the genesis block")
1242 })?,
1243 block.header().parent_hash(),
1244 );
1245 Ok(TransactionForkTarget { fork_block, transaction: tx, block, mined: true, position })
1246 } else {
1247 if self.networks.is_monad() {
1248 eyre::bail!(
1249 "transaction {transaction} is pending and has no canonical block context"
1250 );
1251 }
1252 let block = fork.backend().get_full_block(BlockNumberOrTag::Latest)?;
1253
1254 let fork_block = BlockNumHash::new(block.header().number(), block.header().hash);
1255
1256 Ok(TransactionForkTarget {
1257 fork_block,
1258 transaction: tx,
1259 block,
1260 mined: false,
1261 position: None,
1262 })
1263 }
1264 }
1265
1266 #[cfg(feature = "monad")]
1268 fn full_block_tx_envs(block: &AnyRpcBlock) -> eyre::Result<Vec<TxEnvFor<FEN>>> {
1269 let BlockTransactions::Full(transactions) = block.transactions() else {
1270 eyre::bail!("block {} does not contain full transactions", block.header().number());
1271 };
1272 transactions.iter().map(TxEnvFor::<FEN>::from_any_rpc_transaction).collect()
1273 }
1274
1275 fn replay_tx_env(tx: &AnyRpcTransaction) -> eyre::Result<Option<TxEnvFor<FEN>>> {
1277 let is_system = is_known_system_sender(tx.from()) || tx.ty() == SYSTEM_TRANSACTION_TYPE;
1278 if is_system {
1279 return Ok(TxEnvFor::<FEN>::from_any_rpc_transaction(tx).ok());
1280 }
1281
1282 TxEnvFor::<FEN>::from_any_rpc_transaction(tx).map(Some)
1283 }
1284
1285 #[cfg(feature = "monad")]
1287 fn block_context_inputs_from_backend(
1288 backend: &SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
1289 block: &AnyRpcBlock,
1290 ) -> eyre::Result<BlockContext<FEN>> {
1291 let current = Self::full_block_tx_envs(block)?;
1292
1293 let parent_hash = block.header().parent_hash();
1294 let parent_block = if parent_hash.is_zero() {
1295 None
1296 } else {
1297 let parent_number = block.header().number().checked_sub(1).ok_or_else(|| {
1298 eyre::eyre!("genesis block has non-zero parent hash {parent_hash}")
1299 })?;
1300 let parent = backend
1301 .get_full_block(parent_hash)
1302 .wrap_err_with(|| format!("failed to fetch parent block {parent_hash}"))?;
1303 ensure_block_identity(
1304 &parent,
1305 BlockNumHash::new(parent_number, parent_hash),
1306 "parent",
1307 )?;
1308 Some(parent)
1309 };
1310 let parent =
1311 parent_block.as_ref().map(Self::full_block_tx_envs).transpose()?.unwrap_or_default();
1312
1313 let grandparent = if let Some(parent_block) = &parent_block {
1314 let grandparent_hash = parent_block.header().parent_hash();
1315 if grandparent_hash.is_zero() {
1316 Vec::new()
1317 } else {
1318 let block = backend.get_full_block(grandparent_hash).wrap_err_with(|| {
1319 format!("failed to fetch grandparent block {grandparent_hash}")
1320 })?;
1321 let grandparent_number =
1322 parent_block.header().number().checked_sub(1).ok_or_else(|| {
1323 eyre::eyre!("genesis block has non-zero parent hash {grandparent_hash}")
1324 })?;
1325 ensure_block_identity(
1326 &block,
1327 BlockNumHash::new(grandparent_number, grandparent_hash),
1328 "grandparent",
1329 )?;
1330 Self::full_block_tx_envs(&block)?
1331 }
1332 } else {
1333 Vec::new()
1334 };
1335
1336 Ok(BlockContext::new(grandparent, parent, current))
1337 }
1338
1339 #[cfg(feature = "monad")]
1341 fn block_context_inputs(
1342 &self,
1343 id: LocalForkId,
1344 block: &AnyRpcBlock,
1345 ) -> eyre::Result<BlockContext<FEN>> {
1346 let fork = self.inner.get_fork_by_id(id)?;
1347 Self::block_context_inputs_from_backend(fork.backend(), block)
1348 }
1349
1350 #[cfg(feature = "monad")]
1352 fn context_for_block_position(
1353 block_context: BlockContext<FEN>,
1354 position: ForkPosition,
1355 tx: &TxEnvFor<FEN>,
1356 ) -> eyre::Result<ChainFor<FEN>> {
1357 let cursor = match position {
1358 ForkPosition::AfterBlock { .. } => block_context.into_child(),
1359 ForkPosition::BeforeTransaction { transaction_index, .. } => {
1360 block_context.before_transaction(transaction_index)?
1361 }
1362 };
1363 Ok(cursor.next_transaction(tx))
1364 }
1365
1366 #[cfg(feature = "monad")]
1368 fn context_for_fork_synthetic_transaction(
1369 &self,
1370 id: LocalForkId,
1371 tx: &TxEnvFor<FEN>,
1372 ) -> eyre::Result<ChainFor<FEN>> {
1373 if !self.networks.is_monad() {
1374 return Ok(ChainFor::<FEN>::for_transaction(tx));
1375 }
1376
1377 let fork = self.inner.get_fork_by_id(id)?;
1378 let (position_block, position) = match fork.position {
1379 position @ (ForkPosition::AfterBlock { block }
1380 | ForkPosition::BeforeTransaction { block, .. }) => (block, position),
1381 };
1382 let block = fork.backend().get_full_block(position_block.hash).wrap_err_with(|| {
1383 format!(
1384 "failed to fetch fork block {} ({})",
1385 position_block.number, position_block.hash
1386 )
1387 })?;
1388 ensure_block_identity(&block, position_block, "fork")?;
1389 let context = Self::block_context_inputs_from_backend(fork.backend(), &block)?;
1390 Self::context_for_block_position(context, position, tx)
1391 }
1392
1393 #[cfg(feature = "monad")]
1395 pub fn block_context_for_synthetic_transaction(
1396 &self,
1397 ) -> eyre::Result<Option<BlockContext<FEN>>> {
1398 if !self.networks.is_monad() {
1399 return Ok(None);
1400 }
1401 let Some(id) = self.active_fork_id() else {
1402 return Ok(None);
1403 };
1404
1405 let fork = self.inner.get_fork_by_id(id)?;
1406 let (position_block, transaction_index) = match fork.position {
1407 ForkPosition::AfterBlock { block } => (block, None),
1408 ForkPosition::BeforeTransaction { block, transaction_index } => {
1409 (block, Some(transaction_index))
1410 }
1411 };
1412 let block = fork.backend().get_full_block(position_block.hash).wrap_err_with(|| {
1413 format!(
1414 "failed to fetch active fork block {} ({})",
1415 position_block.number, position_block.hash
1416 )
1417 })?;
1418 ensure_block_identity(&block, position_block, "active fork")?;
1419 let context = Self::block_context_inputs_from_backend(fork.backend(), &block)?;
1420
1421 match transaction_index {
1422 Some(index) => context.before_transaction(index).map(Some),
1423 None => Ok(Some(context.into_child())),
1424 }
1425 }
1426
1427 fn apply_fork_tx_replay_env_changes(
1429 &self,
1430 id: LocalForkId,
1431 evm_env: &mut EvmEnvFor<FEN>,
1432 ) -> eyre::Result<()> {
1433 let fork_id = self.inner.ensure_fork_id(id).cloned()?;
1434 let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
1435 self.apply_fork_tx_replay_env_changes_for(&fork_id, source_chain_id, evm_env)
1436 }
1437
1438 fn apply_fork_tx_replay_env_changes_for(
1440 &self,
1441 fork_id: &ForkId,
1442 source_chain_id: ChainId,
1443 evm_env: &mut EvmEnvFor<FEN>,
1444 ) -> eyre::Result<()> {
1445 let fork_evm_env = self
1446 .forks
1447 .get_evm_env(fork_id.clone())?
1448 .ok_or_else(|| eyre::eyre!("Requested fork `{fork_id}` does not exist"))?;
1449 evm_env.cfg_env.chain_id = fork_evm_env.cfg_env.chain_id;
1450 apply_chain_specific_tx_replay_env_changes_for_chain(evm_env, source_chain_id);
1451 Ok(())
1452 }
1453
1454 fn populate_rolled_active_fork(
1456 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1457 persistent_accounts: &AddressSet,
1458 caller: Option<Address>,
1459 journaled_state: &mut JournaledState,
1460 ) {
1461 let mut persistent_addrs = persistent_accounts.clone();
1462 persistent_addrs.extend(caller);
1463
1464 fork.journaled_state.depth = journaled_state.depth;
1465
1466 for addr in persistent_addrs {
1467 merge_journaled_state_data(addr, journaled_state, &mut fork.journaled_state);
1468 }
1469
1470 for (addr, acc) in &journaled_state.state {
1471 if acc.is_created() && acc.is_touched() {
1472 merge_journaled_state_data(*addr, journaled_state, &mut fork.journaled_state);
1473 } else if !acc.is_created() {
1474 let _ = fork.journaled_state.load_account(&mut fork.db, *addr);
1475 }
1476 }
1477
1478 *journaled_state = fork.journaled_state.clone();
1479 }
1480
1481 fn reset_rolled_active_fork(
1483 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1484 fork_init_journaled_state: &JournaledState,
1485 persistent_accounts: &AddressSet,
1486 caller: Option<Address>,
1487 journaled_state: &mut JournaledState,
1488 ) {
1489 fork.journaled_state = fork_init_journaled_state.clone();
1490 Self::populate_rolled_active_fork(fork, persistent_accounts, caller, journaled_state);
1491 }
1492
1493 fn reset_rolled_inactive_fork(
1500 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1501 fork_init_journaled_state: &JournaledState,
1502 ) {
1503 let rolled =
1504 std::mem::replace(&mut fork.journaled_state, fork_init_journaled_state.clone());
1505 for (addr, acc) in &rolled.state {
1506 if acc.is_created() && acc.is_touched() {
1507 merge_journaled_state_data(*addr, &rolled, &mut fork.journaled_state);
1508 }
1509 }
1510 }
1511
1512 fn roll_fork_with_context(
1514 &mut self,
1515 id: Option<LocalForkId>,
1516 block_number: u64,
1517 evm_env: &mut EvmEnvFor<FEN>,
1518 tx_env: Option<&TxEnvFor<FEN>>,
1519 journaled_state: &mut JournaledState,
1520 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1521 trace!(?id, ?block_number, "roll fork");
1522 let id = self.ensure_fork(id)?;
1523 let rolled = self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?;
1524 self.apply_rolled_fork_with_context(id, rolled, evm_env, tx_env, journaled_state)
1525 }
1526
1527 fn roll_fork_exact_with_context(
1528 &mut self,
1529 id: LocalForkId,
1530 block: BlockNumHash,
1531 prewarm_bal: bool,
1532 evm_env: &mut EvmEnvFor<FEN>,
1533 tx_env: Option<&TxEnvFor<FEN>>,
1534 journaled_state: &mut JournaledState,
1535 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1536 trace!(?id, ?block, "roll fork to exact block");
1537 let rolled = self.forks.roll_fork_exact_with_bal(
1538 self.inner.ensure_fork_id(id).cloned()?,
1539 block,
1540 prewarm_bal,
1541 )?;
1542 self.apply_rolled_fork_with_context(id, rolled, evm_env, tx_env, journaled_state)
1543 }
1544
1545 fn apply_rolled_fork_with_context(
1546 &mut self,
1547 id: LocalForkId,
1548 rolled: ForkResult<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
1549 evm_env: &mut EvmEnvFor<FEN>,
1550 _tx_env: Option<&TxEnvFor<FEN>>,
1551 journaled_state: &mut JournaledState,
1552 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1553 let ForkResult { id: fork_id, backend, env: fork_env, fork: resolved } = rolled;
1554 let context = resolved.context();
1555 let block = resolved.block();
1556 let _affects_active = self.is_active_fork(id);
1557
1558 #[cfg(feature = "monad")]
1559 let context_update = if _affects_active
1560 && self.networks.is_monad()
1561 && let Some(tx) = _tx_env
1562 {
1563 let block_data = backend.get_full_block(block.hash).wrap_err_with(|| {
1564 format!("failed to fetch rolled fork block {} ({})", block.number, block.hash)
1565 })?;
1566 ensure_block_identity(&block_data, block, "rolled fork")?;
1567 let block_context = Self::block_context_inputs_from_backend(&backend, &block_data)?;
1568 ContextUpdate::Replace(block_context.into_child().next_transaction(tx))
1569 } else {
1570 ContextUpdate::Unchanged
1571 };
1572 #[cfg(not(feature = "monad"))]
1573 let context_update = std::marker::PhantomData;
1574
1575 let idx = self.inner.roll_fork(id, fork_id, block, context.source_chain_id, backend)?;
1577
1578 if let Some((active_id, active_idx)) = self.active_fork_ids
1579 && active_id == id
1580 {
1581 let preserved_spec = evm_env.cfg_env.spec;
1582 *evm_env = fork_env;
1583 evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1584
1585 let persistent_accounts = self.inner.persistent_accounts.clone();
1586 let caller = self.inner.caller;
1587 let active = self.inner.get_fork_mut(active_idx);
1588 Self::reset_rolled_active_fork(
1589 active,
1590 &self.fork_init_journaled_state,
1591 &persistent_accounts,
1592 caller,
1593 journaled_state,
1594 );
1595 } else {
1596 let fork = self.inner.get_fork_mut(idx);
1597 Self::reset_rolled_inactive_fork(fork, &self.fork_init_journaled_state);
1598 }
1599
1600 Ok(context_update)
1601 }
1602
1603 fn roll_fork_to_transaction_with_context(
1606 &mut self,
1607 id: Option<LocalForkId>,
1608 transaction: B256,
1609 evm_env: &mut EvmEnvFor<FEN>,
1610 _tx_env: Option<&TxEnvFor<FEN>>,
1611 journaled_state: &mut JournaledState,
1612 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1613 if !self.networks.is_monad() {
1614 return self.roll_fork_to_transaction_inner(id, transaction, evm_env, journaled_state);
1615 }
1616
1617 #[cfg(not(feature = "monad"))]
1618 unreachable!("block context is only required when Monad support is enabled");
1619
1620 #[cfg(feature = "monad")]
1621 {
1622 trace!(?id, ?transaction, "roll fork to transaction");
1623 let id = self.ensure_fork(id)?;
1624 let affects_active = self.is_active_fork(id);
1625 let TransactionForkTarget { fork_block, block, position, .. } =
1626 self.get_block_number_and_block_for_transaction(id, transaction)?;
1627 let position = position.expect("Monad transaction target includes canonical position");
1628 let block_context = self.block_context_inputs(id, &block)?;
1629 let context_update = if affects_active && let Some(tx) = _tx_env {
1630 let fork_position = ForkPosition::BeforeTransaction {
1631 block: BlockNumHash::new(block.header().number(), block.header().hash),
1632 transaction_index: position.index,
1633 };
1634 ContextUpdate::Replace(Self::context_for_block_position(
1635 block_context.clone(),
1636 fork_position,
1637 tx,
1638 )?)
1639 } else if affects_active {
1640 ContextUpdate::Unchanged
1641 } else {
1642 ContextUpdate::Rebase
1643 };
1644
1645 let current_fork_id = self.inner.ensure_fork_id(id).cloned()?;
1646 let ForkResult { id: fork_id, backend, env: fork_env, fork: resolved } =
1647 self.forks.roll_fork_exact(current_fork_id, fork_block)?;
1648 let staged_fork_journaled_state = if affects_active {
1649 self.fork_init_journaled_state.clone()
1650 } else {
1651 self.inner.get_fork_by_id(id)?.journaled_state.clone()
1652 };
1653 let mut staged_fork = self.inner.stage_fork_roll(
1654 id,
1655 fork_id,
1656 fork_block,
1657 resolved.context().source_chain_id,
1658 backend,
1659 staged_fork_journaled_state,
1660 )?;
1661 let mut staged_evm_env = evm_env.clone();
1662 let mut staged_journaled_state = journaled_state.clone();
1663
1664 if affects_active {
1665 let preserved_spec = staged_evm_env.cfg_env.spec;
1666 staged_evm_env = fork_env;
1667 staged_evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1668 Self::populate_rolled_active_fork(
1669 &mut staged_fork.fork,
1670 &self.inner.persistent_accounts,
1671 self.inner.caller,
1672 &mut staged_journaled_state,
1673 );
1674 }
1675
1676 update_env_block::<AnyNetwork, _, _>(
1677 &mut staged_evm_env,
1678 &block,
1679 staged_fork.fork.source_chain_id,
1680 self.networks,
1681 );
1682 let mut replay_env = staged_evm_env.clone();
1683 self.apply_fork_tx_replay_env_changes_for(
1684 &staged_fork.fork_id,
1685 staged_fork.fork.source_chain_id,
1686 &mut replay_env,
1687 )?;
1688 let target = Self::replay_until(
1689 &mut staged_fork.fork,
1690 ReplayInputs {
1691 fork_id: staged_fork.fork_id.clone(),
1692 forks: self.forks.clone(),
1693 evm_env: replay_env,
1694 networks: self.networks,
1695 },
1696 &block,
1697 Some(&block_context),
1698 transaction,
1699 &mut staged_journaled_state,
1700 &self.inner.persistent_accounts,
1701 )?;
1702 eyre::ensure!(
1703 target.is_some(),
1704 "transaction {transaction:?} is missing from block {}",
1705 block.header().number()
1706 );
1707 staged_fork.fork.position = ForkPosition::BeforeTransaction {
1708 block: BlockNumHash::new(block.header().number(), block.header().hash),
1709 transaction_index: position.index,
1710 };
1711
1712 staged_fork.fork.block_env = Some(staged_evm_env.block_env.clone());
1713 self.inner.publish_fork_roll(staged_fork);
1714 *evm_env = staged_evm_env;
1715 *journaled_state = staged_journaled_state;
1716 Ok(context_update)
1717 }
1718 }
1719
1720 fn roll_fork_to_transaction_inner(
1722 &mut self,
1723 id: Option<LocalForkId>,
1724 transaction: B256,
1725 evm_env: &mut EvmEnvFor<FEN>,
1726 journaled_state: &mut JournaledState,
1727 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1728 trace!(?id, ?transaction, "roll fork to transaction");
1729 let id = self.ensure_fork(id)?;
1730 let _affects_active = self.is_active_fork(id);
1731
1732 let TransactionForkTarget { fork_block, block, mined, position, .. } =
1733 self.get_block_number_and_block_for_transaction(id, transaction)?;
1734 #[cfg(feature = "monad")]
1735 let context_update =
1736 if _affects_active { ContextUpdate::Unchanged } else { ContextUpdate::Rebase };
1737 #[cfg(not(feature = "monad"))]
1738 let context_update = std::marker::PhantomData;
1739
1740 self.roll_fork_exact_with_context(id, fork_block, mined, evm_env, None, journaled_state)?;
1742
1743 let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
1744 update_env_block::<AnyNetwork, _, _>(evm_env, &block, source_chain_id, self.networks);
1745
1746 let mut replay_env = evm_env.clone();
1747 self.apply_fork_tx_replay_env_changes(id, &mut replay_env)?;
1748 let persistent_accounts = self.inner.persistent_accounts.clone();
1749 let target = if mined {
1750 let fork_id = self.inner.ensure_fork_id(id)?.clone();
1751 let forks = self.forks.clone();
1752 let fork = self.inner.get_fork_by_id_mut(id)?;
1753 Self::replay_until(
1754 fork,
1755 ReplayInputs { fork_id, forks, evm_env: replay_env, networks: self.networks },
1756 &block,
1757 #[cfg(feature = "monad")]
1758 None,
1759 transaction,
1760 journaled_state,
1761 &persistent_accounts,
1762 )?
1763 } else {
1764 None
1765 };
1766 if target.is_some()
1767 && let Some(position) = position
1768 {
1769 self.inner.get_fork_by_id_mut(id)?.position = ForkPosition::BeforeTransaction {
1770 block: BlockNumHash::new(block.header().number(), block.header().hash),
1771 transaction_index: position.index,
1772 };
1773 }
1774
1775 self.inner.get_fork_by_id_mut(id)?.block_env = Some(evm_env.block_env.clone());
1777
1778 Ok(context_update)
1779 }
1780
1781 fn replay_until(
1785 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1786 replay: ReplayInputs<FEN>,
1787 full_block: &AnyRpcBlock,
1788 #[cfg(feature = "monad")] block_context: Option<&BlockContext<FEN>>,
1789 tx_hash: B256,
1790 journaled_state: &mut JournaledState,
1791 persistent_accounts: &AddressSet,
1792 ) -> eyre::Result<Option<AnyRpcTransaction>> {
1793 let ReplayInputs { fork_id, forks, evm_env, networks } = replay;
1794 trace!(?tx_hash, "replay until transaction");
1795 #[cfg(feature = "monad")]
1796 eyre::ensure!(
1797 !networks.is_monad() || block_context.is_some(),
1798 "block context is required to replay transactions for this network"
1799 );
1800
1801 let BlockTransactions::Full(transactions) = full_block.transactions() else {
1802 eyre::bail!(
1803 "block {} does not contain full transactions",
1804 full_block.header().number()
1805 );
1806 };
1807 let Some(target_index) = transactions.iter().position(|tx| tx.tx_hash() == tx_hash) else {
1808 return Ok(None);
1809 };
1810 if networks.is_monad() {
1811 eyre::ensure!(
1812 fork.position
1813 .after_transaction(
1814 BlockNumHash::new(full_block.header().number(), full_block.header().hash),
1815 full_block.header().parent_hash(),
1816 0,
1817 transactions.len(),
1818 )
1819 .is_some(),
1820 "block {} does not immediately follow the active fork position",
1821 full_block.header().number()
1822 );
1823 }
1824 let target_tx = transactions[target_index].clone();
1825 let factory = FEN::EvmFactory::default();
1826 let mut txs_to_replay = Vec::with_capacity(target_index);
1827 for (index, tx) in transactions[..target_index].iter().enumerate() {
1828 let Some(tx_env) = Self::replay_tx_env(tx)? else { continue };
1829 let is_system = is_known_system_sender(tx.from()) || tx.ty() == SYSTEM_TRANSACTION_TYPE;
1830 txs_to_replay.push((index, *tx.tx_hash(), tx_env, is_system));
1831 }
1832
1833 if !txs_to_replay.is_empty() {
1835 let now = Instant::now();
1836
1837 let chain_id = evm_env.cfg_env.chain_id;
1840 let timestamp = evm_env.block_env.timestamp().saturating_to();
1841 let mut replay_backend = Self::new_with_fork_manager(
1842 forks,
1843 &fork_id,
1844 fork.clone(),
1845 journaled_state.clone(),
1846 networks,
1847 )?;
1848
1849 #[cfg(feature = "monad")]
1850 if let Some(context) = block_context {
1851 for (index, tx_hash, tx_env, is_system) in &txs_to_replay {
1852 let mut evm = factory.create_nested_evm(&mut replay_backend, evm_env.clone());
1853 *evm.chain_mut() = context.transaction(*index);
1854 inject_replay_precompiles(networks, evm.precompiles_mut(), chain_id, timestamp);
1855 trace!(tx=?tx_hash, "committing transaction");
1856 let result = evm
1857 .transact_replay(tx_env.clone(), *is_system)
1858 .wrap_err("backend: failed replaying transaction")?;
1859 drop(evm);
1860 if let Some(result) = result {
1861 replay_backend.commit(result.state);
1862 }
1863 }
1864 }
1865 #[cfg(feature = "monad")]
1866 let replay_without_context = block_context.is_none();
1867 #[cfg(not(feature = "monad"))]
1868 let replay_without_context = true;
1869 if replay_without_context {
1870 let mut index = 0;
1874 while index < txs_to_replay.len() {
1875 let (_, tx_hash, tx_env, is_system) = &txs_to_replay[index];
1876 if *is_system {
1877 trace!(tx=?tx_hash, "committing transaction");
1878 let mut replay =
1879 factory.create_nested_evm(&mut replay_backend, evm_env.clone());
1880 inject_replay_precompiles(
1881 networks,
1882 replay.precompiles_mut(),
1883 chain_id,
1884 timestamp,
1885 );
1886 if let Some(result) = replay
1887 .transact_replay(tx_env.clone(), true)
1888 .wrap_err("backend: failed replaying system transaction")?
1889 {
1890 drop(replay);
1891 replay_backend.commit(result.state);
1892 }
1893 index += 1;
1894 continue;
1895 }
1896
1897 let mut evm = factory.create_foundry_evm_with_inspector(
1898 &mut replay_backend,
1899 evm_env.clone(),
1900 NoOpInspector,
1901 );
1902 inject_replay_precompiles(networks, evm.precompiles_mut(), chain_id, timestamp);
1903 while index < txs_to_replay.len() && !txs_to_replay[index].3 {
1904 let (_, tx_hash, tx_env, _) = &txs_to_replay[index];
1905 trace!(tx=?tx_hash, "committing transaction");
1906 let state = evm
1907 .transact(tx_env.clone())
1908 .wrap_err("backend: failed replaying transaction")?
1909 .state;
1910 evm.db_mut().commit(state);
1911 index += 1;
1912 }
1913 }
1914 }
1915
1916 let (_, index) = replay_backend.active_fork_ids.expect("replay fork is active");
1917 fork.db = replay_backend.inner.take_fork(index).db;
1918
1919 fork.refresh_journaled_states(journaled_state, persistent_accounts)?;
1922
1923 trace!(elapsed=?now.elapsed(), count=txs_to_replay.len(), "replayed transactions");
1924 }
1925
1926 Ok(Some(target_tx))
1927 }
1928}
1929
1930#[cfg(feature = "monad")]
1931fn ensure_block_identity(
1932 block: &AnyRpcBlock,
1933 expected: BlockNumHash,
1934 relation: &str,
1935) -> eyre::Result<()> {
1936 eyre::ensure!(
1937 block.header().number() == expected.number && block.header().hash == expected.hash,
1938 "{relation} block changed: expected {} ({}), got {} ({})",
1939 expected.number,
1940 expected.hash,
1941 block.header().number(),
1942 block.header().hash
1943 );
1944 Ok(())
1945}
1946
1947impl<FEN: FoundryEvmNetwork> DatabaseExt<FEN::EvmFactory> for Backend<FEN> {
1948 fn chain_context_for_synthetic_transaction(
1949 &self,
1950 tx: &TxEnvFor<FEN>,
1951 ) -> eyre::Result<ChainFor<FEN>> {
1952 #[cfg(feature = "monad")]
1953 if let Some(context) = self.block_context_for_synthetic_transaction()? {
1954 return Ok(context.next_transaction(tx));
1955 }
1956 Ok(ChainFor::<FEN>::for_transaction(tx))
1957 }
1958
1959 fn snapshot_state(
1960 &mut self,
1961 journaled_state: &JournaledState,
1962 evm_env: &EvmEnvFor<FEN>,
1963 ) -> U256 {
1964 trace!("create snapshot");
1965 let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new(
1966 self.create_db_snapshot(),
1967 journaled_state.clone(),
1968 evm_env.clone(),
1969 ));
1970 trace!(target: "backend", "Created new snapshot {}", id);
1971 id
1972 }
1973
1974 fn revert_state(
1975 &mut self,
1976 id: U256,
1977 current_state: &JournaledState,
1978 evm_env: &mut EvmEnvFor<FEN>,
1979 caller: Address,
1980 action: RevertStateSnapshotAction,
1981 ) -> Option<JournaledState> {
1982 trace!(?id, "revert snapshot");
1983 if let Some(mut snapshot) = self.inner.state_snapshots.remove_at(id) {
1984 if action.is_keep() {
1986 self.inner.state_snapshots.insert_at(snapshot.clone(), id);
1987 }
1988
1989 if let Some(account) = current_state.state.get(&CHEATCODE_ADDRESS)
1994 && let Some(slot) = account.storage.get(&GLOBAL_FAIL_SLOT)
1995 && !slot.present_value().is_zero()
1996 {
1997 self.set_state_snapshot_failure(true);
1998 }
1999
2000 snapshot.merge(current_state);
2002 let BackendStateSnapshot { db, mut journaled_state, snap_evm_env } = snapshot;
2003 journaled_state.depth = current_state.depth;
2006 match db {
2007 BackendDatabaseSnapshot::InMemory(mem_db) => {
2008 self.mem_db = mem_db;
2009 self.active_fork_ids = None;
2010 }
2011 BackendDatabaseSnapshot::Forked(id, fork_id, idx, mut fork) => {
2012 journaled_state.state.entry(caller).or_insert_with(|| {
2016 let caller_account = current_state
2017 .state
2018 .get(&caller)
2019 .map(|acc| acc.info.clone())
2020 .unwrap_or_default();
2021
2022 if !fork.db.cache.accounts.contains_key(&caller) {
2023 fork.db.insert_account_info(caller, caller_account.clone());
2025 }
2026 caller_account.into()
2027 });
2028 self.inner.revert_state_snapshot(id, fork_id, idx, *fork);
2029 self.active_fork_ids = Some((id, idx))
2030 }
2031 }
2032
2033 *evm_env = snap_evm_env;
2034 trace!(target: "backend", "Reverted snapshot {}", id);
2035
2036 Some(journaled_state)
2037 } else {
2038 warn!(target: "backend", "No snapshot to revert for {}", id);
2039 None
2040 }
2041 }
2042
2043 fn delete_state_snapshot(&mut self, id: U256) -> bool {
2044 self.inner.state_snapshots.remove_at(id).is_some()
2045 }
2046
2047 fn delete_state_snapshots(&mut self) {
2048 self.inner.state_snapshots.clear()
2049 }
2050
2051 fn create_fork(&mut self, create_fork: CreateFork) -> eyre::Result<LocalForkId> {
2052 trace!("create fork");
2053 let ForkResult { id: fork_id, backend: fork, fork: resolved, .. } =
2054 self.forks.create_fork(create_fork)?;
2055 let context = resolved.context();
2056 let block = resolved.block();
2057 let fork_db = ForkDB::new(fork);
2058 let (id, _) = self.inner.insert_new_fork(
2059 fork_id,
2060 block,
2061 context.source_chain_id,
2062 fork_db,
2063 self.fork_init_journaled_state.clone(),
2064 );
2065 Ok(id)
2066 }
2067
2068 fn create_fork_at_transaction(
2069 &mut self,
2070 fork: CreateFork,
2071 transaction: B256,
2072 ) -> eyre::Result<LocalForkId> {
2073 trace!(?transaction, "create fork at transaction");
2074 let id = self.create_fork(fork)?;
2075 let fork_id = self.ensure_fork_id(id).cloned()?;
2076 let mut evm_env = self
2077 .forks
2078 .get_evm_env(fork_id)?
2079 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
2080
2081 self.roll_fork_to_transaction_with_context(
2084 Some(id),
2085 transaction,
2086 &mut evm_env,
2087 None,
2088 &mut self.inner.new_journaled_state(),
2089 )?;
2090
2091 Ok(id)
2092 }
2093
2094 fn select_fork(
2097 &mut self,
2098 id: LocalForkId,
2099 evm_env: &mut EvmEnvFor<FEN>,
2100 tx_env: &mut TxEnvFor<FEN>,
2101 active_journaled_state: &mut JournaledState,
2102 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2103 trace!(?id, "select fork");
2104 if self.is_active_fork(id) {
2105 #[cfg(feature = "monad")]
2107 return Ok(ContextUpdate::Unchanged);
2108 #[cfg(not(feature = "monad"))]
2109 return Ok(std::marker::PhantomData);
2110 }
2111
2112 #[cfg(feature = "monad")]
2113 let chain_context = if self.networks.is_monad() {
2114 Some(self.context_for_fork_synthetic_transaction(id, tx_env)?)
2115 } else {
2116 None
2117 };
2118
2119 if let Some(active_fork_id) = self.active_fork_id() {
2121 let fork_id = self.ensure_fork_id(active_fork_id).cloned()?;
2122 let active = self.inner.get_fork_by_id_mut(active_fork_id)?;
2123 if active.block_env.is_none() {
2124 active.block_env = Some(
2125 self.forks
2126 .get_evm_env(fork_id)?
2127 .ok_or_else(|| {
2128 eyre::eyre!("Requested fork `{active_fork_id}` does not exist")
2129 })?
2130 .block_env,
2131 );
2132 }
2133 let block = active.block_env.as_mut().unwrap();
2134 block.set_number(evm_env.block_env.number());
2135 block.set_timestamp(evm_env.block_env.timestamp());
2136 }
2137
2138 let fork_id = self.ensure_fork_id(id).cloned()?;
2139 let idx = self.inner.ensure_fork_index(&fork_id)?;
2140 let mut fork_evm_env = self
2141 .forks
2142 .get_evm_env(fork_id)?
2143 .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
2144 if let Some(block) = &self.inner.get_fork(idx).block_env {
2145 fork_evm_env.block_env = block.clone();
2146 }
2147
2148 if let Some(active) = self.active_fork_mut() {
2151 active.journaled_state = active_journaled_state.clone();
2152
2153 let caller = tx_env.caller();
2154 let caller_account = active.journaled_state.state.get(&caller).cloned();
2155 let target_fork = self.inner.get_fork_mut(idx);
2156
2157 if target_fork.journaled_state.depth == 0 {
2159 if let Some(mut acc) = caller_account {
2161 let fork_account = Database::basic(&mut target_fork.db, caller)?
2162 .ok_or(BackendError::MissingAccount(caller))?;
2163
2164 acc.info = fork_account;
2165 target_fork.journaled_state.state.insert(caller, acc);
2166 }
2167 }
2168 } else {
2169 self.set_init_journaled_state(active_journaled_state.clone());
2176 self.prepare_init_journal_state()?;
2177
2178 self.fork_init_journaled_state.depth = 0;
2180 }
2181
2182 {
2183 let mut fork = self.inner.take_fork(idx);
2185
2186 let persistent_accounts = self.inner.persistent_accounts.clone();
2194 if let Some(db) = self.active_fork_db_mut() {
2195 for addr in persistent_accounts {
2196 let Ok(db_account) = db.load_account(addr) else { continue };
2197
2198 let Some(fork_account) = fork.journaled_state.state.get_mut(&addr) else {
2199 continue;
2200 };
2201
2202 for (key, val) in &db_account.storage {
2203 if let Some(fork_storage) = fork_account.storage.get_mut(key) {
2204 fork_storage.present_value = *val;
2205 }
2206 }
2207 }
2208 }
2209
2210 fork.journaled_state.depth = active_journaled_state.depth;
2215
2216 let caller = tx_env.caller();
2220 fork.journaled_state.state.entry(caller).or_insert_with(|| {
2221 let caller_account = active_journaled_state
2222 .state
2223 .get(&caller)
2224 .map(|acc| acc.info.clone())
2225 .unwrap_or_default();
2226
2227 if !fork.db.cache.accounts.contains_key(&caller) {
2228 fork.db.insert_account_info(caller, caller_account.clone());
2230 }
2231 caller_account.into()
2232 });
2233
2234 self.update_fork_db(active_journaled_state, &mut fork);
2235
2236 self.inner.set_fork(idx, fork);
2238 }
2239
2240 self.active_fork_ids = Some((id, idx));
2241 let preserved_spec = evm_env.cfg_env.spec;
2245 let disable_fee_charge = evm_env.cfg_env.disable_fee_charge;
2246 tx_env.set_chain_id(Some(fork_evm_env.cfg_env.chain_id));
2247 *evm_env = fork_evm_env;
2248 evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
2249 evm_env.cfg_env.disable_fee_charge = disable_fee_charge;
2250
2251 #[cfg(feature = "monad")]
2252 return Ok(chain_context.map_or(ContextUpdate::Unchanged, ContextUpdate::Replace));
2253 #[cfg(not(feature = "monad"))]
2254 Ok(std::marker::PhantomData)
2255 }
2256
2257 fn roll_fork(
2260 &mut self,
2261 id: Option<LocalForkId>,
2262 block_number: u64,
2263 evm_env: &mut EvmEnvFor<FEN>,
2264 tx_env: &TxEnvFor<FEN>,
2265 journaled_state: &mut JournaledState,
2266 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2267 self.roll_fork_with_context(id, block_number, evm_env, Some(tx_env), journaled_state)
2268 }
2269
2270 fn roll_fork_to_transaction(
2271 &mut self,
2272 id: Option<LocalForkId>,
2273 transaction: B256,
2274 evm_env: &mut EvmEnvFor<FEN>,
2275 tx_env: &TxEnvFor<FEN>,
2276 journaled_state: &mut JournaledState,
2277 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2278 self.roll_fork_to_transaction_with_context(
2279 id,
2280 transaction,
2281 evm_env,
2282 Some(tx_env),
2283 journaled_state,
2284 )
2285 }
2286
2287 fn transact(
2288 &mut self,
2289 maybe_id: Option<LocalForkId>,
2290 transaction: B256,
2291 mut evm_env: EvmEnvFor<FEN>,
2292 _outer_tx_env: &TxEnvFor<FEN>,
2293 journaled_state: &mut JournaledState,
2294 inspector: &mut dyn for<'db> FoundryInspectorExt<
2295 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2296 >,
2297 ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2298 trace!(?maybe_id, ?transaction, "execute transaction");
2299 let persistent_accounts = self.inner.persistent_accounts.clone();
2300 let id = self.ensure_fork(maybe_id)?;
2301 let affects_active = self.is_active_fork(id);
2302 let fork_id = self.ensure_fork_id(id).cloned()?;
2303 let fork = self
2304 .forks
2305 .get_fork_info(fork_id.clone())?
2306 .ok_or_else(|| eyre::eyre!("Requested fork `{id}` does not exist"))?;
2307 eyre::ensure!(
2308 !fork.state_by_number,
2309 "transaction replay requires hash-addressed state; create a transaction-targeted fork or disable fork_state_by_number"
2310 );
2311
2312 let TransactionForkTarget {
2319 transaction: tx,
2320 block,
2321 #[cfg(feature = "monad")]
2322 position,
2323 ..
2324 } = self.get_block_number_and_block_for_transaction(id, transaction)?;
2325 let tx_env = TxEnvFor::<FEN>::from_any_rpc_transaction(&tx)?;
2326 let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
2327 update_env_block::<AnyNetwork, _, _>(&mut evm_env, &block, source_chain_id, self.networks);
2328 self.apply_fork_tx_replay_env_changes(id, &mut evm_env)?;
2329
2330 #[cfg(feature = "monad")]
2331 let block_context = if self.networks.is_monad() {
2332 Some(self.block_context_inputs(id, &block)?)
2333 } else {
2334 None
2335 };
2336 #[cfg(feature = "monad")]
2337 let chain_context = if let Some(context) = &block_context {
2338 context.transaction(
2339 position.expect("Monad transaction target includes canonical position").index,
2340 )
2341 } else {
2342 ChainFor::<FEN>::for_transaction(&tx_env)
2343 };
2344
2345 #[cfg(not(feature = "monad"))]
2346 let chain_context = ChainFor::<FEN>::for_transaction(&tx_env);
2347
2348 #[cfg(feature = "monad")]
2349 let next_position = if block_context.is_some() {
2350 let position = position.expect("Monad transaction target includes canonical position");
2351 Some(
2352 self.inner
2353 .get_fork_by_id(id)?
2354 .position
2355 .after_transaction(
2356 BlockNumHash::new(block.header().number(), block.header().hash),
2357 block.header().parent_hash(),
2358 position.index,
2359 position.count,
2360 )
2361 .ok_or_else(|| {
2362 eyre::eyre!(
2363 "transaction {transaction} does not immediately follow the active \
2364 fork position"
2365 )
2366 })?,
2367 )
2368 } else {
2369 None
2370 };
2371 #[cfg(feature = "monad")]
2372 let context_update = if affects_active && let Some(context) = block_context {
2373 ContextUpdate::Replace(Self::context_for_block_position(
2374 context,
2375 next_position.expect("block context has a next position"),
2376 _outer_tx_env,
2377 )?)
2378 } else if affects_active {
2379 ContextUpdate::Unchanged
2380 } else {
2381 ContextUpdate::Rebase
2382 };
2383 #[cfg(not(feature = "monad"))]
2384 let context_update = std::marker::PhantomData;
2385
2386 let mut replay_journaled_state = if affects_active {
2387 journaled_state.clone()
2388 } else {
2389 self.inner.get_fork_by_id(id)?.journaled_state.clone()
2390 };
2391 if !affects_active {
2392 for addr in persistent_accounts.iter().copied() {
2393 merge_journaled_state_data(addr, journaled_state, &mut replay_journaled_state);
2394 }
2395 }
2396
2397 let fork = self.inner.get_fork_by_id_mut(id)?;
2398 commit_transaction::<FEN>(
2399 TransactionInputs {
2400 evm_env,
2401 tx_env,
2402 chain_context,
2403 rpc_block_number: block.header().number(),
2404 journaled_state: replay_journaled_state,
2405 },
2406 journaled_state,
2407 fork,
2408 &fork_id,
2409 self.networks,
2410 &persistent_accounts,
2411 inspector,
2412 )?;
2413 #[cfg(feature = "monad")]
2414 if let Some(position) = next_position {
2415 fork.position = position;
2416 }
2417 Ok(context_update)
2418 }
2419
2420 fn transact_from_tx(
2421 &mut self,
2422 tx_env: TxEnvFor<FEN>,
2423 evm_env: EvmEnvFor<FEN>,
2424 journaled_state: &mut JournaledState,
2425 inspector: &mut dyn for<'db> FoundryInspectorExt<
2426 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2427 >,
2428 ) -> eyre::Result<()> {
2429 trace!("execute signed transaction");
2430
2431 let mut db = self.clone();
2434 db.commit(journaled_state.state.clone());
2435 let res = {
2436 let depth = journaled_state.depth + 1;
2437 let factory = FEN::EvmFactory::default();
2438 let chain_context = db.chain_context_for_synthetic_transaction(&tx_env)?;
2439 let mut evm = factory.create_nested_evm_with_inspector(&mut db, evm_env, inspector);
2440 *evm.chain_mut() = chain_context;
2441 evm.journal_inner_mut().depth = depth;
2442 evm.transact_raw(tx_env)?
2443 };
2444
2445 db.commit(res.state.clone());
2446 update_state(&mut journaled_state.state, &mut db, None)?;
2447 self.commit(res.state);
2448
2449 Ok(())
2450 }
2451
2452 fn active_fork_id(&self) -> Option<LocalForkId> {
2453 self.active_fork_ids.map(|(id, _)| id)
2454 }
2455
2456 fn active_fork_url(&self) -> Option<String> {
2457 let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
2458 self.forks.get_fork_url(fork.clone()).ok()?
2459 }
2460
2461 fn active_fork_options(&self) -> Option<CreateFork> {
2462 let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
2463 self.forks.get_fork_options(fork.clone()).ok()?
2464 }
2465
2466 fn active_fork_source_chain_id(&self) -> Option<u64> {
2467 Some(self.inner.get_fork_by_id(self.active_fork_id()?).ok()?.source_chain_id)
2468 }
2469
2470 fn active_fork_block_number(&self) -> Option<u64> {
2471 if let Some(block_number) = self.fork_block_number_override {
2472 return Some(block_number);
2473 }
2474 let fork = self.inner.get_fork_by_id(self.active_fork_id()?).ok()?;
2475 Some(match fork.position {
2476 ForkPosition::AfterBlock { block } | ForkPosition::BeforeTransaction { block, .. } => {
2477 block.number
2478 }
2479 })
2480 }
2481
2482 fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
2483 if let Some(id) = id {
2484 if self.inner.issued_local_fork_ids.contains_key(&id) {
2485 return Ok(id);
2486 }
2487 eyre::bail!("Requested fork `{}` does not exist", id);
2488 }
2489 if let Some(id) = self.active_fork_id() {
2490 Ok(id)
2491 } else {
2492 eyre::bail!("No fork active");
2493 }
2494 }
2495
2496 fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
2497 self.inner.ensure_fork_id(id)
2498 }
2499
2500 fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic> {
2501 let active_id = self.active_fork_id()?;
2502 let active_fork = self.active_fork()?;
2503
2504 if self.inner.forks.len() == 1 {
2505 return None;
2508 }
2509
2510 if !active_fork.is_contract(callee) && !is_contract_in_state(evm_state, callee) {
2511 let mut available_on = Vec::new();
2513 for (id, fork) in self.inner.forks_iter().filter(|(id, _)| *id != active_id) {
2514 trace!(?id, address=?callee, "checking if account exists");
2515 if fork.is_contract(callee) {
2516 available_on.push(id);
2517 }
2518 }
2519
2520 return if available_on.is_empty() {
2521 Some(RevertDiagnostic::ContractDoesNotExist {
2522 contract: callee,
2523 active: active_id,
2524 persistent: self.is_persistent(&callee),
2525 })
2526 } else {
2527 Some(RevertDiagnostic::ContractExistsOnOtherForks {
2530 contract: callee,
2531 active: active_id,
2532 available_on,
2533 })
2534 };
2535 }
2536 None
2537 }
2538
2539 fn load_allocs(
2543 &mut self,
2544 allocs: &BTreeMap<Address, GenesisAccount>,
2545 journaled_state: &mut JournaledState,
2546 ) -> Result<(), BackendError> {
2547 for (addr, acc) in allocs {
2549 self.clone_account(acc, addr, journaled_state)?;
2550 }
2551
2552 Ok(())
2553 }
2554
2555 fn clone_account(
2560 &mut self,
2561 source: &GenesisAccount,
2562 target: &Address,
2563 journaled_state: &mut JournaledState,
2564 ) -> Result<(), BackendError> {
2565 let mut state_acc = journaled_state.load_account_mut(self, *target)?;
2568
2569 if let Some(bytecode) = source.code.as_ref() {
2571 let bytecode_hash = keccak256(bytecode);
2572 let bytecode = Bytecode::new_raw(bytecode.0.clone().into());
2573 state_acc.set_code(bytecode_hash, bytecode);
2574 }
2575
2576 state_acc.set_balance(source.balance);
2578
2579 if let Some(acc) = journaled_state.state.get_mut(target) {
2581 if let Some(storage) = source.storage.as_ref() {
2582 for (slot, value) in storage {
2583 let slot = (*slot).into();
2584 acc.storage.insert(
2585 slot,
2586 EvmStorageSlot::new_changed(
2587 acc.storage.get(&slot).map(|s| s.present_value()).unwrap_or_default(),
2588 (*value).into(),
2589 TransactionId::ZERO,
2590 ),
2591 );
2592 }
2593 }
2594
2595 acc.info.nonce = source.nonce.unwrap_or_default();
2597 };
2598
2599 journaled_state.touch(*target);
2601
2602 Ok(())
2603 }
2604
2605 fn add_persistent_account(&mut self, account: Address) -> bool {
2606 trace!(?account, "add persistent account");
2607 self.inner.persistent_accounts.insert(account)
2608 }
2609
2610 fn refresh_fork_account(
2611 &mut self,
2612 address: Address,
2613 field: ForkAccountField,
2614 journaled_state: &mut JournaledState,
2615 ) -> Result<(), BackendError> {
2616 let Some(fork) = self.active_fork_mut() else { return Ok(()) };
2617 trace!(?address, ?field, "refresh fork account");
2618 fork.db.db.data().accounts.write().remove(&address);
2619
2620 let cache_modified = fork
2621 .db
2622 .cache
2623 .accounts
2624 .get(&address)
2625 .is_some_and(|account| account.account_state != AccountState::None);
2626 if !cache_modified {
2627 fork.db.cache.accounts.remove(&address);
2628 }
2629
2630 if !cache_modified
2631 && !journaled_state.state.contains_key(&address)
2632 && !fork.journaled_state.state.contains_key(&address)
2633 {
2634 return Ok(());
2635 }
2636
2637 let mut refreshed = DatabaseRef::basic_ref(&fork.db.db, address)?.unwrap_or_default();
2638 if matches!(field, ForkAccountField::Code) {
2639 fork.db.insert_contract(&mut refreshed);
2640 }
2641 if let Some(cached) = fork.db.cache.accounts.get_mut(&address) {
2642 field.update(&mut cached.info, &refreshed);
2643 if cached.account_state == AccountState::NotExisting && !refreshed.is_empty() {
2644 cached.account_state = AccountState::None;
2645 }
2646 }
2647 if let Some(journaled_account) = journaled_state.state.get_mut(&address) {
2648 field.update(&mut journaled_account.info, &refreshed);
2649 }
2650 if let Some(journaled_account) = fork.journaled_state.state.get_mut(&address) {
2651 field.update(&mut journaled_account.info, &refreshed);
2652 }
2653 Ok(())
2654 }
2655
2656 fn refresh_fork_storage(
2657 &mut self,
2658 address: Address,
2659 slot: U256,
2660 journaled_state: &mut JournaledState,
2661 ) -> Result<(), BackendError> {
2662 let Some(fork) = self.active_fork_mut() else { return Ok(()) };
2663 trace!(?address, ?slot, "refresh fork storage");
2664 if let Some(storage) = fork.db.db.data().storage.write().get_mut(&address) {
2665 storage.remove(&slot);
2666 }
2667 let cache_modified = fork
2668 .db
2669 .cache
2670 .accounts
2671 .get(&address)
2672 .is_some_and(|account| account.account_state != AccountState::None);
2673 if !cache_modified && let Some(account) = fork.db.cache.accounts.get_mut(&address) {
2674 account.storage.remove(&slot);
2675 }
2676
2677 let outer_loaded = journaled_state
2678 .state
2679 .get(&address)
2680 .is_some_and(|account| account.storage.contains_key(&slot));
2681 let fork_loaded = fork
2682 .journaled_state
2683 .state
2684 .get(&address)
2685 .is_some_and(|account| account.storage.contains_key(&slot));
2686 if !cache_modified && !outer_loaded && !fork_loaded {
2687 return Ok(());
2688 }
2689
2690 let value = DatabaseRef::storage_ref(&fork.db.db, address, slot)?;
2691 if let Some(account) = fork.db.cache.accounts.get_mut(&address) {
2692 account.storage.insert(slot, value);
2693 if account.account_state == AccountState::NotExisting && !value.is_zero() {
2694 account.account_state = AccountState::None;
2695 }
2696 }
2697 if let Some(storage) = journaled_state
2698 .state
2699 .get_mut(&address)
2700 .and_then(|account| account.storage.get_mut(&slot))
2701 {
2702 storage.present_value = value;
2703 }
2704 if let Some(storage) = fork
2705 .journaled_state
2706 .state
2707 .get_mut(&address)
2708 .and_then(|account| account.storage.get_mut(&slot))
2709 {
2710 storage.present_value = value;
2711 }
2712 Ok(())
2713 }
2714
2715 fn remove_persistent_account(&mut self, account: &Address) -> bool {
2716 trace!(?account, "remove persistent account");
2717 self.inner.persistent_accounts.remove(account)
2718 }
2719
2720 fn is_persistent(&self, acc: &Address) -> bool {
2721 self.inner.persistent_accounts.contains(acc)
2722 }
2723
2724 fn allow_cheatcode_access(&mut self, account: Address) -> bool {
2725 trace!(?account, "allow cheatcode access");
2726 self.inner.cheatcode_access_accounts.insert(account)
2727 }
2728
2729 fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
2730 trace!(?account, "revoke cheatcode access");
2731 self.inner.cheatcode_access_accounts.remove(account)
2732 }
2733
2734 fn has_cheatcode_access(&self, account: &Address) -> bool {
2735 self.inner.cheatcode_access_accounts.contains(account)
2736 }
2737
2738 fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
2739 if let Some(db) = self.active_fork_db_mut() {
2740 db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
2741 } else {
2742 self.mem_db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
2743 }
2744 }
2745}
2746
2747impl<FEN: FoundryEvmNetwork> DatabaseRef for Backend<FEN> {
2748 type Error = DatabaseError;
2749
2750 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
2751 let mut account = if let Some(db) = self.active_fork_db() {
2752 db.basic_ref(address)?
2753 } else {
2754 self.mem_db.basic_ref(address)?
2755 };
2756 self.apply_bal_account(address, &mut account)?;
2757 Ok(account)
2758 }
2759
2760 fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
2761 if let Some(db) = self.active_fork_db() {
2762 db.code_by_hash_ref(code_hash)
2763 } else {
2764 Ok(self.mem_db.code_by_hash_ref(code_hash)?)
2765 }
2766 }
2767
2768 fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
2769 if let Some(value) = self.bal_storage(address, index)? {
2770 return Ok(value);
2771 }
2772 if let Some(db) = self.active_fork_db() {
2773 DatabaseRef::storage_ref(db, address, index)
2774 } else {
2775 Ok(DatabaseRef::storage_ref(&self.mem_db, address, index)?)
2776 }
2777 }
2778
2779 fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
2780 if let Some(db) = self.active_fork_db() {
2781 db.block_hash_ref(number)
2782 } else {
2783 Ok(self.mem_db.block_hash_ref(number)?)
2784 }
2785 }
2786}
2787
2788impl<FEN: FoundryEvmNetwork> DatabaseCommit for Backend<FEN> {
2789 fn commit(&mut self, changes: AddressMap<Account>) {
2790 if self.bal.take().is_some() {
2791 for (address, account) in &changes {
2794 if !account.is_touched() && !account.is_loaded_as_not_existing() {
2795 self.insert_account_info(*address, account.info.clone());
2796 }
2797 }
2798 }
2799 if let Some(db) = self.active_fork_db_mut() {
2800 db.commit(changes)
2801 } else {
2802 self.mem_db.commit(changes)
2803 }
2804 }
2805}
2806
2807impl<FEN: FoundryEvmNetwork> Database for Backend<FEN> {
2808 type Error = DatabaseError;
2809 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
2810 let mut account = if let Some(db) = self.active_fork_db_mut() {
2811 db.basic(address)?
2812 } else {
2813 self.mem_db.basic(address)?
2814 };
2815 self.apply_bal_account(address, &mut account)?;
2816 Ok(existing_account(self.inner.spec_id, account))
2817 }
2818
2819 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
2820 if let Some(db) = self.active_fork_db_mut() {
2821 Ok(db.code_by_hash(code_hash)?)
2822 } else {
2823 Ok(self.mem_db.code_by_hash(code_hash)?)
2824 }
2825 }
2826
2827 fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
2828 if let Some(value) = self.bal_storage(address, index)? {
2829 return Ok(value);
2830 }
2831 if let Some(db) = self.active_fork_db_mut() {
2832 Ok(Database::storage(db, address, index)?)
2833 } else {
2834 Ok(Database::storage(&mut self.mem_db, address, index)?)
2835 }
2836 }
2837
2838 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
2839 if let Some(db) = self.active_fork_db_mut() {
2840 Ok(db.block_hash(number)?)
2841 } else {
2842 Ok(self.mem_db.block_hash(number)?)
2843 }
2844 }
2845}
2846
2847#[derive(Clone, Debug)]
2849pub enum BackendDatabaseSnapshot<N: Network, B: ForkBlockEnv = BlockEnv> {
2850 InMemory(FoundryEvmInMemoryDB),
2852 Forked(LocalForkId, ForkId, ForkLookupIndex, Box<Fork<N, B>>),
2854}
2855
2856#[derive(Clone, Debug)]
2858pub struct Fork<N: Network, B: ForkBlockEnv = BlockEnv> {
2859 db: ForkDB<N, B>,
2860 journaled_state: JournaledState,
2861 source_chain_id: ChainId,
2862 position: ForkPosition,
2863 block_env: Option<B>,
2865}
2866
2867impl<N: Network, B: ForkBlockEnv> Fork<N, B> {
2868 pub const fn backend(&self) -> &SharedBackend<N, B> {
2870 &self.db.db
2871 }
2872
2873 pub fn is_contract(&self, acc: Address) -> bool {
2875 if let Ok(Some(acc)) = self.db.basic_ref(acc)
2876 && !acc.is_empty_code_hash()
2877 {
2878 return true;
2879 }
2880 is_contract_in_state(&self.journaled_state.state, acc)
2881 }
2882
2883 fn refresh_journaled_states(
2886 &mut self,
2887 journaled_state: &mut JournaledState,
2888 persistent_accounts: &AddressSet,
2889 ) -> Result<(), BackendError> {
2890 update_state(&mut journaled_state.state, &mut self.db, Some(persistent_accounts))?;
2891 update_state(&mut self.journaled_state.state, &mut self.db, Some(persistent_accounts))?;
2892 Ok(())
2893 }
2894}
2895
2896pub struct BackendInner<FEN: FoundryEvmNetwork> {
2898 pub launched_with_fork: Option<(ForkId, LocalForkId, ForkLookupIndex)>,
2903 pub issued_local_fork_ids: HashMap<LocalForkId, ForkId>,
2915 pub created_forks: HashMap<ForkId, ForkLookupIndex>,
2918 pub forks: Vec<Option<Fork<AnyNetwork, BlockEnvFor<FEN>>>>,
2921 #[allow(clippy::type_complexity)]
2923 pub state_snapshots: StateSnapshots<
2924 BackendStateSnapshot<
2925 BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
2926 SpecFor<FEN>,
2927 BlockEnvFor<FEN>,
2928 >,
2929 >,
2930 pub has_state_snapshot_failure: bool,
2940 pub caller: Option<Address>,
2942 pub next_fork_id: LocalForkId,
2944 pub persistent_accounts: AddressSet,
2948 pub spec_id: SpecFor<FEN>,
2950 pub cheatcode_access_accounts: AddressSet,
2952}
2953
2954impl<FEN: FoundryEvmNetwork> Clone for BackendInner<FEN> {
2955 fn clone(&self) -> Self {
2956 Self {
2957 launched_with_fork: self.launched_with_fork.clone(),
2958 issued_local_fork_ids: self.issued_local_fork_ids.clone(),
2959 created_forks: self.created_forks.clone(),
2960 forks: self.forks.clone(),
2961 state_snapshots: self.state_snapshots.clone(),
2962 has_state_snapshot_failure: self.has_state_snapshot_failure,
2963 caller: self.caller,
2964 next_fork_id: self.next_fork_id,
2965 persistent_accounts: self.persistent_accounts.clone(),
2966 spec_id: self.spec_id,
2967 cheatcode_access_accounts: self.cheatcode_access_accounts.clone(),
2968 }
2969 }
2970}
2971
2972impl<FEN: FoundryEvmNetwork> Debug for BackendInner<FEN> {
2973 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2974 f.debug_struct("BackendInner")
2975 .field("launched_with_fork", &self.launched_with_fork)
2976 .field("issued_local_fork_ids", &self.issued_local_fork_ids)
2977 .field("created_forks", &self.created_forks)
2978 .field("forks", &self.forks)
2979 .field("state_snapshots", &self.state_snapshots)
2980 .field("has_state_snapshot_failure", &self.has_state_snapshot_failure)
2981 .field("caller", &self.caller)
2982 .field("next_fork_id", &self.next_fork_id)
2983 .field("persistent_accounts", &self.persistent_accounts)
2984 .field("spec_id", &self.spec_id)
2985 .field("cheatcode_access_accounts", &self.cheatcode_access_accounts)
2986 .finish()
2987 }
2988}
2989
2990impl<FEN: FoundryEvmNetwork> BackendInner<FEN> {
2991 pub fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
2992 self.issued_local_fork_ids
2993 .get(&id)
2994 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
2995 }
2996
2997 pub fn ensure_fork_index(&self, id: &ForkId) -> eyre::Result<ForkLookupIndex> {
2998 self.created_forks
2999 .get(id)
3000 .copied()
3001 .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
3002 }
3003
3004 pub fn ensure_fork_index_by_local_id(&self, id: LocalForkId) -> eyre::Result<ForkLookupIndex> {
3005 self.ensure_fork_index(self.ensure_fork_id(id)?)
3006 }
3007
3008 #[track_caller]
3010 fn get_fork(&self, idx: ForkLookupIndex) -> &Fork<AnyNetwork, BlockEnvFor<FEN>> {
3011 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
3012 self.forks[idx].as_ref().unwrap()
3013 }
3014
3015 #[track_caller]
3017 fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork<AnyNetwork, BlockEnvFor<FEN>> {
3018 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
3019 self.forks[idx].as_mut().unwrap()
3020 }
3021
3022 #[track_caller]
3024 fn get_fork_by_id_mut(
3025 &mut self,
3026 id: LocalForkId,
3027 ) -> eyre::Result<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
3028 let idx = self.ensure_fork_index_by_local_id(id)?;
3029 Ok(self.get_fork_mut(idx))
3030 }
3031
3032 #[track_caller]
3034 fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
3035 let idx = self.ensure_fork_index_by_local_id(id)?;
3036 Ok(self.get_fork(idx))
3037 }
3038
3039 fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork<AnyNetwork, BlockEnvFor<FEN>> {
3041 debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
3042 self.forks[idx].take().unwrap()
3043 }
3044
3045 fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork<AnyNetwork, BlockEnvFor<FEN>>) {
3046 self.forks[idx] = Some(fork)
3047 }
3048
3049 pub fn forks_iter(
3051 &self,
3052 ) -> impl Iterator<Item = (LocalForkId, &Fork<AnyNetwork, BlockEnvFor<FEN>>)> + '_ {
3053 self.issued_local_fork_ids
3054 .iter()
3055 .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id])))
3056 }
3057
3058 pub fn forks_iter_mut(
3060 &mut self,
3061 ) -> impl Iterator<Item = &mut Fork<AnyNetwork, BlockEnvFor<FEN>>> + '_ {
3062 self.forks.iter_mut().filter_map(|f| f.as_mut())
3063 }
3064
3065 pub fn revert_state_snapshot(
3067 &mut self,
3068 id: LocalForkId,
3069 fork_id: ForkId,
3070 idx: ForkLookupIndex,
3071 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
3072 ) {
3073 self.created_forks.insert(fork_id.clone(), idx);
3074 self.issued_local_fork_ids.insert(id, fork_id);
3075 self.set_fork(idx, fork)
3076 }
3077
3078 pub fn update_fork_mapping(
3080 &mut self,
3081 id: LocalForkId,
3082 fork_id: ForkId,
3083 block: BlockNumHash,
3084 source_chain_id: ChainId,
3085 db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
3086 journaled_state: JournaledState,
3087 ) -> ForkLookupIndex {
3088 let idx = self.forks.len();
3089 self.issued_local_fork_ids.insert(id, fork_id.clone());
3090 self.created_forks.insert(fork_id, idx);
3091
3092 let fork = Fork {
3093 db,
3094 journaled_state,
3095 source_chain_id,
3096 position: ForkPosition::AfterBlock { block },
3097 block_env: None,
3098 };
3099 self.forks.push(Some(fork));
3100 idx
3101 }
3102
3103 pub fn roll_fork(
3104 &mut self,
3105 id: LocalForkId,
3106 new_fork_id: ForkId,
3107 block: BlockNumHash,
3108 source_chain_id: ChainId,
3109 backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
3110 ) -> eyre::Result<ForkLookupIndex> {
3111 let fork_id = self.ensure_fork_id(id)?;
3112 let idx = self.ensure_fork_index(fork_id)?;
3113
3114 if let Some(active) = self.forks[idx].as_mut() {
3115 let mut new_db = ForkDB::new(backend);
3117 for addr in self.persistent_accounts.iter().copied() {
3118 merge_db_account_data(addr, &active.db, &mut new_db);
3119 }
3120 active.db = new_db;
3121 active.source_chain_id = source_chain_id;
3122 active.position = ForkPosition::AfterBlock { block };
3123 active.block_env = None;
3124 }
3125 self.issued_local_fork_ids.insert(id, new_fork_id.clone());
3126 self.created_forks.insert(new_fork_id, idx);
3127 Ok(idx)
3128 }
3129
3130 #[cfg(feature = "monad")]
3132 fn stage_fork_roll(
3133 &self,
3134 id: LocalForkId,
3135 new_fork_id: ForkId,
3136 block: BlockNumHash,
3137 source_chain_id: ChainId,
3138 backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
3139 journaled_state: JournaledState,
3140 ) -> eyre::Result<StagedForkRoll<FEN>> {
3141 let fork_id = self.ensure_fork_id(id)?;
3142 let idx = self.ensure_fork_index(fork_id)?;
3143 let current = self.get_fork(idx);
3144
3145 let mut new_db = ForkDB::new(backend);
3148 for addr in self.persistent_accounts.iter().copied() {
3149 merge_db_account_data(addr, ¤t.db, &mut new_db);
3150 }
3151
3152 Ok(StagedForkRoll {
3153 local_id: id,
3154 fork_id: new_fork_id,
3155 fork_index: idx,
3156 fork: Fork {
3157 db: new_db,
3158 journaled_state,
3159 source_chain_id,
3160 position: ForkPosition::AfterBlock { block },
3161 block_env: None,
3162 },
3163 })
3164 }
3165
3166 #[cfg(feature = "monad")]
3168 fn publish_fork_roll(&mut self, staged: StagedForkRoll<FEN>) -> ForkLookupIndex {
3169 let StagedForkRoll { local_id, fork_id, fork_index, fork } = staged;
3170 self.set_fork(fork_index, fork);
3171 self.issued_local_fork_ids.insert(local_id, fork_id.clone());
3172 self.created_forks.insert(fork_id, fork_index);
3173 fork_index
3174 }
3175
3176 pub fn insert_new_fork(
3180 &mut self,
3181 fork_id: ForkId,
3182 block: BlockNumHash,
3183 source_chain_id: ChainId,
3184 db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
3185 journaled_state: JournaledState,
3186 ) -> (LocalForkId, ForkLookupIndex) {
3187 self.insert_fork(
3188 fork_id,
3189 Fork {
3190 db,
3191 journaled_state,
3192 source_chain_id,
3193 position: ForkPosition::AfterBlock { block },
3194 block_env: None,
3195 },
3196 )
3197 }
3198
3199 fn insert_fork(
3201 &mut self,
3202 fork_id: ForkId,
3203 fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
3204 ) -> (LocalForkId, ForkLookupIndex) {
3205 let idx = self.forks.len();
3206 self.created_forks.insert(fork_id.clone(), idx);
3207 let id = self.next_id();
3208 self.issued_local_fork_ids.insert(id, fork_id);
3209 self.forks.push(Some(fork));
3210 (id, idx)
3211 }
3212
3213 fn next_id(&mut self) -> U256 {
3214 let id = self.next_fork_id;
3215 self.next_fork_id += U256::ONE;
3216 id
3217 }
3218
3219 pub fn len(&self) -> usize {
3221 self.issued_local_fork_ids.len()
3222 }
3223
3224 pub fn is_empty(&self) -> bool {
3226 self.issued_local_fork_ids.is_empty()
3227 }
3228
3229 pub fn precompile_addresses(&self) -> AddressSet {
3230 let evm = FEN::EvmFactory::default().create_evm(
3231 EmptyDB::default(),
3232 EvmEnv::new(CfgEnv::new_with_spec(self.spec_id), Default::default()),
3233 );
3234 evm.precompiles().addresses().copied().collect()
3235 }
3236
3237 pub fn new_journaled_state(&self) -> JournaledState {
3239 let mut journal = {
3240 let mut journal_inner = JournalInner::new();
3241 journal_inner.set_spec_id(self.spec_id.into());
3242 journal_inner
3243 };
3244 let precompile_addresses = self.precompile_addresses();
3245 journal.warm_addresses.set_precompile_addresses(&precompile_addresses);
3246 journal
3247 }
3248}
3249
3250impl<FEN: FoundryEvmNetwork> Default for BackendInner<FEN> {
3251 fn default() -> Self {
3252 Self {
3253 launched_with_fork: None,
3254 issued_local_fork_ids: Default::default(),
3255 created_forks: Default::default(),
3256 forks: vec![],
3257 state_snapshots: Default::default(),
3258 has_state_snapshot_failure: false,
3259 caller: None,
3260 next_fork_id: Default::default(),
3261 persistent_accounts: Default::default(),
3262 spec_id: SpecFor::<FEN>::default(),
3263 cheatcode_access_accounts: AddressSet::from_iter([
3266 CHEATCODE_ADDRESS,
3267 TEST_CONTRACT_ADDRESS,
3268 CALLER,
3269 ]),
3270 }
3271 }
3272}
3273
3274pub(crate) fn merge_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
3277 accounts: impl IntoIterator<Item = Address>,
3278 active: &CacheDB<ExtDB>,
3279 active_journaled_state: &mut JournaledState,
3280 target_fork: &mut Fork<N, B>,
3281) {
3282 for addr in accounts {
3283 merge_db_account_data(addr, active, &mut target_fork.db);
3284 merge_journaled_state_data(addr, active_journaled_state, &mut target_fork.journaled_state);
3285 }
3286
3287 *active_journaled_state = target_fork.journaled_state.clone();
3288}
3289
3290pub fn existing_account(
3298 spec: impl Into<SpecId>,
3299 account: Option<AccountInfo>,
3300) -> Option<AccountInfo> {
3301 if spec.into().is_enabled_in(SpecId::SPURIOUS_DRAGON) {
3302 account.filter(|account| !account.is_empty())
3303 } else {
3304 account
3305 }
3306}
3307
3308pub fn account_fetch_policy_for_source(
3313 source_chain_id: ChainId,
3314 network_profile: NetworkConfigs,
3315) -> AccountFetchPolicy {
3316 let source_chain = Chain::from_id(source_chain_id);
3317 let is_tempo =
3318 source_chain.is_tempo() || (source_chain.named().is_none() && network_profile.is_tempo());
3319 if is_tempo { AccountFetchPolicy::RequireAccountInfo } else { AccountFetchPolicy::Auto }
3320}
3321
3322fn merge_journaled_state_data(
3324 addr: Address,
3325 active_journaled_state: &JournaledState,
3326 fork_journaled_state: &mut JournaledState,
3327) {
3328 if let Some(mut acc) = active_journaled_state.state.get(&addr).cloned() {
3329 trace!(?addr, "updating journaled_state account data");
3330 if let Some(fork_account) = fork_journaled_state.state.get_mut(&addr) {
3331 fork_account.storage.extend(std::mem::take(&mut acc.storage));
3333 std::mem::swap(&mut fork_account.storage, &mut acc.storage);
3335 }
3336 fork_journaled_state.state.insert(addr, acc);
3337 }
3338}
3339
3340fn merge_db_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
3342 addr: Address,
3343 active: &CacheDB<ExtDB>,
3344 fork_db: &mut ForkDB<N, B>,
3345) {
3346 trace!(?addr, "merging database data");
3347
3348 let Some(acc) = active.cache.accounts.get(&addr) else { return };
3349
3350 if let Some(code) = active.cache.contracts.get(&acc.info.code_hash()) {
3352 trace!("merging contract cache");
3353 fork_db.cache.contracts.insert(acc.info.code_hash(), code.clone());
3354 }
3355
3356 use std::collections::hash_map::Entry;
3358 match fork_db.cache.accounts.entry(addr) {
3359 Entry::Vacant(vacant) => {
3360 trace!("target account not present - inserting from active");
3361 vacant.insert(acc.clone());
3364 }
3365 Entry::Occupied(mut occupied) => {
3366 trace!("target account present - merging storage slots");
3367 let fork_account = occupied.get_mut();
3370 fork_account.storage.extend(&acc.storage);
3371 }
3372 }
3373}
3374
3375fn is_contract_in_state(evm_state: &EvmState, acc: Address) -> bool {
3377 evm_state.get(&acc).is_some_and(|acc| !acc.info.is_empty_code_hash())
3378}
3379
3380fn update_env_block<N: Network, SPEC: Into<SpecId> + Copy, BLOCK: FoundryBlock>(
3382 evm_env: &mut EvmEnv<SPEC, BLOCK>,
3383 block: &N::BlockResponse,
3384 source_chain_id: ChainId,
3385 networks: NetworkConfigs,
3386) {
3387 let header = block.header();
3388 let block_env = &mut evm_env.block_env;
3389 block_env.set_timestamp(U256::from(header.timestamp()));
3390 block_env.set_beneficiary(header.beneficiary());
3391 block_env.set_difficulty(header.difficulty());
3392 block_env.set_prevrandao(header.mix_hash());
3393 block_env.set_basefee(header.base_fee_per_gas().unwrap_or_default());
3394 block_env.set_gas_limit(header.gas_limit());
3395 block_env.set_number(U256::from(header.number()));
3396 block_env.set_slot_num(header.slot_number().unwrap_or_default());
3397
3398 apply_chain_and_block_specific_env_changes_for_chain::<N, _, _>(
3399 evm_env,
3400 block,
3401 source_chain_id,
3402 networks,
3403 );
3404}
3405
3406fn commit_transaction<FEN: FoundryEvmNetwork>(
3409 transaction: TransactionInputs<FEN>,
3410 journaled_state: &mut JournaledState,
3411 fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
3412 fork_id: &ForkId,
3413 networks: NetworkConfigs,
3414 persistent_accounts: &AddressSet,
3415 inspector: &mut dyn for<'db> FoundryInspectorExt<
3416 <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
3417 >,
3418) -> eyre::Result<()> {
3419 let TransactionInputs {
3420 evm_env,
3421 tx_env,
3422 chain_context,
3423 rpc_block_number,
3424 journaled_state: replay_journaled_state,
3425 } = transaction;
3426 let now = Instant::now();
3427 let base_state = replay_journaled_state.state.clone();
3428 let res = {
3429 let fork = fork.clone();
3430 let depth = journaled_state.depth;
3431 let mut db: Backend<FEN> =
3432 Backend::new_with_fork(fork_id, fork, replay_journaled_state, networks)?;
3433 db.fork_block_number_override = Some(rpc_block_number);
3434 db.commit(base_state.clone());
3435
3436 let mut evm = FEN::EvmFactory::default()
3437 .create_nested_evm_with_inspector(&mut db, evm_env, inspector);
3438 *evm.chain_mut() = chain_context;
3439 evm.journal_inner_mut().depth = depth + 1;
3440 evm.transact_raw(tx_env).wrap_err("backend: failed committing transaction")?
3441 };
3442 trace!(elapsed = ?now.elapsed(), "transacted transaction");
3443
3444 apply_state_changeset(base_state, res.state, journaled_state, fork, persistent_accounts)?;
3445 Ok(())
3446}
3447
3448pub fn update_state<DB: Database>(
3451 state: &mut EvmState,
3452 db: &mut DB,
3453 persistent_accounts: Option<&AddressSet>,
3454) -> Result<(), DB::Error> {
3455 for (addr, acc) in state.iter_mut() {
3456 if persistent_accounts.is_none_or(|accounts| !accounts.contains(addr)) {
3457 acc.info = db.basic(*addr)?.unwrap_or_default();
3458 for (key, val) in &mut acc.storage {
3459 val.present_value = db.storage(*addr, *key)?;
3460 }
3461 }
3462 }
3463
3464 Ok(())
3465}
3466
3467fn apply_state_changeset<N: Network, B: ForkBlockEnv>(
3470 base_state: EvmState,
3471 state: EvmState,
3472 journaled_state: &mut JournaledState,
3473 fork: &mut Fork<N, B>,
3474 persistent_accounts: &AddressSet,
3475) -> Result<(), BackendError> {
3476 let mut staged_db = fork.db.clone();
3479 let mut staged_journaled_state = journaled_state.clone();
3480 let mut staged_fork_journaled_state = fork.journaled_state.clone();
3481 let unchanged_persistent_accounts =
3482 persistent_accounts.iter().filter(|addr| !state.contains_key(*addr)).copied().collect();
3483 staged_db.commit(base_state);
3484 staged_db.commit(state);
3485 update_state(
3486 &mut staged_journaled_state.state,
3487 &mut staged_db,
3488 Some(&unchanged_persistent_accounts),
3489 )?;
3490 update_state(
3491 &mut staged_fork_journaled_state.state,
3492 &mut staged_db,
3493 Some(&unchanged_persistent_accounts),
3494 )?;
3495
3496 fork.db = staged_db;
3497 *journaled_state = staged_journaled_state;
3498 fork.journaled_state = staged_fork_journaled_state;
3499 Ok(())
3500}
3501
3502fn inject_replay_precompiles(
3503 networks: NetworkConfigs,
3504 precompiles: &mut PrecompilesMap,
3505 chain_id: ChainId,
3506 timestamp: u64,
3507) {
3508 networks.inject_precompiles(precompiles);
3509 apply_bsc_p256_precompile(precompiles, chain_id, timestamp);
3510}
3511
3512#[cfg(test)]
3513mod tests {
3514 use super::{Fork, ForkAccountField, ReplayInputs, apply_state_changeset, update_env_block};
3515 use crate::{
3516 backend::{Backend, DatabaseExt, ForkPosition},
3517 evm::EthEvmNetwork,
3518 fork::{CreateFork, ForkId, MultiFork},
3519 opts::EvmOpts,
3520 };
3521 use alloy_consensus::{Signed, TxEnvelope, TxLegacy, transaction::Recovered};
3522 use alloy_eips::BlockNumHash;
3523 use alloy_evm::EvmEnv;
3524 use alloy_network::{
3525 AnyHeader, AnyNetwork, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope,
3526 AnyTxType, TransactionBuilder, UnknownTxEnvelope, UnknownTypedTransaction,
3527 };
3528 use alloy_primitives::{
3529 Address, B256, Bytes, Signature, TxKind, U256, address, keccak256, map::AddressSet,
3530 };
3531 use alloy_provider::{Provider, ProviderBuilder, mock::Asserter};
3532 use alloy_rpc_types::{
3533 Block, BlockTransactions, Transaction as RpcTransaction, TransactionRequest,
3534 };
3535 use alloy_serde::WithOtherFields;
3536 use alloy_sol_types::SolValue;
3537 use anvil::{NodeConfig, spawn};
3538 use foundry_common::{SYSTEM_TRANSACTION_TYPE, provider::get_http_provider};
3539 use foundry_config::{Config, NamedChain};
3540 use foundry_evm_networks::{NetworkConfigs, celo::transfer::CELO_TRANSFER_ADDRESS};
3541 use foundry_fork_db::{
3542 SharedBackend,
3543 cache::{BlockchainDb, BlockchainDbMeta},
3544 };
3545 use revm::{
3546 context::{BlockEnv, JournalInner},
3547 database::{AccountState, CacheDB, DatabaseRef, DbAccount},
3548 primitives::{KECCAK_EMPTY, hardfork::SpecId},
3549 state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
3550 };
3551
3552 use std::sync::Arc;
3553
3554 #[cfg(feature = "base")]
3555 use crate::evm::{BaseEvmNetwork, base::base_code_sentinel_addresses};
3556 #[cfg(feature = "base")]
3557 use alloy_primitives::bytes;
3558 #[cfg(feature = "base")]
3559 use base_common_chains::ChainConfig;
3560 #[cfg(feature = "base")]
3561 use base_common_consensus::{
3562 BaseTransactionInfo, BaseTxEnvelope, Predeploys, TxDeposit as BaseTxDeposit,
3563 };
3564 #[cfg(feature = "base")]
3565 use base_common_evm::{BaseSpecId, BaseUpgrade};
3566 #[cfg(feature = "base")]
3567 use base_common_precompiles::ActivationRegistryStorage;
3568 #[cfg(feature = "base")]
3569 use base_common_rpc_types::Transaction as BaseRpcTransaction;
3570
3571 #[cfg(feature = "optimism")]
3572 use crate::evm::OpEvmNetwork;
3573 #[cfg(feature = "optimism")]
3574 use op_alloy_consensus::{
3575 OpTxEnvelope, TxDeposit as OpTxDeposit, transaction::OpTransactionInfo,
3576 };
3577 #[cfg(feature = "optimism")]
3578 use op_alloy_rpc_types::Transaction as OpRpcTransaction;
3579
3580 #[cfg(any(feature = "base", feature = "optimism"))]
3581 use alloy_consensus::Sealed;
3582 #[cfg(any(feature = "base", feature = "optimism"))]
3583 use alloy_eips::Typed2718;
3584
3585 #[cfg(feature = "monad")]
3586 use super::{ContextUpdate, ensure_block_identity};
3587 #[cfg(feature = "monad")]
3588 use crate::evm::monad::BlockContext;
3589 #[cfg(feature = "monad")]
3590 use monad_revm::{
3591 MonadHardfork,
3592 api::block::syscall_snapshot_calldata,
3593 staking::{STAKING_ADDRESS, constants::SYSTEM_ADDRESS},
3594 };
3595
3596 fn fork_with_closed_backend() -> Fork<AnyNetwork, BlockEnv> {
3597 let provider =
3598 ProviderBuilder::<_, _, AnyNetwork>::default().connect_mocked_client(Asserter::new());
3599 let db = BlockchainDb::new(
3600 BlockchainDbMeta::new(BlockEnv::default(), "http://localhost".to_string()),
3601 None,
3602 );
3603 let (backend, handler) = SharedBackend::new(provider, db, None);
3604 drop(handler);
3605 Fork {
3606 db: CacheDB::new(backend),
3607 journaled_state: JournalInner::new(),
3608 source_chain_id: 1,
3609 position: ForkPosition::AfterBlock { block: BlockNumHash::default() },
3610 block_env: None,
3611 }
3612 }
3613
3614 fn rpc_block(number: u64, hash: B256, parent_hash: B256) -> AnyRpcBlock {
3615 let header = AnyHeader { number, parent_hash, ..Default::default() };
3616 AnyRpcBlock::new(
3617 Block::new(
3618 AnyRpcHeader::from_sealed(header.seal(hash)),
3619 BlockTransactions::Full(Vec::new()),
3620 )
3621 .into(),
3622 )
3623 }
3624
3625 fn rpc_transaction(
3626 caller: Address,
3627 nonce: u64,
3628 value: u64,
3629 gas_limit: u64,
3630 recipient: Address,
3631 hash: B256,
3632 ) -> AnyRpcTransaction {
3633 let tx = TxLegacy {
3634 nonce,
3635 gas_limit,
3636 to: TxKind::Call(recipient),
3637 value: U256::from(value),
3638 ..Default::default()
3639 };
3640 let signed = Signed::new_unchecked(tx, Signature::new(U256::ONE, U256::ONE, false), hash);
3641 AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
3642 inner: Recovered::new_unchecked(
3643 AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(signed)),
3644 caller,
3645 ),
3646 block_hash: None,
3647 block_number: None,
3648 transaction_index: None,
3649 effective_gas_price: None,
3650 block_timestamp: None,
3651 }))
3652 }
3653
3654 #[cfg(feature = "base")]
3655 #[tokio::test(flavor = "multi_thread")]
3656 async fn base_fork_prefix_replay_uses_source_chain_activation_admin() {
3657 let sender = Address::with_last_byte(0x42);
3658 let recorder = Address::with_last_byte(0x43);
3659 let target = B256::with_last_byte(2);
3660 let admin =
3661 ChainConfig::activation_admin_address_for_upgrade_by_chain_id(8453, BaseUpgrade::Beryl)
3662 .unwrap();
3663 let code = revm::bytecode::Bytecode::new_legacy(bytes!(
3665 "63f851a44060e01b60005260206000600460007384530000000000000000000000000000000000015afa60325760006000fd5b60005160005500"
3666 ));
3667 for chain_id in [8453, 31337, 84532] {
3668 let forks = MultiFork::<AnyNetwork, BaseSpecId, BlockEnv>::spawn();
3669 let mut fork = fork_with_closed_backend();
3670 fork.source_chain_id = 8453;
3671 fork.db.insert_account_info(
3672 sender,
3673 AccountInfo { balance: U256::from(10).pow(U256::from(18)), ..Default::default() },
3674 );
3675 fork.db.insert_account_info(recorder, AccountInfo::default().with_code(code.clone()));
3676 for address in base_code_sentinel_addresses(BaseUpgrade::Beryl).chain([
3677 Address::ZERO,
3678 Predeploys::L1_BLOCK_INFO,
3679 Predeploys::BASE_FEE_VAULT,
3680 Predeploys::L1_FEE_VAULT,
3681 Predeploys::OPERATOR_FEE_VAULT,
3682 ]) {
3683 fork.db.insert_account_info(address, AccountInfo::default());
3684 }
3685 for account in fork.db.cache.accounts.values_mut() {
3686 account.account_state = AccountState::StorageCleared;
3687 }
3688 let mut block = rpc_block(1, B256::with_last_byte(1), B256::ZERO);
3689 block.inner.transactions = BlockTransactions::Full(vec![
3690 rpc_transaction(sender, 0, 0, 200_000, recorder, B256::with_last_byte(1)),
3691 rpc_transaction(sender, 1, 0, 21_000, recorder, target),
3692 ]);
3693 let mut cfg = revm::context::CfgEnv::new_with_spec(BaseSpecId::new(BaseUpgrade::Beryl));
3694 cfg.chain_id = chain_id;
3695 let result = Backend::<BaseEvmNetwork>::replay_until(
3696 &mut fork,
3697 ReplayInputs {
3698 fork_id: ForkId::new("http://localhost", Some(0)),
3699 forks,
3700 evm_env: EvmEnv::new(cfg, BlockEnv::default()),
3701 networks: NetworkConfigs::with_base(),
3702 },
3703 &block,
3704 #[cfg(feature = "monad")]
3705 None,
3706 target,
3707 &mut JournalInner::new(),
3708 &AddressSet::default(),
3709 )
3710 .unwrap();
3711 assert!(result.is_some());
3712 assert_eq!(
3713 fork.db.storage_ref(recorder, U256::ZERO).unwrap(),
3714 U256::from_be_slice(admin.as_slice()),
3715 "execution chain {chain_id}"
3716 );
3717 assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().nonce, 1);
3718 }
3719 }
3720
3721 #[test]
3722 fn fork_replay_backend_reuses_manager_on_current_thread_runtime() {
3723 let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
3724
3725 let forks = runtime.block_on(async {
3726 let forks = MultiFork::<AnyNetwork, SpecId, BlockEnv>::spawn();
3727 let sender = Address::with_last_byte(0x42);
3728 let recipient = Address::with_last_byte(0x43);
3729 let target = B256::with_last_byte(2);
3730 let mut fork = fork_with_closed_backend();
3731 fork.db.insert_account_info(
3732 sender,
3733 AccountInfo { balance: U256::from(100), ..Default::default() },
3734 );
3735 fork.db.insert_account_info(recipient, AccountInfo::default());
3736 fork.db.insert_account_info(Address::ZERO, AccountInfo::default());
3737 let mut block = rpc_block(1, B256::with_last_byte(1), B256::ZERO);
3738 block.inner.transactions = BlockTransactions::Full(vec![
3739 rpc_transaction(sender, 0, 7, 21_000, recipient, B256::with_last_byte(1)),
3740 rpc_transaction(sender, 1, 0, 21_000, recipient, target),
3741 ]);
3742
3743 let result = Backend::<EthEvmNetwork>::replay_until(
3744 &mut fork,
3745 ReplayInputs {
3746 fork_id: ForkId::new("http://localhost", Some(0)),
3747 forks: forks.clone(),
3748 evm_env: EvmEnv::default(),
3749 networks: NetworkConfigs::default(),
3750 },
3751 &block,
3752 #[cfg(feature = "monad")]
3753 None,
3754 target,
3755 &mut JournalInner::new(),
3756 &AddressSet::default(),
3757 )
3758 .unwrap();
3759
3760 assert!(result.is_some());
3761 assert_eq!(fork.db.basic_ref(recipient).unwrap().unwrap().balance, U256::from(7));
3762 assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().nonce, 1);
3763 forks
3764 });
3765 drop(runtime);
3766 drop(forks);
3767 }
3768
3769 #[test]
3770 fn fork_replay_keeps_system_skips_and_prefix_atomicity() {
3771 let sender = Address::with_last_byte(0x42);
3772 let recipient = Address::with_last_byte(0x43);
3773 let system = address!("6f49a8f621353f12378d0046e7d7e4b9b249dc9e");
3774 let target = B256::with_last_byte(4);
3775
3776 #[cfg(not(feature = "monad"))]
3777 let contexts = [false];
3778 #[cfg(feature = "monad")]
3779 let contexts = [false, true];
3780 for _with_context in contexts {
3781 for invalid_nonce in [false, true] {
3782 let mut fork = fork_with_closed_backend();
3783 fork.db.insert_account_info(
3784 sender,
3785 AccountInfo { balance: U256::from(100), ..Default::default() },
3786 );
3787 fork.db.insert_account_info(recipient, AccountInfo::default());
3788 fork.db.insert_account_info(Address::ZERO, AccountInfo::default());
3789 let block = AnyRpcBlock::new(
3790 Block::new(
3791 AnyRpcHeader::from_sealed(
3792 AnyHeader { number: 1, ..Default::default() }
3793 .seal(B256::with_last_byte(1)),
3794 ),
3795 BlockTransactions::Full(vec![
3796 rpc_transaction(
3797 sender,
3798 0,
3799 7,
3800 21_000,
3801 recipient,
3802 B256::with_last_byte(1),
3803 ),
3804 rpc_transaction(system, 0, 0, 0, recipient, B256::with_last_byte(2)),
3807 rpc_transaction(
3808 sender,
3809 if invalid_nonce { 0 } else { 1 },
3810 5,
3811 21_000,
3812 recipient,
3813 B256::with_last_byte(3),
3814 ),
3815 rpc_transaction(sender, 2, 2, 21_000, recipient, target),
3816 ]),
3817 )
3818 .into(),
3819 );
3820 let networks = NetworkConfigs::default();
3821 #[cfg(feature = "monad")]
3822 let networks = if _with_context { NetworkConfigs::with_monad() } else { networks };
3823 #[cfg(feature = "monad")]
3824 let context = _with_context.then(|| {
3825 BlockContext::<EthEvmNetwork>::new(Vec::new(), Vec::new(), Vec::new())
3826 });
3827 let result = Backend::<EthEvmNetwork>::replay_until(
3828 &mut fork,
3829 ReplayInputs {
3830 fork_id: ForkId::new("http://localhost", Some(0)),
3831 forks: MultiFork::spawn(),
3832 evm_env: EvmEnv::default(),
3833 networks,
3834 },
3835 &block,
3836 #[cfg(feature = "monad")]
3837 context.as_ref(),
3838 target,
3839 &mut JournalInner::new(),
3840 &AddressSet::default(),
3841 );
3842 if invalid_nonce {
3843 assert!(result.is_err());
3844 assert_eq!(fork.db.basic_ref(recipient).unwrap().unwrap().balance, U256::ZERO);
3845 assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().nonce, 0);
3846 } else {
3847 assert!(result.unwrap().is_some());
3848 assert_eq!(
3849 fork.db.basic_ref(recipient).unwrap().unwrap().balance,
3850 U256::from(12)
3851 );
3852 assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().nonce, 2);
3853 }
3854 }
3855 }
3856 }
3857
3858 #[test]
3859 #[cfg(feature = "monad")]
3860 fn fork_replay_executes_monad_system_prefix_with_or_without_profile() {
3861 let transaction = |nonce, hash| {
3862 let tx = TxLegacy {
3863 nonce,
3864 to: TxKind::Call(STAKING_ADDRESS),
3865 input: syscall_snapshot_calldata(),
3866 ..Default::default()
3867 };
3868 let signed =
3869 Signed::new_unchecked(tx, Signature::new(U256::ONE, U256::ONE, false), hash);
3870 AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
3871 inner: Recovered::new_unchecked(
3872 AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(signed)),
3873 SYSTEM_ADDRESS,
3874 ),
3875 block_hash: None,
3876 block_number: None,
3877 transaction_index: None,
3878 effective_gas_price: None,
3879 block_timestamp: None,
3880 }))
3881 };
3882 let target = B256::with_last_byte(2);
3883 let mut block = rpc_block(1, B256::with_last_byte(1), B256::ZERO);
3884 block.inner.transactions = BlockTransactions::Full(vec![
3885 transaction(3, B256::with_last_byte(1)),
3886 transaction(4, target),
3887 ]);
3888 for with_context in [false, true] {
3889 let mut fork = fork_with_closed_backend();
3890 fork.db.insert_account_info(
3891 SYSTEM_ADDRESS,
3892 AccountInfo { nonce: 3, ..Default::default() },
3893 );
3894 fork.db.insert_account_info(STAKING_ADDRESS, AccountInfo::default());
3895 fork.db.replace_account_storage(STAKING_ADDRESS, Default::default()).unwrap();
3896 let context = with_context.then(|| {
3897 BlockContext::<crate::evm::MonadEvmNetwork>::new(Vec::new(), Vec::new(), Vec::new())
3898 });
3899 let result = Backend::<crate::evm::MonadEvmNetwork>::replay_until(
3900 &mut fork,
3901 ReplayInputs {
3902 fork_id: ForkId::new("http://localhost", Some(0)),
3903 forks: MultiFork::spawn(),
3904 evm_env: EvmEnv::new(
3905 revm::context::CfgEnv::new_with_spec(MonadHardfork::MonadNine),
3906 BlockEnv::default(),
3907 ),
3908 networks: if with_context {
3909 NetworkConfigs::with_monad()
3910 } else {
3911 NetworkConfigs::default()
3912 },
3913 },
3914 &block,
3915 context.as_ref(),
3916 target,
3917 &mut JournalInner::new(),
3918 &AddressSet::default(),
3919 );
3920 assert!(result.unwrap().is_some());
3921 assert_eq!(fork.db.basic_ref(SYSTEM_ADDRESS).unwrap().unwrap().nonce, 4);
3922 }
3923 }
3924
3925 #[test]
3926 #[cfg(feature = "monad")]
3927 fn validates_block_identity() {
3928 let hash = B256::with_last_byte(2);
3929 let block = rpc_block(2, hash, B256::with_last_byte(1));
3930 assert!(ensure_block_identity(&block, BlockNumHash::new(2, hash), "parent").is_ok());
3931
3932 let err =
3933 ensure_block_identity(&block, BlockNumHash::new(2, B256::with_last_byte(3)), "parent")
3934 .unwrap_err();
3935 assert!(err.to_string().contains("parent block changed"));
3936
3937 let err =
3938 ensure_block_identity(&block, BlockNumHash::new(1, hash), "grandparent").unwrap_err();
3939 assert!(err.to_string().contains("grandparent block changed"));
3940 }
3941
3942 #[test]
3943 fn failed_fork_state_refresh_does_not_publish_transaction_changes() {
3944 let mut fork = fork_with_closed_backend();
3945 let externally_loaded = Address::with_last_byte(1);
3946 let fork_loaded = Address::with_last_byte(2);
3947 let committed = Address::with_last_byte(3);
3948 let seeded = Address::with_last_byte(4);
3949 let missing_slot = U256::ONE;
3950
3951 let cached_external = AccountInfo { balance: U256::from(11), ..Default::default() };
3952 let cached_fork = AccountInfo { balance: U256::from(12), ..Default::default() };
3953 let cached_seeded = AccountInfo { balance: U256::from(14), ..Default::default() };
3954 fork.db.insert_account_info(externally_loaded, cached_external);
3955 fork.db.insert_account_info(fork_loaded, cached_fork);
3956 fork.db.insert_account_info(seeded, cached_seeded);
3957
3958 let mut journaled_state = JournalInner::new();
3959 let external_account =
3960 Account::default().with_info(AccountInfo { balance: U256::ONE, ..Default::default() });
3961 journaled_state.state.insert(externally_loaded, external_account);
3962
3963 let mut fork_account = Account::default()
3964 .with_info(AccountInfo { balance: U256::from(2), ..Default::default() });
3965 fork_account
3966 .storage
3967 .insert(missing_slot, EvmStorageSlot::new(U256::ZERO, TransactionId::ZERO));
3968 fork.journaled_state.state.insert(fork_loaded, fork_account);
3969
3970 let mut committed_account = Account::default()
3971 .with_info(AccountInfo { balance: U256::from(13), ..Default::default() });
3972 committed_account.mark_touch();
3973 let mut state = EvmState::default();
3974 state.insert(committed, committed_account);
3975
3976 let mut seeded_account = Account::default()
3977 .with_info(AccountInfo { balance: U256::from(15), ..Default::default() });
3978 seeded_account.mark_touch();
3979 let mut base_state = EvmState::default();
3980 base_state.insert(seeded, seeded_account);
3981
3982 let result = apply_state_changeset(
3983 base_state,
3984 state,
3985 &mut journaled_state,
3986 &mut fork,
3987 &AddressSet::default(),
3988 );
3989 assert!(result.is_err());
3990 assert!(!fork.db.cache.accounts.contains_key(&committed));
3991 assert_eq!(fork.db.basic_ref(seeded).unwrap().unwrap().balance, U256::from(14));
3992 assert_eq!(journaled_state.state[&externally_loaded].info.balance, U256::ONE);
3993 assert_eq!(fork.journaled_state.state[&fork_loaded].info.balance, U256::from(2));
3994 }
3995
3996 #[test]
3997 fn failed_fork_state_refresh_preserves_not_existing_account() {
3998 let mut fork = fork_with_closed_backend();
3999 let address = Address::with_last_byte(1);
4000 let missing_slot = U256::ONE;
4001 fork.db.cache.accounts.insert(address, DbAccount::new_not_existing());
4002
4003 let mut journaled_state = JournalInner::new();
4004 let mut journaled_account =
4005 Account::default().with_info(AccountInfo { balance: U256::ONE, ..Default::default() });
4006 journaled_account
4007 .storage
4008 .insert(missing_slot, EvmStorageSlot::new(U256::from(7), TransactionId::ZERO));
4009 journaled_state.state.insert(address, journaled_account);
4010
4011 let mut touched_account = Account::default()
4012 .with_info(AccountInfo { balance: U256::from(13), ..Default::default() });
4013 touched_account.mark_touch();
4014 let mut state = EvmState::default();
4015 state.insert(address, touched_account);
4016
4017 let result = apply_state_changeset(
4018 EvmState::default(),
4019 state,
4020 &mut journaled_state,
4021 &mut fork,
4022 &AddressSet::default(),
4023 );
4024 assert!(result.is_err());
4025 assert_eq!(fork.db.cache.accounts[&address].account_state, AccountState::NotExisting);
4026 assert_eq!(journaled_state.state[&address].info.balance, U256::ONE);
4027 assert_eq!(
4028 journaled_state.state[&address].storage[&missing_slot].present_value(),
4029 U256::from(7)
4030 );
4031 }
4032
4033 #[tokio::test(flavor = "multi_thread")]
4034 async fn refresh_fork_account_updates_loaded_journals() {
4035 let (api, handle) = spawn(NodeConfig::test()).await;
4036 let target = address!("0x0000000000000000000000000000000000001331");
4037 let code =
4038 Bytes::from_static(&[0x60, 0x2a, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3]);
4039
4040 let provider = handle.http_provider();
4041 let block_number = provider.get_block_number().await.unwrap();
4042 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
4043 evm_opts.fork_url = Some(handle.http_endpoint());
4044 evm_opts.fork_block_number = Some(block_number);
4045 let fork = evm_opts.get_fork(&Config::default(), 31_337, Some(block_number)).unwrap();
4046 let mut backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
4047
4048 let mut journaled_state = JournalInner::new();
4049 journaled_state.load_account(&mut backend, target).unwrap();
4050 journaled_state.state.get_mut(&target).unwrap().info.balance = U256::ONE;
4051 let fork = backend.active_fork_mut().unwrap();
4052 fork.journaled_state.load_account(&mut fork.db, target).unwrap();
4053 fork.journaled_state.state.get_mut(&target).unwrap().info.balance = U256::from(2);
4054 let cached = fork.db.cache.accounts.get_mut(&target).unwrap();
4055 cached.info.balance = U256::from(3);
4056 cached.account_state = AccountState::Touched;
4057 assert_eq!(journaled_state.state[&target].info.code_hash, KECCAK_EMPTY);
4058 assert_eq!(fork.journaled_state.state[&target].info.code_hash, KECCAK_EMPTY);
4059
4060 api.anvil_set_code(target, code.clone()).await.unwrap();
4061 backend.refresh_fork_account(target, ForkAccountField::Code, &mut journaled_state).unwrap();
4062
4063 let expected_hash = keccak256(&code);
4064 let refreshed = &journaled_state.state[&target].info;
4065 assert_eq!(refreshed.code_hash(), expected_hash);
4066 assert_eq!(refreshed.code.as_ref().unwrap().original_bytes(), code);
4067 assert_eq!(refreshed.balance, U256::ONE);
4068 let refreshed = &backend.active_fork().unwrap().journaled_state.state[&target].info;
4069 assert_eq!(refreshed.code_hash(), expected_hash);
4070 assert_eq!(refreshed.code.as_ref().unwrap().original_bytes(), code);
4071 assert_eq!(refreshed.balance, U256::from(2));
4072 let cached = &backend.active_fork().unwrap().db.cache.accounts[&target];
4073 assert_eq!(cached.info.code_hash(), expected_hash);
4074 assert_eq!(cached.info.balance, U256::from(3));
4075 }
4076
4077 #[test]
4078 fn ethereum_replay_skips_unknown_system_envelopes_before_conversion() {
4079 let transaction = |ty| {
4080 let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
4081 hash: B256::ZERO,
4082 inner: UnknownTypedTransaction {
4083 ty: AnyTxType(ty),
4084 fields: Default::default(),
4085 memo: Default::default(),
4086 },
4087 });
4088 AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
4089 inner: Recovered::new_unchecked(unknown, Address::with_last_byte(0x42)),
4090 block_hash: None,
4091 block_number: None,
4092 transaction_index: None,
4093 effective_gas_price: None,
4094 block_timestamp: None,
4095 }))
4096 };
4097
4098 assert!(
4099 Backend::<EthEvmNetwork>::replay_tx_env(&transaction(SYSTEM_TRANSACTION_TYPE))
4100 .unwrap()
4101 .is_none()
4102 );
4103 assert!(Backend::<EthEvmNetwork>::replay_tx_env(&transaction(0xff)).is_err());
4104 }
4105
4106 #[cfg(feature = "optimism")]
4107 #[test]
4108 fn optimism_fork_transaction_hash_replay_executes_deposit_prefix() {
4109 let sender = Address::with_last_byte(0xaa);
4110 let recipient = Address::with_last_byte(0xbb);
4111 let deposit = OpTxDeposit {
4112 source_hash: B256::repeat_byte(0x11),
4113 from: sender,
4114 to: TxKind::Call(recipient),
4115 mint: 1_000,
4116 value: U256::from(600),
4117 gas_limit: 100_000,
4118 is_system_transaction: false,
4119 input: Bytes::new(),
4120 };
4121 let rpc = OpRpcTransaction::from_transaction(
4122 Recovered::new_unchecked(OpTxEnvelope::Deposit(Sealed::new(deposit)), sender),
4123 OpTransactionInfo::default(),
4124 );
4125 let deposit_rpc =
4126 serde_json::from_value::<AnyRpcTransaction>(serde_json::to_value(rpc).unwrap())
4127 .unwrap();
4128 assert_eq!(deposit_rpc.ty(), SYSTEM_TRANSACTION_TYPE);
4129
4130 let target = B256::with_last_byte(2);
4131 let mut block = rpc_block(1, B256::with_last_byte(1), B256::ZERO);
4132 block.inner.transactions = BlockTransactions::Full(vec![
4133 rpc_transaction(
4134 address!("6f49a8f621353f12378d0046e7d7e4b9b249dc9e"),
4135 0,
4136 0,
4137 0,
4138 recipient,
4139 B256::with_last_byte(3),
4140 ),
4141 deposit_rpc,
4142 rpc_transaction(sender, 0, 0, 21_000, recipient, target),
4143 ]);
4144 let mut fork = fork_with_closed_backend();
4145 fork.db.insert_account_info(sender, AccountInfo::default());
4146 fork.db.insert_account_info(recipient, AccountInfo::default());
4147
4148 let result = Backend::<OpEvmNetwork>::replay_until(
4149 &mut fork,
4150 ReplayInputs {
4151 fork_id: ForkId::new("http://localhost", Some(0)),
4152 forks: MultiFork::spawn(),
4153 evm_env: EvmEnv::default(),
4154 networks: NetworkConfigs::with_optimism(),
4155 },
4156 &block,
4157 #[cfg(feature = "monad")]
4158 None,
4159 target,
4160 &mut JournalInner::new(),
4161 &AddressSet::default(),
4162 )
4163 .unwrap();
4164
4165 assert!(result.is_some());
4166 assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().balance, U256::from(400));
4167 assert_eq!(fork.db.basic_ref(recipient).unwrap().unwrap().balance, U256::from(600));
4168 }
4169
4170 #[cfg(feature = "base")]
4171 #[test]
4172 fn base_fork_replay_refreshes_accounts_after_deposit() {
4173 let deposit_sender = Address::with_last_byte(0xaa);
4174 let sender = Address::with_last_byte(0xbb);
4175 let destroyer = Address::with_last_byte(0xcc);
4176 let recorder = Address::with_last_byte(0xdd);
4177 let registry = ActivationRegistryStorage::ADDRESS;
4178
4179 let mut destroyer_code = vec![0x73];
4181 destroyer_code.extend(registry);
4182 destroyer_code.push(0xff);
4183 let destroyer_code = revm::bytecode::Bytecode::new_legacy(destroyer_code.into());
4184
4185 let mut recorder_code = vec![0x73];
4187 recorder_code.extend(registry);
4188 recorder_code.extend([0x31, 0x5f, 0x55, 0x00]);
4189 let recorder_code = revm::bytecode::Bytecode::new_legacy(recorder_code.into());
4190
4191 let deposit = BaseTxDeposit {
4192 source_hash: B256::repeat_byte(0x11),
4193 from: deposit_sender,
4194 to: TxKind::Call(destroyer),
4195 mint: 1_000,
4196 value: U256::from(600),
4197 gas_limit: 100_000,
4198 is_system_transaction: false,
4199 input: Bytes::new(),
4200 };
4201 let rpc = BaseRpcTransaction::from_transaction(
4202 Recovered::new_unchecked(BaseTxEnvelope::Deposit(Sealed::new(deposit)), deposit_sender),
4203 BaseTransactionInfo::default(),
4204 );
4205 let deposit_rpc =
4206 serde_json::from_value::<AnyRpcTransaction>(serde_json::to_value(rpc).unwrap())
4207 .unwrap();
4208 assert_eq!(deposit_rpc.ty(), SYSTEM_TRANSACTION_TYPE);
4209
4210 let target = B256::with_last_byte(3);
4211 let mut block = rpc_block(1, B256::with_last_byte(1), B256::ZERO);
4212 block.inner.transactions = BlockTransactions::Full(vec![
4213 deposit_rpc,
4214 rpc_transaction(sender, 0, 0, 100_000, recorder, B256::with_last_byte(2)),
4215 rpc_transaction(sender, 1, 0, 21_000, recorder, target),
4216 ]);
4217
4218 let mut fork = fork_with_closed_backend();
4219 fork.db.insert_account_info(deposit_sender, AccountInfo::default());
4220 fork.db.insert_account_info(sender, AccountInfo::default());
4221 fork.db.insert_account_info(destroyer, AccountInfo::default().with_code(destroyer_code));
4222 fork.db.insert_account_info(recorder, AccountInfo::default().with_code(recorder_code));
4223 let sentinel = revm::bytecode::Bytecode::new_legacy(Bytes::from_static(
4224 crate::constants::SYSTEM_PRECOMPILE_STUB,
4225 ));
4226 fork.db.insert_account_info(registry, AccountInfo::default().with_code(sentinel));
4227 for address in base_code_sentinel_addresses(BaseUpgrade::Beryl).chain([
4228 Address::ZERO,
4229 Predeploys::L1_BLOCK_INFO,
4230 Predeploys::BASE_FEE_VAULT,
4231 Predeploys::L1_FEE_VAULT,
4232 Predeploys::OPERATOR_FEE_VAULT,
4233 ]) {
4234 fork.db.cache.accounts.entry(address).or_default();
4235 }
4236 for account in fork.db.cache.accounts.values_mut() {
4237 account.account_state = AccountState::StorageCleared;
4238 }
4239
4240 let mut cfg = revm::context::CfgEnv::new_with_spec(BaseSpecId::new(BaseUpgrade::Beryl));
4241 cfg.chain_id = 8453;
4242 let result = Backend::<BaseEvmNetwork>::replay_until(
4243 &mut fork,
4244 ReplayInputs {
4245 fork_id: ForkId::new("http://localhost", Some(0)),
4246 forks: MultiFork::spawn(),
4247 evm_env: EvmEnv::new(cfg, BlockEnv::default()),
4248 networks: NetworkConfigs::with_base(),
4249 },
4250 &block,
4251 #[cfg(feature = "monad")]
4252 None,
4253 target,
4254 &mut JournalInner::new(),
4255 &AddressSet::default(),
4256 )
4257 .unwrap();
4258
4259 assert!(result.is_some());
4260 assert_eq!(fork.db.basic_ref(deposit_sender).unwrap().unwrap().balance, U256::from(400));
4261 assert_eq!(fork.db.basic_ref(registry).unwrap().unwrap().balance, U256::from(600));
4262 assert_eq!(fork.db.storage_ref(recorder, U256::ZERO).unwrap(), U256::from(600));
4263 }
4264
4265 #[tokio::test(flavor = "multi_thread")]
4266 async fn celo_transaction_hash_fork_replays_transfer_precompile() {
4267 let networks = NetworkConfigs::with_celo();
4268 let (api, handle) = spawn(
4269 NodeConfig::test().with_chain_id(Some(NamedChain::Celo as u64)).with_networks(networks),
4270 )
4271 .await;
4272 let provider = handle.http_provider();
4273 let sender = provider.get_accounts().await.unwrap()[0];
4274 let recipient = Address::with_last_byte(0x99);
4275 let transfer_amount = U256::from(1_000);
4276 let target_amount = U256::ONE;
4277 let nonce = provider.get_transaction_count(sender).await.unwrap();
4278 let gas_price = provider.get_gas_price().await.unwrap();
4279
4280 api.anvil_set_auto_mine(false).await.unwrap();
4281 api.send_transaction(WithOtherFields::new(
4282 TransactionRequest::default()
4283 .with_from(sender)
4284 .with_to(CELO_TRANSFER_ADDRESS)
4285 .with_nonce(nonce)
4286 .with_gas_limit(100_000)
4287 .with_gas_price(gas_price)
4288 .with_input(Bytes::from((sender, recipient, transfer_amount).abi_encode())),
4289 ))
4290 .await
4291 .unwrap();
4292 let target_hash = api
4293 .send_transaction(WithOtherFields::new(
4294 TransactionRequest::default()
4295 .with_from(sender)
4296 .with_to(recipient)
4297 .with_nonce(nonce + 1)
4298 .with_gas_limit(21_000)
4299 .with_gas_price(gas_price)
4300 .with_value(target_amount),
4301 ))
4302 .await
4303 .unwrap();
4304 api.mine_one().await.unwrap();
4305
4306 assert_eq!(provider.get_balance(recipient).await.unwrap(), transfer_amount + target_amount);
4307
4308 let endpoint = handle.http_endpoint();
4309 let fork_block_number = provider.get_block_number().await.unwrap();
4310 let evm_opts = EvmOpts {
4311 fork_url: Some(endpoint.clone()),
4312 fork_block_number: Some(fork_block_number),
4313 networks,
4314 ..Default::default()
4315 };
4316 let fork = CreateFork { url: endpoint, enable_caching: false, evm_opts };
4317 let mut backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
4318 backend.set_networks(networks);
4319
4320 let fork_id = backend.create_fork_at_transaction(fork, target_hash).unwrap();
4321 let fork = backend.inner.get_fork_by_id(fork_id).unwrap();
4322 assert!(matches!(
4323 fork.position,
4324 ForkPosition::BeforeTransaction { transaction_index: 1, .. }
4325 ));
4326 assert_eq!(
4327 fork.db.basic_ref(recipient).unwrap().unwrap_or_default().balance,
4328 transfer_amount
4329 );
4330 }
4331
4332 #[test]
4333 fn fork_position_advances_from_exact_transaction_predecessor() {
4334 let parent_block = BlockNumHash::new(10, B256::with_last_byte(10));
4335 let block = BlockNumHash::new(11, B256::with_last_byte(11));
4336 let parent = ForkPosition::AfterBlock { block: parent_block };
4337 assert_eq!(
4338 parent.after_transaction(block, parent_block.hash, 0, 2),
4339 Some(ForkPosition::BeforeTransaction { block, transaction_index: 1 })
4340 );
4341 assert_eq!(
4342 parent.after_transaction(block, parent_block.hash, 0, 1),
4343 Some(ForkPosition::AfterBlock { block })
4344 );
4345
4346 let before_first = ForkPosition::BeforeTransaction { block, transaction_index: 0 };
4347 assert_eq!(
4348 before_first.after_transaction(block, parent_block.hash, 0, 2),
4349 Some(ForkPosition::BeforeTransaction { block, transaction_index: 1 })
4350 );
4351
4352 let before_second = ForkPosition::BeforeTransaction { block, transaction_index: 1 };
4353 assert_eq!(
4354 before_second.after_transaction(block, parent_block.hash, 1, 3),
4355 Some(ForkPosition::BeforeTransaction { block, transaction_index: 2 })
4356 );
4357 assert_eq!(
4358 before_second.after_transaction(block, parent_block.hash, 1, 2),
4359 Some(ForkPosition::AfterBlock { block })
4360 );
4361
4362 assert_eq!(parent.after_transaction(block, B256::ZERO, 0, 1), None);
4363 assert_eq!(parent.after_transaction(block, parent_block.hash, 1, 2), None);
4364 assert_eq!(before_second.after_transaction(block, parent_block.hash, 0, 3), None);
4365 assert_eq!(before_second.after_transaction(block, parent_block.hash, 2, 3), None);
4366 assert_eq!(before_second.after_transaction(block, parent_block.hash, 1, 1), None);
4367 assert_eq!(parent.after_transaction(block, parent_block.hash, 0, 0), None);
4368 }
4369
4370 #[test]
4371 fn fork_block_env_updates_slot_number() {
4372 let mut evm_env =
4373 EvmEnv::new(revm::context::CfgEnv::<SpecId>::default(), BlockEnv::default());
4374 for slot_number in [Some(42), Some(u64::MAX), None, Some(0)] {
4375 let header = AnyHeader { slot_number, ..Default::default() };
4376 let block = AnyRpcBlock::new(
4377 Block::new(
4378 AnyRpcHeader::from_sealed(header.seal(B256::ZERO)),
4379 BlockTransactions::Full(Vec::new()),
4380 )
4381 .into(),
4382 );
4383 update_env_block::<AnyNetwork, _, _>(
4384 &mut evm_env,
4385 &block,
4386 NamedChain::Mainnet as u64,
4387 NetworkConfigs::default(),
4388 );
4389 assert_eq!(evm_env.block_env.slot_num, slot_number.unwrap_or_default());
4390 }
4391 }
4392
4393 #[test]
4394 fn fork_replay_block_env_preserves_arbitrum_l1_number() {
4395 let header = AnyHeader { number: 75_219_831, ..Default::default() };
4396 let mut block = AnyRpcBlock::new(
4397 Block::new(
4398 AnyRpcHeader::from_sealed(header.seal(B256::ZERO)),
4399 BlockTransactions::Full(Vec::new()),
4400 )
4401 .into(),
4402 );
4403 block.other.insert("l1BlockNumber".to_string(), serde_json::json!("0x10276d3"));
4404 let mut evm_env =
4405 EvmEnv::new(revm::context::CfgEnv::<SpecId>::default(), BlockEnv::default());
4406
4407 update_env_block::<AnyNetwork, _, _>(
4408 &mut evm_env,
4409 &block,
4410 NamedChain::Arbitrum as u64,
4411 NetworkConfigs::default(),
4412 );
4413
4414 assert_eq!(evm_env.block_env.number, U256::from(16_938_707));
4415 }
4416
4417 #[tokio::test(flavor = "multi_thread")]
4418 async fn temporary_backend_preserves_fork_position() {
4419 let (_api, handle) = spawn(NodeConfig::test()).await;
4420 let provider = handle.http_provider();
4421 let block_number = provider.get_block_number().await.unwrap();
4422
4423 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
4424 evm_opts.fork_url = Some(handle.http_endpoint());
4425 evm_opts.fork_block_number = Some(block_number);
4426 let fork = evm_opts.get_fork(&Config::default(), 31_337, Some(block_number)).unwrap();
4427 let mut backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
4428 let id = backend.active_fork_ids.unwrap().0;
4429 let fork_id = backend.inner.ensure_fork_id(id).unwrap().clone();
4430
4431 for position in [
4432 ForkPosition::BeforeTransaction {
4433 block: BlockNumHash::new(block_number + 1, B256::with_last_byte(1)),
4434 transaction_index: 2,
4435 },
4436 ForkPosition::AfterBlock {
4437 block: BlockNumHash::new(block_number + 2, B256::with_last_byte(2)),
4438 },
4439 ] {
4440 backend.inner.get_fork_by_id_mut(id).unwrap().position = position;
4441 let fork = backend.active_fork().unwrap().clone();
4442 let journaled_state = fork.journaled_state.clone();
4443 let mut temporary = Backend::<EthEvmNetwork>::new_with_fork(
4444 &fork_id,
4445 fork,
4446 journaled_state,
4447 NetworkConfigs::default(),
4448 )
4449 .unwrap();
4450
4451 assert_eq!(temporary.active_fork().unwrap().position, position);
4452 let expected = match position {
4453 ForkPosition::AfterBlock { block }
4454 | ForkPosition::BeforeTransaction { block, .. } => block.number,
4455 };
4456 assert_eq!(temporary.active_fork_block_number(), Some(expected));
4457 temporary.fork_block_number_override = Some(expected + 1);
4458 assert_eq!(temporary.active_fork_block_number(), Some(expected + 1));
4459 }
4460 }
4461
4462 #[tokio::test(flavor = "multi_thread")]
4463 async fn fork_execution_scopes_release_their_registry() {
4464 let (_api, handle) = spawn(NodeConfig::test()).await;
4465 let request = CreateFork {
4466 url: handle.http_endpoint(),
4467 enable_caching: false,
4468 evm_opts: EvmOpts { fork_url: Some(handle.http_endpoint()), ..Default::default() },
4469 };
4470 let seed = Backend::<EthEvmNetwork>::spawn(Some(request.clone())).unwrap();
4471 let initial = seed.ensure_fork_id(seed.active_fork_id().unwrap()).unwrap().clone();
4472 let shared = seed.forks.get_fork(initial.clone()).unwrap().unwrap();
4473 let mut execution = seed.clone_with_fork_scope().unwrap();
4474 let inherited = execution.forks.get_fork(initial.clone()).unwrap().unwrap();
4475 assert!(Arc::ptr_eq(&shared.data(), &inherited.data()));
4476
4477 let local = execution.create_fork(request).unwrap();
4478 let local = execution.ensure_fork_id(local).unwrap().clone();
4479 assert!(execution.forks.get_fork(local.clone()).unwrap().is_some());
4480 assert!(seed.forks.get_fork(local.clone()).unwrap().is_none());
4481 let next = seed.clone_with_fork_scope().unwrap();
4482 assert!(next.forks.get_fork(local).unwrap().is_none());
4483 let (_other_api, other) = spawn(NodeConfig::test()).await;
4484 let unique = execution
4485 .create_fork(CreateFork {
4486 url: other.http_endpoint(),
4487 enable_caching: false,
4488 evm_opts: EvmOpts { fork_url: Some(other.http_endpoint()), ..Default::default() },
4489 })
4490 .unwrap();
4491 let unique = execution.ensure_fork_id(unique).unwrap().clone();
4492 let unique = execution.forks.get_fork(unique).unwrap().unwrap();
4493 let cache = Arc::downgrade(&unique.data());
4494 drop(unique);
4495 drop(execution);
4496 assert!(cache.upgrade().is_none(), "execution-owned remote cache must be released");
4497 drop(seed);
4498
4499 assert!(next.forks.get_fork(initial).unwrap().is_some());
4501 assert!(
4502 next.basic_ref(handle.dev_accounts().next().unwrap()).unwrap().unwrap().balance
4503 > U256::ZERO
4504 );
4505 }
4506
4507 #[tokio::test(flavor = "multi_thread")]
4508 async fn fork_clones_keep_block_overrides_local() {
4509 let (_api, handle) = spawn(NodeConfig::test()).await;
4510 let opts = EvmOpts { fork_url: Some(handle.http_endpoint()), ..Default::default() };
4511 let request = CreateFork {
4512 url: handle.http_endpoint(),
4513 enable_caching: false,
4514 evm_opts: opts.clone(),
4515 };
4516 let mut first = Backend::<EthEvmNetwork>::spawn(Some(request.clone())).unwrap();
4517 let initial_id = first.active_fork_id().unwrap();
4518 let other_id = first.create_fork(request).unwrap();
4519 let (mut first_env, mut first_tx) = first.env(&opts).await.unwrap();
4520 let mut second_env = first_env.clone();
4521 let mut second_tx = first_tx.clone();
4522 let original = first_env.block_env.clone();
4523 let mut second = first.clone();
4524 let mut first_journal = JournalInner::new();
4525 let mut second_journal = JournalInner::new();
4526
4527 second.select_fork(other_id, &mut second_env, &mut second_tx, &mut second_journal).unwrap();
4528 first_env.block_env.number = original.number + U256::ONE;
4529 first_env.block_env.timestamp = original.timestamp + U256::from(100);
4530 let changed = first_env.block_env.clone();
4531 first.select_fork(other_id, &mut first_env, &mut first_tx, &mut first_journal).unwrap();
4532 second
4533 .select_fork(initial_id, &mut second_env, &mut second_tx, &mut second_journal)
4534 .unwrap();
4535 assert_eq!(second_env.block_env.number, original.number);
4536 assert_eq!(second_env.block_env.timestamp, original.timestamp);
4537 first.select_fork(initial_id, &mut first_env, &mut first_tx, &mut first_journal).unwrap();
4538 assert_eq!(first_env.block_env.number, changed.number);
4539 assert_eq!(first_env.block_env.timestamp, changed.timestamp);
4540 }
4541
4542 #[tokio::test(flavor = "multi_thread")]
4543 #[cfg(feature = "monad")]
4544 async fn fork_factory_boundary_preserves_explicit_execution_overrides() {
4545 async fn pinned_opts(endpoint: String, networks: Option<NetworkConfigs>) -> EvmOpts {
4546 let mut opts = EvmOpts { fork_url: Some(endpoint), ..Default::default() };
4547 if let Some(networks) = networks {
4548 opts.networks = networks;
4549 }
4550 opts.infer_network_from_fork().await.unwrap();
4551 let identity = opts.fork_endpoint.clone().unwrap();
4552 let network_is_inferred = opts.fork_network_is_inferred;
4553 opts.expect_fork_endpoint(identity, network_is_inferred);
4554 opts.pin_fork_block().await.unwrap();
4555 opts
4556 }
4557
4558 fn target_fork(opts: EvmOpts, url: String) -> crate::fork::CreateFork {
4559 crate::fork::CreateFork { url, enable_caching: false, evm_opts: opts }
4560 }
4561
4562 let (ethereum_base_api, ethereum_base) = spawn(NodeConfig::test()).await;
4563 let (_ethereum_target_api, ethereum_target) = spawn(NodeConfig::test()).await;
4564 let (monad_base_api, monad_base) = spawn(NodeConfig::test_monad()).await;
4565 let (_monad_target_api, monad_target) = spawn(NodeConfig::test_monad()).await;
4566 ethereum_base_api.mine_one().await.unwrap();
4567 monad_base_api.mine_one().await.unwrap();
4568
4569 let inferred_ethereum = pinned_opts(ethereum_base.http_endpoint(), None).await;
4570 assert!(inferred_ethereum.fork_network_is_inferred);
4571 let error = Backend::<EthEvmNetwork>::spawn(Some(target_fork(
4572 inferred_ethereum,
4573 monad_target.http_endpoint(),
4574 )))
4575 .unwrap_err();
4576 assert!(
4577 error
4578 .to_string()
4579 .contains("cannot create a `monad` fork with an EVM instantiated for `ethereum`"),
4580 "{error}"
4581 );
4582
4583 let inferred_monad = pinned_opts(monad_base.http_endpoint(), None).await;
4584 assert!(inferred_monad.fork_network_is_inferred);
4585 let error = Backend::<crate::evm::MonadEvmNetwork>::spawn(Some(target_fork(
4586 inferred_monad,
4587 ethereum_target.http_endpoint(),
4588 )))
4589 .unwrap_err();
4590 assert!(
4591 error
4592 .to_string()
4593 .contains("cannot create a `ethereum` fork with an EVM instantiated for `monad`"),
4594 "{error}"
4595 );
4596
4597 let explicit_ethereum =
4598 pinned_opts(ethereum_base.http_endpoint(), Some(NetworkConfigs::with_ethereum())).await;
4599 assert!(!explicit_ethereum.fork_network_is_inferred);
4600 let _backend = Backend::<EthEvmNetwork>::spawn(Some(target_fork(
4601 explicit_ethereum,
4602 monad_target.http_endpoint(),
4603 )))
4604 .unwrap();
4605
4606 let explicit_monad =
4607 pinned_opts(monad_base.http_endpoint(), Some(NetworkConfigs::with_monad())).await;
4608 assert!(!explicit_monad.fork_network_is_inferred);
4609 let _backend = Backend::<crate::evm::MonadEvmNetwork>::spawn(Some(target_fork(
4610 explicit_monad,
4611 ethereum_target.http_endpoint(),
4612 )))
4613 .unwrap();
4614 }
4615
4616 #[tokio::test(flavor = "multi_thread")]
4617 #[cfg(feature = "optimism")]
4618 async fn optimism_fork_rejects_non_op_source() {
4619 let (_op_api, op) = spawn(NodeConfig::test().with_optimism()).await;
4620 let (_eth_api, eth) = spawn(NodeConfig::test()).await;
4621 let mut opts = EvmOpts { fork_url: Some(op.http_endpoint()), ..Default::default() };
4622 opts.infer_network_from_fork().await.unwrap();
4623 assert!(opts.fork_network_is_inferred);
4624 assert!(opts.networks.is_optimism());
4625 let original_fork = crate::fork::CreateFork {
4626 url: op.http_endpoint(),
4627 enable_caching: false,
4628 evm_opts: opts.clone(),
4629 };
4630 let mut backend = Backend::<OpEvmNetwork>::spawn(Some(original_fork)).unwrap();
4631 let original_id = backend.active_fork_id();
4632 let error = backend
4633 .create_fork(crate::fork::CreateFork {
4634 url: eth.http_endpoint(),
4635 enable_caching: false,
4636 evm_opts: opts.clone(),
4637 })
4638 .unwrap_err();
4639 assert_eq!(
4640 error.to_string(),
4641 "cannot create a `ethereum` fork with an EVM instantiated for `optimism`; run the script with --rpc-url pointing to the forked chain to select its EVM"
4642 );
4643 assert_eq!(backend.active_fork_id(), original_id);
4644 }
4645
4646 #[tokio::test(flavor = "multi_thread")]
4647 #[cfg(feature = "optimism")]
4648 async fn optimism_fork_selection_preserves_disabled_fee_charging() {
4649 let (_api, op) = spawn(NodeConfig::test().with_optimism()).await;
4650 let mut opts = EvmOpts { fork_url: Some(op.http_endpoint()), ..Default::default() };
4651 opts.infer_network_from_fork().await.unwrap();
4652 let mut backend = Backend::<OpEvmNetwork>::spawn(None).unwrap();
4653 let id = backend
4654 .create_fork(crate::fork::CreateFork {
4655 url: op.http_endpoint(),
4656 enable_caching: false,
4657 evm_opts: opts,
4658 })
4659 .unwrap();
4660 let mut evm_env = EvmEnv::default();
4661 evm_env.cfg_env.disable_fee_charge = true;
4662 backend
4663 .select_fork(id, &mut evm_env, &mut Default::default(), &mut JournalInner::new())
4664 .unwrap();
4665 assert!(evm_env.cfg_env.disable_fee_charge);
4666 }
4667
4668 #[tokio::test(flavor = "multi_thread")]
4669 #[cfg(all(feature = "optimism", feature = "monad"))]
4670 async fn optimism_fork_selection_keeps_chain_context() {
4671 let (_api, op) = spawn(NodeConfig::test().with_optimism()).await;
4672 let mut opts = EvmOpts { fork_url: Some(op.http_endpoint()), ..Default::default() };
4673 opts.infer_network_from_fork().await.unwrap();
4674 let mut backend = Backend::<OpEvmNetwork>::spawn(None).unwrap();
4675 let id = backend
4676 .create_fork(crate::fork::CreateFork {
4677 url: op.http_endpoint(),
4678 enable_caching: false,
4679 evm_opts: opts,
4680 })
4681 .unwrap();
4682 let update = backend
4683 .select_fork(
4684 id,
4685 &mut EvmEnv::default(),
4686 &mut Default::default(),
4687 &mut JournalInner::new(),
4688 )
4689 .unwrap();
4690 assert!(matches!(update, ContextUpdate::Unchanged));
4691 }
4692
4693 #[tokio::test(flavor = "multi_thread")]
4694 async fn can_read_write_cache() {
4695 let endpoint = &*foundry_test_utils::rpc::next_http_rpc_endpoint();
4696 let provider = get_http_provider(endpoint);
4697
4698 let block_num = provider.get_block_number().await.unwrap();
4699
4700 let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
4701 evm_opts.fork_url = Some(endpoint.to_string());
4702 evm_opts.fork_block_number = Some(block_num);
4703
4704 let backend = Backend::<EthEvmNetwork>::spawn(evm_opts.get_fork(
4705 &Config::default(),
4706 NamedChain::Mainnet as u64,
4707 None,
4708 ))
4709 .unwrap();
4710 let (evm_env, _) = backend.env(&evm_opts).await.unwrap();
4711 let fork = backend.fork().unwrap().unwrap();
4712 let fork_hash = fork.hash();
4713 let source_id = fork.source_id();
4714
4715 let address = address!("0x63091244180ae240c87d1f528f5f269134cb07b3");
4717
4718 let num_slots = 5;
4719 let _account = backend.basic_ref(address);
4720 for idx in 0..num_slots {
4721 let _ = backend.storage_ref(address, U256::from(idx));
4722 }
4723 drop(backend);
4724
4725 let meta = BlockchainDbMeta::new(evm_env.block_env, endpoint.to_string())
4726 .with_fork_identity(fork_hash, source_id);
4727
4728 let db = BlockchainDb::new(
4729 meta,
4730 Some(Config::foundry_block_cache_dir(NamedChain::Mainnet, block_num).unwrap()),
4731 );
4732 assert!(db.accounts().read().contains_key(&address));
4733 assert!(db.storage().read().contains_key(&address));
4734 assert_eq!(db.storage().read().get(&address).unwrap().len(), num_slots as usize);
4735 }
4736}