Skip to main content

foundry_evm_core/backend/
mod.rs

1//! Foundry's main executor backend abstraction and implementation.
2
3use crate::{
4    FoundryBlock, FoundryInspectorExt, FoundryTransaction, FromAnyRpcTransaction,
5    constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS},
6    evm::{
7        BlockEnvFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, FoundryEvmFactory,
8        FoundryEvmNetwork, HaltReasonFor, SpecFor, TxEnvFor,
9    },
10    fork::{CreateFork, ForkId, MultiFork},
11    state_snapshot::StateSnapshots,
12    utils::get_blob_base_fee_update_fraction,
13};
14use alloy_consensus::{BlockHeader, Typed2718};
15use alloy_evm::{Evm, EvmEnv, EvmFactory};
16use alloy_genesis::GenesisAccount;
17use alloy_network::{
18    AnyNetwork, AnyRpcBlock, AnyRpcTransaction, BlockResponse, Network, TransactionResponse,
19};
20use alloy_primitives::{Address, B256, TxKind, U256, keccak256, map::AddressSet, uint};
21use alloy_rpc_types::BlockNumberOrTag;
22use eyre::Context;
23use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender};
24use foundry_evm_networks::NetworkConfigs;
25pub use foundry_fork_db::{BlockchainDb, ForkBlockEnv, SharedBackend, cache::BlockchainDbMeta};
26use revm::{
27    Database, DatabaseCommit, JournalEntry,
28    bytecode::Bytecode,
29    context::{Block, BlockEnv, CfgEnv, ContextTr, JournalInner, Transaction},
30    context_interface::{journaled_state::account::JournaledAccountTr, result::ResultAndState},
31    database::{AccountState, CacheDB, DatabaseRef, EmptyDB},
32    primitives::{AddressMap, HashMap as Map, KECCAK_EMPTY, Log},
33    state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
34};
35use std::{
36    collections::{BTreeMap, HashMap, HashSet},
37    fmt::Debug,
38    time::Instant,
39};
40
41mod diagnostic;
42pub use diagnostic::RevertDiagnostic;
43
44mod error;
45pub use error::{BackendError, BackendResult, DatabaseError, DatabaseResult};
46
47mod cow;
48pub use cow::CowBackend;
49
50mod in_memory_db;
51pub use in_memory_db::{EmptyDBWrapper, FoundryEvmInMemoryDB, MemDb};
52
53mod snapshot;
54pub use snapshot::{BackendStateSnapshot, RevertStateSnapshotAction, StateSnapshot};
55
56// A `revm::Database` that is used in forking mode
57type ForkDB<N, B> = CacheDB<SharedBackend<N, B>>;
58
59/// Represents a numeric `ForkId` valid only for the existence of the `Backend`.
60///
61/// The difference between `ForkId` and `LocalForkId` is that `ForkId` tracks pairs of `endpoint +
62/// block` which can be reused by multiple tests, whereas the `LocalForkId` is unique within a test
63pub type LocalForkId = U256;
64
65/// Represents the index of a fork in the created forks vector
66/// This is used for fast lookup
67type ForkLookupIndex = usize;
68
69/// All accounts that will have persistent storage across fork swaps.
70const DEFAULT_PERSISTENT_ACCOUNTS: [Address; 3] =
71    [CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, CALLER];
72
73/// `bytes32("failed")`, as a storage slot key into [`CHEATCODE_ADDRESS`].
74///
75/// Used by all `forge-std` test contracts and newer `DSTest` test contracts as a global marker for
76/// a failed test.
77pub const GLOBAL_FAIL_SLOT: U256 =
78    uint!(0x6661696c65640000000000000000000000000000000000000000000000000000_U256);
79
80pub type JournaledState = JournalInner<JournalEntry>;
81
82/// An extension trait that allows us to easily extend the `revm::Inspector` capabilities
83#[auto_impl::auto_impl(&mut)]
84pub trait DatabaseExt<F: FoundryEvmFactory>:
85    Database<Error = DatabaseError> + DatabaseCommit + Debug
86{
87    /// Creates a new state snapshot at the current point of execution.
88    ///
89    /// A state snapshot is associated with a new unique id that's created for the snapshot.
90    /// State snapshots can be reverted: [DatabaseExt::revert_state], however, depending on the
91    /// [RevertStateSnapshotAction], it will keep the snapshot alive or delete it.
92    fn snapshot_state(
93        &mut self,
94        journaled_state: &JournaledState,
95        evm_env: &EvmEnv<F::Spec, F::BlockEnv>,
96    ) -> U256;
97
98    /// Reverts the snapshot if it exists
99    ///
100    /// Returns `true` if the snapshot was successfully reverted, `false` if no snapshot for that id
101    /// exists.
102    ///
103    /// **N.B.** While this reverts the state of the evm to the snapshot, it keeps new logs made
104    /// since the snapshots was created. This way we can show logs that were emitted between
105    /// snapshot and its revert.
106    /// This will also revert any changes in the `EvmEnv` and `TxEnv` and replace them with the
107    /// captured values from `Self::snapshot_state`.
108    ///
109    /// Depending on [RevertStateSnapshotAction] it will keep the snapshot alive or delete it.
110    fn revert_state(
111        &mut self,
112        id: U256,
113        journaled_state: &JournaledState,
114        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
115        caller: Address,
116        action: RevertStateSnapshotAction,
117    ) -> Option<JournaledState>;
118
119    /// Deletes the state snapshot with the given `id`
120    ///
121    /// Returns `true` if the snapshot was successfully deleted, `false` if no snapshot for that id
122    /// exists.
123    fn delete_state_snapshot(&mut self, id: U256) -> bool;
124
125    /// Deletes all state snapshots.
126    fn delete_state_snapshots(&mut self);
127
128    /// Creates and also selects a new fork
129    ///
130    /// This is basically `create_fork` + `select_fork`
131    fn create_select_fork(
132        &mut self,
133        fork: CreateFork,
134        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
135        tx_env: &mut F::Tx,
136        journaled_state: &mut JournaledState,
137    ) -> eyre::Result<LocalForkId> {
138        let id = self.create_fork(fork)?;
139        self.select_fork(id, evm_env, tx_env, journaled_state)?;
140        Ok(id)
141    }
142
143    /// Creates and also selects a new fork
144    ///
145    /// This is basically `create_fork` + `select_fork`
146    fn create_select_fork_at_transaction(
147        &mut self,
148        fork: CreateFork,
149        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
150        tx_env: &mut F::Tx,
151        journaled_state: &mut JournaledState,
152        transaction: B256,
153    ) -> eyre::Result<LocalForkId> {
154        let id = self.create_fork_at_transaction(fork, transaction)?;
155        self.select_fork(id, evm_env, tx_env, journaled_state)?;
156        Ok(id)
157    }
158
159    /// Creates a new fork but does _not_ select it
160    fn create_fork(&mut self, fork: CreateFork) -> eyre::Result<LocalForkId>;
161
162    /// Creates a new fork but does _not_ select it
163    fn create_fork_at_transaction(
164        &mut self,
165        fork: CreateFork,
166        transaction: B256,
167    ) -> eyre::Result<LocalForkId>;
168
169    /// Selects the fork's state
170    ///
171    /// This will also modify the current `EvmEnv` and `TxEnv`.
172    ///
173    /// **Note**: this does not change the local state, but swaps the remote state
174    ///
175    /// # Errors
176    ///
177    /// Returns an error if no fork with the given `id` exists
178    fn select_fork(
179        &mut self,
180        id: LocalForkId,
181        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
182        tx_env: &mut F::Tx,
183        journaled_state: &mut JournaledState,
184    ) -> eyre::Result<()>;
185
186    /// Updates the fork to given block number.
187    ///
188    /// This will essentially create a new fork at the given block height.
189    ///
190    /// # Errors
191    ///
192    /// Returns an error if not matching fork was found.
193    fn roll_fork(
194        &mut self,
195        id: Option<LocalForkId>,
196        block_number: u64,
197        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
198        journaled_state: &mut JournaledState,
199    ) -> eyre::Result<()>;
200
201    /// Updates the fork to given transaction hash
202    ///
203    /// This will essentially create a new fork at the block this transaction was mined and replays
204    /// all transactions up until the given transaction.
205    ///
206    /// # Errors
207    ///
208    /// Returns an error if not matching fork was found.
209    fn roll_fork_to_transaction(
210        &mut self,
211        id: Option<LocalForkId>,
212        transaction: B256,
213        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
214        journaled_state: &mut JournaledState,
215    ) -> eyre::Result<()>;
216
217    /// Fetches the given transaction for the fork and executes it, committing the state in the DB
218    fn transact(
219        &mut self,
220        id: Option<LocalForkId>,
221        transaction: B256,
222        evm_env: EvmEnv<F::Spec, F::BlockEnv>,
223        journaled_state: &mut JournaledState,
224        inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
225    ) -> eyre::Result<()>;
226
227    /// Executes a given TransactionRequest, commits the new state to the DB
228    fn transact_from_tx(
229        &mut self,
230        tx_env: F::Tx,
231        evm_env: EvmEnv<F::Spec, F::BlockEnv>,
232        journaled_state: &mut JournaledState,
233        inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
234    ) -> eyre::Result<()>;
235
236    /// Returns the `ForkId` that's currently used in the database, if fork mode is on
237    fn active_fork_id(&self) -> Option<LocalForkId>;
238
239    /// Returns the Fork url that's currently used in the database, if fork mode is on
240    fn active_fork_url(&self) -> Option<String>;
241
242    /// Returns the active fork's current fork block number, if any.
243    fn active_fork_block_number(&self) -> Option<u64> {
244        None
245    }
246
247    /// Whether the database is currently in forked mode.
248    fn is_forked_mode(&self) -> bool {
249        self.active_fork_id().is_some()
250    }
251
252    /// Ensures that an appropriate fork exists
253    ///
254    /// If `id` contains a requested `Fork` this will ensure it exists.
255    /// Otherwise, this returns the currently active fork.
256    ///
257    /// # Errors
258    ///
259    /// Returns an error if the given `id` does not match any forks
260    ///
261    /// Returns an error if no fork exists
262    fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId>;
263
264    /// Ensures that a corresponding `ForkId` exists for the given local `id`
265    fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId>;
266
267    /// Handling multiple accounts/new contracts in a multifork environment can be challenging since
268    /// every fork has its own standalone storage section. So this can be a common error to run
269    /// into:
270    ///
271    /// ```solidity
272    /// function testCanDeploy() public {
273    ///    vm.selectFork(mainnetFork);
274    ///    // contract created while on `mainnetFork`
275    ///    DummyContract dummy = new DummyContract();
276    ///    // this will succeed
277    ///    dummy.hello();
278    ///
279    ///    vm.selectFork(optimismFork);
280    ///
281    ///    vm.expectRevert();
282    ///    // this will revert since `dummy` contract only exists on `mainnetFork`
283    ///    dummy.hello();
284    /// }
285    /// ```
286    ///
287    /// If this happens (`dummy.hello()`), or more general, a call on an address that's not a
288    /// contract, revm will revert without useful context. This call will check in this context if
289    /// `address(dummy)` belongs to an existing contract and if not will check all other forks if
290    /// the contract is deployed there.
291    ///
292    /// Returns a more useful error message if that's the case
293    fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic>;
294
295    /// Loads the account allocs from the given `allocs` map into the passed [JournaledState].
296    ///
297    /// Returns [Ok] if all accounts were successfully inserted into the journal, [Err] otherwise.
298    fn load_allocs(
299        &mut self,
300        allocs: &BTreeMap<Address, GenesisAccount>,
301        journaled_state: &mut JournaledState,
302    ) -> Result<(), BackendError>;
303
304    /// Copies bytecode, storage, nonce and balance from the given genesis account to the target
305    /// address.
306    ///
307    /// Returns [Ok] if data was successfully inserted into the journal, [Err] otherwise.
308    fn clone_account(
309        &mut self,
310        source: &GenesisAccount,
311        target: &Address,
312        journaled_state: &mut JournaledState,
313    ) -> Result<(), BackendError>;
314
315    /// Returns true if the given account is currently marked as persistent.
316    fn is_persistent(&self, acc: &Address) -> bool;
317
318    /// Drops cached account info for `address` on the active fork so the next read re-fetches it
319    /// from the node (used after out-of-band mutations like `anvil_setBalance` via `vm.rpc`).
320    fn invalidate_fork_cache_account(&mut self, address: Address);
321
322    /// Like [`invalidate_fork_cache_account`](Self::invalidate_fork_cache_account), but for a
323    /// single storage slot.
324    fn invalidate_fork_cache_storage(&mut self, address: Address, slot: U256);
325
326    /// Revokes persistent status from the given account.
327    fn remove_persistent_account(&mut self, account: &Address) -> bool;
328
329    /// Marks the given account as persistent.
330    fn add_persistent_account(&mut self, account: Address) -> bool;
331
332    /// Removes persistent status from all given accounts.
333    #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
334    fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
335    where
336        Self: Sized,
337    {
338        for acc in accounts {
339            self.remove_persistent_account(&acc);
340        }
341    }
342
343    /// Extends the persistent accounts with the accounts the iterator yields.
344    #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
345    fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
346    where
347        Self: Sized,
348    {
349        for acc in accounts {
350            self.add_persistent_account(acc);
351        }
352    }
353
354    /// Grants cheatcode access for the given `account`
355    ///
356    /// Returns true if the `account` already has access
357    fn allow_cheatcode_access(&mut self, account: Address) -> bool;
358
359    /// Revokes cheatcode access for the given account
360    ///
361    /// Returns true if the `account` was previously allowed cheatcode access
362    fn revoke_cheatcode_access(&mut self, account: &Address) -> bool;
363
364    /// Returns `true` if the given account is allowed to execute cheatcodes
365    fn has_cheatcode_access(&self, account: &Address) -> bool;
366
367    /// Ensures that `account` is allowed to execute cheatcodes
368    ///
369    /// Returns an error if [`Self::has_cheatcode_access`] returns `false`
370    fn ensure_cheatcode_access(&self, account: &Address) -> Result<(), BackendError> {
371        if !self.has_cheatcode_access(account) {
372            return Err(BackendError::NoCheats(*account));
373        }
374        Ok(())
375    }
376
377    /// Same as [`Self::ensure_cheatcode_access()`] but only enforces it if the backend is currently
378    /// in forking mode
379    fn ensure_cheatcode_access_forking_mode(&self, account: &Address) -> Result<(), BackendError> {
380        if self.is_forked_mode() {
381            return self.ensure_cheatcode_access(account);
382        }
383        Ok(())
384    }
385
386    /// Set the blockhash for a given block number.
387    ///
388    /// # Arguments
389    ///
390    /// * `number` - The block number to set the blockhash for
391    /// * `hash` - The blockhash to set
392    ///
393    /// # Note
394    ///
395    /// This function mimics the EVM limits of the `blockhash` operation:
396    /// - It sets the blockhash for blocks where `block.number - 256 <= number < block.number`
397    /// - Setting a blockhash for the current block (number == block.number) has no effect
398    /// - Setting a blockhash for future blocks (number > block.number) has no effect
399    /// - Setting a blockhash for blocks older than `block.number - 256` has no effect
400    fn set_blockhash(&mut self, block_number: U256, block_hash: B256);
401}
402
403/// Provides the underlying `revm::Database` implementation.
404///
405/// A `Backend` can be initialised in two forms:
406///
407/// # 1. Empty in-memory Database
408/// This is the default variant: an empty `revm::Database`
409///
410/// # 2. Forked Database
411/// A `revm::Database` that forks off a remote client
412///
413///
414/// In addition to that we support forking manually on the fly.
415/// Additional forks can be created. Each unique fork is identified by its unique `ForkId`. We treat
416/// forks as unique if they have the same `(endpoint, block number)` pair.
417///
418/// When it comes to testing, it's intended that each contract will use its own `Backend`
419/// (`Backend::clone`). This way each contract uses its own encapsulated evm state. For in-memory
420/// testing, the database is just an owned `revm::InMemoryDB`.
421///
422/// Each `Fork`, identified by a unique id, uses completely separate storage, write operations are
423/// performed only in the fork's own database, `ForkDB`.
424///
425/// A `ForkDB` consists of 2 halves:
426///   - everything fetched from the remote is readonly
427///   - all local changes (instructed by the contract) are written to the backend's `db` and don't
428///     alter the state of the remote client.
429///
430/// # Fork swapping
431///
432/// Multiple "forks" can be created `Backend::create_fork()`, however only 1 can be used by the
433/// `db`. However, their state can be hot-swapped by swapping the read half of `db` from one fork to
434/// another.
435/// When swapping forks (`Backend::select_fork()`) we also update the current `EvmEnv` of the `EVM`
436/// accordingly, so that all `block.*` config values match
437///
438/// When another for is selected [`DatabaseExt::select_fork()`] the entire storage, including
439/// `JournaledState` is swapped, but the storage of the caller's and the test contract account is
440/// _always_ cloned. This way a fork has entirely separate storage but data can still be shared
441/// across fork boundaries via stack and contract variables.
442///
443/// # Snapshotting
444///
445/// A snapshot of the current overall state can be taken at any point in time. A snapshot is
446/// identified by a unique id that's returned when a snapshot is created. A snapshot can only be
447/// reverted _once_. After a successful revert, the same snapshot id cannot be used again. Reverting
448/// a snapshot replaces the current active state with the snapshot state, the snapshot is deleted
449/// afterwards, as well as any snapshots taken after the reverted snapshot, (e.g.: reverting to id
450/// 0x1 will delete snapshots with ids 0x1, 0x2, etc.)
451///
452/// **Note:** State snapshots work across fork-swaps, e.g. if fork `A` is currently active, then a
453/// snapshot is created before fork `B` is selected, then fork `A` will be the active fork again
454/// after reverting the snapshot.
455#[must_use]
456pub struct Backend<FEN: FoundryEvmNetwork = EthEvmNetwork> {
457    /// The access point for managing forks
458    forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
459    // The default in memory db
460    mem_db: FoundryEvmInMemoryDB,
461    /// The journaled_state to use to initialize new forks with
462    ///
463    /// The way [`JournaledState`] works is, that it holds the "hot" accounts loaded from the
464    /// underlying `Database` that feeds the Account and State data to the journaled_state so it
465    /// can apply changes to the state while the EVM executes.
466    ///
467    /// In a way the `JournaledState` is something like a cache that
468    /// 1. check if account is already loaded (hot)
469    /// 2. if not load from the `Database` (this will then retrieve the account via RPC in forking
470    ///    mode)
471    ///
472    /// To properly initialize we store the `JournaledState` before the first fork is selected
473    /// ([`DatabaseExt::select_fork`]).
474    ///
475    /// This will be an empty `JournaledState`, which will be populated with persistent accounts,
476    /// See [`Self::update_fork_db()`].
477    fork_init_journaled_state: JournaledState,
478    /// The currently active fork database
479    ///
480    /// If this is set, then the Backend is currently in forking mode
481    active_fork_ids: Option<(LocalForkId, ForkLookupIndex)>,
482    /// holds additional Backend data
483    inner: BackendInner<FEN>,
484}
485
486impl<FEN: FoundryEvmNetwork> Clone for Backend<FEN> {
487    fn clone(&self) -> Self {
488        Self {
489            forks: self.forks.clone(),
490            mem_db: self.mem_db.clone(),
491            fork_init_journaled_state: self.fork_init_journaled_state.clone(),
492            active_fork_ids: self.active_fork_ids,
493            inner: self.inner.clone(),
494        }
495    }
496}
497
498impl<FEN: FoundryEvmNetwork> Debug for Backend<FEN> {
499    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
500        f.debug_struct("Backend")
501            .field("forks", &self.forks)
502            .field("mem_db", &self.mem_db)
503            .field("fork_init_journaled_state", &self.fork_init_journaled_state)
504            .field("active_fork_ids", &self.active_fork_ids)
505            .field("inner", &self.inner)
506            .finish()
507    }
508}
509
510impl<FEN: FoundryEvmNetwork> Backend<FEN> {
511    /// Creates a new Backend with a spawned multi fork thread.
512    ///
513    /// If `fork` is `Some` this will use a `fork` database, otherwise with an in-memory
514    /// database.
515    pub fn spawn(fork: Option<CreateFork>) -> eyre::Result<Self> {
516        Self::new(MultiFork::<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>::spawn(), fork)
517    }
518
519    /// Creates a new instance of `Backend`
520    ///
521    /// If `fork` is `Some` this will use a `fork` database, otherwise with an in-memory
522    /// database.
523    ///
524    /// Prefer using [`spawn`](Self::spawn) instead.
525    pub fn new(
526        forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
527        fork: Option<CreateFork>,
528    ) -> eyre::Result<Self> {
529        trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend");
530        // Note: this will take of registering the `fork`
531        let inner = BackendInner {
532            persistent_accounts: HashSet::from(DEFAULT_PERSISTENT_ACCOUNTS),
533            ..Default::default()
534        };
535
536        let mut backend = Self {
537            forks,
538            mem_db: CacheDB::new(Default::default()),
539            fork_init_journaled_state: inner.new_journaled_state(),
540            active_fork_ids: None,
541            inner,
542        };
543
544        if let Some(fork) = fork {
545            let (fork_id, fork, _) = backend.forks.create_fork(fork)?;
546            let fork_db = ForkDB::new(fork);
547            let fork_ids = backend.inner.insert_new_fork(
548                fork_id.clone(),
549                fork_db,
550                backend.inner.new_journaled_state(),
551            );
552            backend.inner.launched_with_fork = Some((fork_id, fork_ids.0, fork_ids.1));
553            backend.active_fork_ids = Some(fork_ids);
554        }
555
556        trace!(target: "backend", forking_mode=? backend.active_fork_ids.is_some(), "created executor backend");
557
558        Ok(backend)
559    }
560
561    /// Creates a new instance of `Backend` with fork added to the fork database and sets the fork
562    /// as active
563    pub(crate) fn new_with_fork(
564        id: &ForkId,
565        fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
566        journaled_state: JournaledState,
567    ) -> eyre::Result<Self> {
568        let mut backend = Self::spawn(None)?;
569        let fork_ids = backend.inner.insert_new_fork(id.clone(), fork.db, journaled_state);
570        backend.inner.launched_with_fork = Some((id.clone(), fork_ids.0, fork_ids.1));
571        backend.active_fork_ids = Some(fork_ids);
572        Ok(backend)
573    }
574
575    /// Creates a new instance with a `BackendDatabase::InMemory` cache layer for the `CacheDB`
576    pub fn clone_empty(&self) -> Self {
577        Self {
578            forks: self.forks.clone(),
579            mem_db: CacheDB::new(Default::default()),
580            fork_init_journaled_state: self.inner.new_journaled_state(),
581            active_fork_ids: None,
582            inner: Default::default(),
583        }
584    }
585
586    pub fn insert_account_info(&mut self, address: Address, account: AccountInfo) {
587        if let Some(db) = self.active_fork_db_mut() {
588            db.insert_account_info(address, account)
589        } else {
590            self.mem_db.insert_account_info(address, account)
591        }
592    }
593
594    /// Inserts a value on an account's storage without overriding account info
595    pub fn insert_account_storage(
596        &mut self,
597        address: Address,
598        slot: U256,
599        value: U256,
600    ) -> Result<(), DatabaseError> {
601        if let Some(db) = self.active_fork_db_mut() {
602            db.insert_account_storage(address, slot, value)
603        } else {
604            self.mem_db.insert_account_storage(address, slot, value)
605        }
606    }
607
608    /// Completely replace an account's storage without overriding account info.
609    ///
610    /// When forking, this causes the backend to assume a `0` value for all
611    /// unset storage slots instead of trying to fetch it.
612    pub fn replace_account_storage(
613        &mut self,
614        address: Address,
615        storage: Map<U256, U256>,
616    ) -> Result<(), DatabaseError> {
617        if let Some(db) = self.active_fork_db_mut() {
618            db.replace_account_storage(address, storage.into_iter().collect())
619        } else {
620            self.mem_db.replace_account_storage(address, storage.into_iter().collect())
621        }
622    }
623
624    /// Returns all snapshots created in this backend
625    #[allow(clippy::type_complexity)]
626    pub const fn state_snapshots(
627        &self,
628    ) -> &StateSnapshots<
629        BackendStateSnapshot<
630            BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
631            SpecFor<FEN>,
632            BlockEnvFor<FEN>,
633        >,
634    > {
635        &self.inner.state_snapshots
636    }
637
638    /// Sets the address of the `DSTest` contract that is being executed
639    ///
640    /// This will also mark the caller as persistent and remove the persistent status from the
641    /// previous test contract address
642    ///
643    /// This will also grant cheatcode access to the test account
644    pub fn set_test_contract(&mut self, acc: Address) -> &mut Self {
645        trace!(?acc, "setting test account");
646        self.inner.persistent_accounts.insert(acc);
647        self.inner.cheatcode_access_accounts.insert(acc);
648        self
649    }
650
651    /// Sets the caller address
652    pub fn set_caller(&mut self, acc: Address) -> &mut Self {
653        trace!(?acc, "setting caller account");
654        self.inner.caller = Some(acc);
655        self.inner.cheatcode_access_accounts.insert(acc);
656        self
657    }
658
659    /// Sets the current spec id
660    pub fn set_spec_id(&mut self, spec_id: impl Into<SpecFor<FEN>>) -> &mut Self {
661        self.inner.spec_id = spec_id.into();
662        self
663    }
664
665    /// Returns the set caller address
666    pub const fn caller_address(&self) -> Option<Address> {
667        self.inner.caller
668    }
669
670    /// Failures occurred in state snapshots are tracked when the state snapshot is reverted.
671    ///
672    /// If an error occurs in a restored state snapshot, the test is considered failed.
673    ///
674    /// This returns whether there was a reverted state snapshot that recorded an error.
675    pub const fn has_state_snapshot_failure(&self) -> bool {
676        self.inner.has_state_snapshot_failure
677    }
678
679    /// Sets the state snapshot failure flag.
680    pub const fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) {
681        self.inner.has_state_snapshot_failure = has_state_snapshot_failure
682    }
683
684    /// When creating or switching forks, we update the AccountInfo of the contract
685    pub(crate) fn update_fork_db(
686        &self,
687        active_journaled_state: &mut JournaledState,
688        target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
689    ) {
690        self.update_fork_db_contracts(
691            self.inner.persistent_accounts.iter().copied(),
692            active_journaled_state,
693            target_fork,
694        )
695    }
696
697    /// Merges the state of all `accounts` from the currently active db into the given `fork`
698    pub(crate) fn update_fork_db_contracts(
699        &self,
700        accounts: impl IntoIterator<Item = Address>,
701        active_journaled_state: &mut JournaledState,
702        target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
703    ) {
704        if let Some(db) = self.active_fork_db() {
705            merge_account_data(accounts, db, active_journaled_state, target_fork)
706        } else {
707            merge_account_data(accounts, &self.mem_db, active_journaled_state, target_fork)
708        }
709    }
710
711    /// Returns the memory db used if not in forking mode
712    pub const fn mem_db(&self) -> &FoundryEvmInMemoryDB {
713        &self.mem_db
714    }
715
716    /// Returns true if the `id` is currently active
717    pub fn is_active_fork(&self, id: LocalForkId) -> bool {
718        self.active_fork_ids.map(|(i, _)| i == id).unwrap_or_default()
719    }
720
721    /// Returns `true` if the `Backend` is currently in forking mode
722    pub fn is_in_forking_mode(&self) -> bool {
723        self.active_fork().is_some()
724    }
725
726    /// Returns the currently active `Fork`, if any
727    pub fn active_fork(&self) -> Option<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
728        self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx))
729    }
730
731    /// Returns the currently active `Fork`, if any
732    pub fn active_fork_mut(&mut self) -> Option<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
733        self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx))
734    }
735
736    /// Returns the currently active `ForkDB`, if any
737    pub fn active_fork_db(&self) -> Option<&ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
738        self.active_fork().map(|f| &f.db)
739    }
740
741    /// Returns the currently active `ForkDB`, if any
742    pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
743        self.active_fork_mut().map(|f| &mut f.db)
744    }
745
746    /// Returns the current database implementation as a `&dyn` value.
747    pub fn db(&self) -> &dyn Database<Error = DatabaseError> {
748        match self.active_fork_db() {
749            Some(fork_db) => fork_db,
750            None => &self.mem_db,
751        }
752    }
753
754    /// Returns the current database implementation as a `&mut dyn` value.
755    pub fn db_mut(&mut self) -> &mut dyn Database<Error = DatabaseError> {
756        match self.active_fork_ids.map(|(_, idx)| &mut self.inner.get_fork_mut(idx).db) {
757            Some(fork_db) => fork_db,
758            None => &mut self.mem_db,
759        }
760    }
761
762    /// Creates a snapshot of the currently active database
763    pub(crate) fn create_db_snapshot(
764        &self,
765    ) -> BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>> {
766        if let Some((id, idx)) = self.active_fork_ids {
767            let fork = self.inner.get_fork(idx).clone();
768            let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed");
769            BackendDatabaseSnapshot::Forked(id, fork_id, idx, Box::new(fork))
770        } else {
771            BackendDatabaseSnapshot::InMemory(self.mem_db.clone())
772        }
773    }
774
775    /// Since each `Fork` tracks logs separately, we need to merge them to get _all_ of them
776    pub fn merged_logs(&self, mut logs: Vec<Log>) -> Vec<Log> {
777        if let Some((_, active)) = self.active_fork_ids {
778            let mut all_logs = Vec::with_capacity(logs.len());
779
780            self.inner
781                .forks
782                .iter()
783                .enumerate()
784                .filter_map(|(idx, f)| f.as_ref().map(|f| (idx, f)))
785                .for_each(|(idx, f)| {
786                    if idx == active {
787                        all_logs.append(&mut logs);
788                    } else {
789                        all_logs.extend(f.journaled_state.logs.clone())
790                    }
791                });
792            return all_logs;
793        }
794
795        logs
796    }
797
798    /// Initializes settings we need to keep track of.
799    ///
800    /// We need to track these mainly to prevent issues when switching between different evms
801    pub(crate) fn initialize(
802        &mut self,
803        spec_id: impl Into<SpecFor<FEN>>,
804        caller: Address,
805        tx_kind: TxKind,
806    ) {
807        self.set_caller(caller);
808        self.set_spec_id(spec_id);
809
810        let test_contract = match tx_kind {
811            TxKind::Call(to) => to,
812            TxKind::Create => {
813                let nonce =
814                    self.basic_ref(caller).map(|b| b.unwrap_or_default().nonce).unwrap_or_default();
815                caller.create(nonce)
816            }
817        };
818        self.set_test_contract(test_contract);
819    }
820
821    /// Executes the configured test call of the `env` without committing state changes.
822    ///
823    /// Note: in case there are any cheatcodes executed that modify the environment, this will
824    /// update the given `env` with the new values.
825    #[instrument(name = "inspect", level = "debug", skip_all)]
826    pub fn inspect<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
827        &mut self,
828        evm_env: &mut EvmEnvFor<FEN>,
829        tx_env: &mut TxEnvFor<FEN>,
830        inspector: I,
831    ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
832        self.initialize(evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind());
833        let mut evm = FEN::EvmFactory::default().create_foundry_evm_with_inspector(
834            self,
835            evm_env.to_owned(),
836            inspector,
837        );
838        let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?;
839
840        *tx_env = evm.tx().clone();
841        *evm_env = evm.finish().1;
842
843        Ok(res)
844    }
845
846    /// Returns true if the address is a precompile
847    pub fn is_existing_precompile(&self, addr: &Address) -> bool {
848        self.inner.precompile_addresses().contains(addr)
849    }
850
851    /// Sets the initial journaled state to use when initializing forks
852    #[inline]
853    fn set_init_journaled_state(&mut self, journaled_state: JournaledState) {
854        trace!("recording fork init journaled_state");
855        self.fork_init_journaled_state = journaled_state;
856    }
857
858    /// Cleans up already loaded accounts that would be initialized without the correct data from
859    /// the fork.
860    ///
861    /// It can happen that an account is loaded before the first fork is selected, like
862    /// `getNonce(addr)`, which will load an empty account by default.
863    ///
864    /// This account data then would not match the account data of a fork if it exists.
865    /// So when the first fork is initialized we replace these accounts with the actual account as
866    /// it exists on the fork.
867    fn prepare_init_journal_state(&mut self) -> Result<(), BackendError> {
868        let loaded_accounts = self
869            .fork_init_journaled_state
870            .state
871            .iter()
872            .filter(|(addr, _)| {
873                !self.is_existing_precompile(addr)
874                    && !self.inner.persistent_accounts.contains(*addr)
875            })
876            .map(|(addr, _)| addr)
877            .copied()
878            .collect::<Vec<_>>();
879
880        for fork in self.inner.forks_iter_mut() {
881            let mut journaled_state = self.fork_init_journaled_state.clone();
882            for loaded_account in loaded_accounts.iter().copied() {
883                trace!(?loaded_account, "replacing account on init");
884                let init_account =
885                    journaled_state.state.get_mut(&loaded_account).expect("exists; qed");
886
887                // here's an edge case where we need to check if this account has been created, in
888                // which case we don't need to replace it with the account from the fork because the
889                // created account takes precedence: for example contract creation in setups
890                if init_account.is_created() {
891                    trace!(?loaded_account, "skipping created account");
892                    continue;
893                }
894
895                // otherwise we need to replace the account's info with the one from the fork's
896                // database
897                let fork_account = Database::basic(&mut fork.db, loaded_account)?
898                    .ok_or(BackendError::MissingAccount(loaded_account))?;
899                init_account.info = fork_account;
900            }
901            fork.journaled_state = journaled_state;
902        }
903        Ok(())
904    }
905
906    /// Returns the block numbers required for replaying a transaction
907    fn get_block_number_and_block_for_transaction(
908        &self,
909        id: LocalForkId,
910        transaction: B256,
911    ) -> eyre::Result<(u64, AnyRpcBlock)> {
912        let fork = self.inner.get_fork_by_id(id)?;
913        let tx = fork.backend().get_transaction(transaction)?;
914
915        // get the block number we need to fork
916        if let Some(tx_block) = tx.block_number() {
917            let block = fork.backend().get_full_block(tx_block)?;
918
919            // we need to subtract 1 here because we want the state before the transaction
920            // was mined
921            let fork_block = tx_block - 1;
922            Ok((fork_block, block))
923        } else {
924            let block = fork.backend().get_full_block(BlockNumberOrTag::Latest)?;
925
926            let number = block.header().number();
927
928            Ok((number, block))
929        }
930    }
931
932    /// Replays all the transactions at the forks current block that were mined before the `tx`
933    ///
934    /// Returns the _unmined_ transaction that corresponds to the given `tx_hash`
935    pub fn replay_until(
936        &mut self,
937        id: LocalForkId,
938        evm_env: EvmEnvFor<FEN>,
939        tx_hash: B256,
940        journaled_state: &mut JournaledState,
941    ) -> eyre::Result<Option<AnyRpcTransaction>> {
942        trace!(?id, ?tx_hash, "replay until transaction");
943
944        let persistent_accounts = self.inner.persistent_accounts.clone();
945
946        let fork = self.inner.get_fork_by_id_mut(id)?;
947        let full_block =
948            fork.backend().get_full_block(evm_env.block_env.number().saturating_to::<u64>())?;
949
950        // Collect non-system transactions up to and including the target.
951        let txs = full_block
952            .transactions()
953            .txns()
954            .filter(|tx| !is_known_system_sender(tx.from()) && tx.ty() != SYSTEM_TRANSACTION_TYPE);
955
956        let mut txs_to_replay = Vec::new();
957        let mut target_tx = None;
958        for tx in txs {
959            if tx.tx_hash() == tx_hash {
960                target_tx = Some(tx.clone());
961                break;
962            }
963            txs_to_replay.push(tx.clone());
964        }
965
966        // Replay all preceding transactions using a single EVM + cloned ForkDB.
967        if !txs_to_replay.is_empty() {
968            let now = Instant::now();
969
970            // Clone the fork's CacheDB once. The underlying SharedBackend is Arc-backed,
971            // so only the local cache layer is actually duplicated.
972            let chain_id = evm_env.cfg_env.chain_id;
973            let timestamp = evm_env.block_env.timestamp().saturating_to();
974            let replay_db = fork.db.clone();
975            let mut evm = FEN::EvmFactory::default().create_evm(replay_db, evm_env);
976            NetworkConfigs::default().inject_chain_precompiles(
977                evm.precompiles_mut(),
978                chain_id,
979                timestamp,
980            );
981
982            for tx in &txs_to_replay {
983                let tx_env = TxEnvFor::<FEN>::from_any_rpc_transaction(tx)?;
984                trace!(tx=?tx.tx_hash(), "committing transaction");
985                evm.transact_commit(tx_env).wrap_err("backend: failed committing transaction")?;
986            }
987
988            // Extract the DB back and replace the fork's database with the replayed state.
989            fork.db = evm.into_db();
990
991            // Refresh journaled states from the updated database, preserving persistent
992            // accounts (cheatcode address, CREATE2 deployer, test contract, etc.).
993            fork.refresh_journaled_states(journaled_state, &persistent_accounts)?;
994
995            trace!(elapsed=?now.elapsed(), count=txs_to_replay.len(), "replayed transactions");
996        }
997
998        Ok(target_tx)
999    }
1000}
1001
1002impl<FEN: FoundryEvmNetwork> DatabaseExt<FEN::EvmFactory> for Backend<FEN> {
1003    fn snapshot_state(
1004        &mut self,
1005        journaled_state: &JournaledState,
1006        evm_env: &EvmEnvFor<FEN>,
1007    ) -> U256 {
1008        trace!("create snapshot");
1009        let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new(
1010            self.create_db_snapshot(),
1011            journaled_state.clone(),
1012            evm_env.clone(),
1013        ));
1014        trace!(target: "backend", "Created new snapshot {}", id);
1015        id
1016    }
1017
1018    fn revert_state(
1019        &mut self,
1020        id: U256,
1021        current_state: &JournaledState,
1022        evm_env: &mut EvmEnvFor<FEN>,
1023        caller: Address,
1024        action: RevertStateSnapshotAction,
1025    ) -> Option<JournaledState> {
1026        trace!(?id, "revert snapshot");
1027        if let Some(mut snapshot) = self.inner.state_snapshots.remove_at(id) {
1028            // Re-insert snapshot to persist it
1029            if action.is_keep() {
1030                self.inner.state_snapshots.insert_at(snapshot.clone(), id);
1031            }
1032
1033            // https://github.com/foundry-rs/foundry/issues/3055
1034            // Check if an error occurred either during or before the snapshot.
1035            // DSTest contracts don't have snapshot functionality, so this slot is enough to check
1036            // for failure here.
1037            if let Some(account) = current_state.state.get(&CHEATCODE_ADDRESS)
1038                && let Some(slot) = account.storage.get(&GLOBAL_FAIL_SLOT)
1039                && !slot.present_value.is_zero()
1040            {
1041                self.set_state_snapshot_failure(true);
1042            }
1043
1044            // merge additional logs
1045            snapshot.merge(current_state);
1046            let BackendStateSnapshot { db, mut journaled_state, snap_evm_env } = snapshot;
1047            match db {
1048                BackendDatabaseSnapshot::InMemory(mem_db) => {
1049                    self.mem_db = mem_db;
1050                }
1051                BackendDatabaseSnapshot::Forked(id, fork_id, idx, mut fork) => {
1052                    // there might be the case where the snapshot was created during `setUp` with
1053                    // another caller, so we need to ensure the caller account is present in the
1054                    // journaled state and database
1055                    journaled_state.state.entry(caller).or_insert_with(|| {
1056                        let caller_account = current_state
1057                            .state
1058                            .get(&caller)
1059                            .map(|acc| acc.info.clone())
1060                            .unwrap_or_default();
1061
1062                        if !fork.db.cache.accounts.contains_key(&caller) {
1063                            // update the caller account which is required by the evm
1064                            fork.db.insert_account_info(caller, caller_account.clone());
1065                        }
1066                        caller_account.into()
1067                    });
1068                    self.inner.revert_state_snapshot(id, fork_id, idx, *fork);
1069                    self.active_fork_ids = Some((id, idx))
1070                }
1071            }
1072
1073            *evm_env = snap_evm_env;
1074            trace!(target: "backend", "Reverted snapshot {}", id);
1075
1076            Some(journaled_state)
1077        } else {
1078            warn!(target: "backend", "No snapshot to revert for {}", id);
1079            None
1080        }
1081    }
1082
1083    fn delete_state_snapshot(&mut self, id: U256) -> bool {
1084        self.inner.state_snapshots.remove_at(id).is_some()
1085    }
1086
1087    fn delete_state_snapshots(&mut self) {
1088        self.inner.state_snapshots.clear()
1089    }
1090
1091    fn create_fork(&mut self, create_fork: CreateFork) -> eyre::Result<LocalForkId> {
1092        trace!("create fork");
1093        let (fork_id, fork, _) = self.forks.create_fork(create_fork)?;
1094
1095        let fork_db = ForkDB::new(fork);
1096        let (id, _) =
1097            self.inner.insert_new_fork(fork_id, fork_db, self.fork_init_journaled_state.clone());
1098        Ok(id)
1099    }
1100
1101    fn create_fork_at_transaction(
1102        &mut self,
1103        fork: CreateFork,
1104        transaction: B256,
1105    ) -> eyre::Result<LocalForkId> {
1106        trace!(?transaction, "create fork at transaction");
1107        let id = self.create_fork(fork)?;
1108        let fork_id = self.ensure_fork_id(id).cloned()?;
1109        let mut evm_env = self
1110            .forks
1111            .get_evm_env(fork_id)?
1112            .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
1113
1114        // we still need to roll to the transaction, but we only need an empty dummy state since we
1115        // don't need to update the active journaled state yet
1116        self.roll_fork_to_transaction(
1117            Some(id),
1118            transaction,
1119            &mut evm_env,
1120            &mut self.inner.new_journaled_state(),
1121        )?;
1122
1123        Ok(id)
1124    }
1125
1126    /// Select an existing fork by id.
1127    /// When switching forks we copy the shared state
1128    fn select_fork(
1129        &mut self,
1130        id: LocalForkId,
1131        evm_env: &mut EvmEnvFor<FEN>,
1132        tx_env: &mut TxEnvFor<FEN>,
1133        active_journaled_state: &mut JournaledState,
1134    ) -> eyre::Result<()> {
1135        trace!(?id, "select fork");
1136        if self.is_active_fork(id) {
1137            // nothing to do
1138            return Ok(());
1139        }
1140
1141        // Update block number and timestamp of active fork (if any) with current env values,
1142        // in order to preserve values changed by using `roll` and `warp` cheatcodes.
1143        if let Some(active_fork_id) = self.active_fork_id() {
1144            self.forks.update_block(
1145                self.ensure_fork_id(active_fork_id).cloned()?,
1146                evm_env.block_env.number(),
1147                evm_env.block_env.timestamp(),
1148            )?;
1149        }
1150
1151        let fork_id = self.ensure_fork_id(id).cloned()?;
1152        let idx = self.inner.ensure_fork_index(&fork_id)?;
1153        let fork_evm_env = self
1154            .forks
1155            .get_evm_env(fork_id)?
1156            .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
1157
1158        // If we're currently in forking mode we need to update the journaled_state to this point,
1159        // this ensures the changes performed while the fork was active are recorded
1160        if let Some(active) = self.active_fork_mut() {
1161            active.journaled_state = active_journaled_state.clone();
1162
1163            let caller = tx_env.caller();
1164            let caller_account = active.journaled_state.state.get(&caller).cloned();
1165            let target_fork = self.inner.get_fork_mut(idx);
1166
1167            // depth 0 will be the default value when the fork was created
1168            if target_fork.journaled_state.depth == 0 {
1169                // Initialize caller with its fork info
1170                if let Some(mut acc) = caller_account {
1171                    let fork_account = Database::basic(&mut target_fork.db, caller)?
1172                        .ok_or(BackendError::MissingAccount(caller))?;
1173
1174                    acc.info = fork_account;
1175                    target_fork.journaled_state.state.insert(caller, acc);
1176                }
1177            }
1178        } else {
1179            // this is the first time a fork is selected. This means up to this point all changes
1180            // are made in a single `JournaledState`, for example after a `setup` that only created
1181            // different forks. Since the `JournaledState` is valid for all forks until the
1182            // first fork is selected, we need to update it for all forks and use it as init state
1183            // for all future forks
1184
1185            self.set_init_journaled_state(active_journaled_state.clone());
1186            self.prepare_init_journal_state()?;
1187
1188            // Make sure that the next created fork has a depth of 0.
1189            self.fork_init_journaled_state.depth = 0;
1190        }
1191
1192        {
1193            // update the shared state and track
1194            let mut fork = self.inner.take_fork(idx);
1195
1196            // Make sure all persistent accounts on the newly selected fork reflect same state as
1197            // the active db / previous fork.
1198            // This can get out of sync when multiple forks are created on test `setUp`, then a
1199            // fork is selected and persistent contract is changed. If first action in test is to
1200            // select a different fork, then the persistent contract state won't reflect changes
1201            // done in `setUp` for the other fork.
1202            // See <https://github.com/foundry-rs/foundry/issues/10296> and <https://github.com/foundry-rs/foundry/issues/10552>.
1203            let persistent_accounts = self.inner.persistent_accounts.clone();
1204            if let Some(db) = self.active_fork_db_mut() {
1205                for addr in persistent_accounts {
1206                    let Ok(db_account) = db.load_account(addr) else { continue };
1207
1208                    let Some(fork_account) = fork.journaled_state.state.get_mut(&addr) else {
1209                        continue;
1210                    };
1211
1212                    for (key, val) in &db_account.storage {
1213                        if let Some(fork_storage) = fork_account.storage.get_mut(key) {
1214                            fork_storage.present_value = *val;
1215                        }
1216                    }
1217                }
1218            }
1219
1220            // since all forks handle their state separately, the depth can drift
1221            // this is a handover where the target fork starts at the same depth where it was
1222            // selected. This ensures that there are no gaps in depth which would
1223            // otherwise cause issues with the tracer
1224            fork.journaled_state.depth = active_journaled_state.depth;
1225
1226            // another edge case where a fork is created and selected during setup with not
1227            // necessarily the same caller as for the test, however we must always
1228            // ensure that fork's state contains the current sender
1229            let caller = tx_env.caller();
1230            fork.journaled_state.state.entry(caller).or_insert_with(|| {
1231                let caller_account = active_journaled_state
1232                    .state
1233                    .get(&caller)
1234                    .map(|acc| acc.info.clone())
1235                    .unwrap_or_default();
1236
1237                if !fork.db.cache.accounts.contains_key(&caller) {
1238                    // update the caller account which is required by the evm
1239                    fork.db.insert_account_info(caller, caller_account.clone());
1240                }
1241                caller_account.into()
1242            });
1243
1244            self.update_fork_db(active_journaled_state, &mut fork);
1245
1246            // insert the fork back
1247            self.inner.set_fork(idx, fork);
1248        }
1249
1250        self.active_fork_ids = Some((id, idx));
1251        // Update current environment with environment of newly selected fork.
1252        // Preserve the configured spec (evm_version) from the current environment — the fork's
1253        // evm_env is built with SPEC::default() and must not override the user's hardfork setting.
1254        let preserved_spec = evm_env.cfg_env.spec;
1255        tx_env.set_chain_id(Some(fork_evm_env.cfg_env.chain_id));
1256        *evm_env = fork_evm_env;
1257        evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1258
1259        Ok(())
1260    }
1261
1262    /// This is effectively the same as [`Self::create_select_fork()`] but updating an existing
1263    /// [ForkId] that is mapped to the [LocalForkId]
1264    fn roll_fork(
1265        &mut self,
1266        id: Option<LocalForkId>,
1267        block_number: u64,
1268        evm_env: &mut EvmEnvFor<FEN>,
1269        journaled_state: &mut JournaledState,
1270    ) -> eyre::Result<()> {
1271        trace!(?id, ?block_number, "roll fork");
1272        let id = self.ensure_fork(id)?;
1273        let (fork_id, backend, fork_env) =
1274            self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?;
1275        // this will update the local mapping
1276        self.inner.roll_fork(id, fork_id, backend)?;
1277
1278        if let Some((active_id, active_idx)) = self.active_fork_ids {
1279            // the currently active fork is the targeted fork of this call
1280            if active_id == id {
1281                // need to update the block's env settings right away, which is otherwise set when
1282                // forks are selected `select_fork`
1283                let preserved_spec = evm_env.cfg_env.spec;
1284                *evm_env = fork_env;
1285                evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1286
1287                // we also need to update the journaled_state right away, this has essentially the
1288                // same effect as selecting (`select_fork`) by discarding
1289                // non-persistent storage from the journaled_state. This which will
1290                // reset cached state from the previous block
1291                let mut persistent_addrs = self.inner.persistent_accounts.clone();
1292                // we also want to copy the caller state here
1293                persistent_addrs.extend(self.caller_address());
1294
1295                let active = self.inner.get_fork_mut(active_idx);
1296                active.journaled_state = self.fork_init_journaled_state.clone();
1297                active.journaled_state.depth = journaled_state.depth;
1298
1299                for addr in persistent_addrs {
1300                    merge_journaled_state_data(addr, journaled_state, &mut active.journaled_state);
1301                }
1302
1303                // Ensure all previously loaded accounts are present in the journaled state to
1304                // prevent issues in the new journalstate, e.g. assumptions that accounts are loaded
1305                // if the account is not touched, we reload it, if it's touched we clone it.
1306                //
1307                // Special case for accounts that are not created: we don't merge their state but
1308                // load it in order to reflect their state at the new block (they should explicitly
1309                // be marked as persistent if it is desired to keep state between fork rolls).
1310                for (addr, acc) in &journaled_state.state {
1311                    if acc.is_created() {
1312                        if acc.is_touched() {
1313                            merge_journaled_state_data(
1314                                *addr,
1315                                journaled_state,
1316                                &mut active.journaled_state,
1317                            );
1318                        }
1319                    } else {
1320                        let _ = active.journaled_state.load_account(&mut active.db, *addr);
1321                    }
1322                }
1323
1324                *journaled_state = active.journaled_state.clone();
1325            }
1326        }
1327        Ok(())
1328    }
1329
1330    fn roll_fork_to_transaction(
1331        &mut self,
1332        id: Option<LocalForkId>,
1333        transaction: B256,
1334        evm_env: &mut EvmEnvFor<FEN>,
1335        journaled_state: &mut JournaledState,
1336    ) -> eyre::Result<()> {
1337        trace!(?id, ?transaction, "roll fork to transaction");
1338        let id = self.ensure_fork(id)?;
1339
1340        let (fork_block, block) =
1341            self.get_block_number_and_block_for_transaction(id, transaction)?;
1342
1343        // roll the fork to the transaction's parent block or latest if it's pending, because we
1344        // need to fork off the parent block's state for tx level forking and then replay the txs
1345        // before the tx in that block to get the state at the tx
1346        self.roll_fork(Some(id), fork_block, evm_env, journaled_state)?;
1347
1348        // we need to update the env to the block
1349        update_env_block(evm_env, block.header());
1350
1351        // after we forked at the fork block we need to properly update the block env to the block
1352        // env of the tx's block
1353        let _ = self
1354            .forks
1355            .update_block_env(self.inner.ensure_fork_id(id).cloned()?, evm_env.block_env.clone());
1356
1357        // replay all transactions that came before
1358        self.replay_until(id, evm_env.clone(), transaction, journaled_state)?;
1359
1360        Ok(())
1361    }
1362
1363    fn transact(
1364        &mut self,
1365        maybe_id: Option<LocalForkId>,
1366        transaction: B256,
1367        mut evm_env: EvmEnvFor<FEN>,
1368        journaled_state: &mut JournaledState,
1369        inspector: &mut dyn for<'db> FoundryInspectorExt<
1370            <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
1371        >,
1372    ) -> eyre::Result<()> {
1373        trace!(?maybe_id, ?transaction, "execute transaction");
1374        let persistent_accounts = self.inner.persistent_accounts.clone();
1375        let id = self.ensure_fork(maybe_id)?;
1376        let fork_id = self.ensure_fork_id(id).cloned()?;
1377
1378        let tx = {
1379            let fork = self.inner.get_fork_by_id_mut(id)?;
1380            fork.backend().get_transaction(transaction)?
1381        };
1382        let tx_env = TxEnvFor::<FEN>::from_any_rpc_transaction(&tx)?;
1383
1384        // This is a bit ambiguous because the user wants to transact an arbitrary transaction in
1385        // the current context, but we're assuming the user wants to transact the transaction as it
1386        // was mined. Usually this is used in a combination of a fork at the transaction's parent
1387        // transaction in the block and then the transaction is transacted:
1388        // <https://github.com/foundry-rs/foundry/issues/6538>
1389        // So we modify the env to match the transaction's block.
1390        let (_fork_block, block) =
1391            self.get_block_number_and_block_for_transaction(id, transaction)?;
1392        update_env_block(&mut evm_env, block.header());
1393
1394        let fork = self.inner.get_fork_by_id_mut(id)?;
1395        commit_transaction::<FEN>(
1396            evm_env,
1397            tx_env,
1398            journaled_state,
1399            fork,
1400            &fork_id,
1401            &persistent_accounts,
1402            inspector,
1403        )
1404    }
1405
1406    fn transact_from_tx(
1407        &mut self,
1408        tx_env: TxEnvFor<FEN>,
1409        evm_env: EvmEnvFor<FEN>,
1410        journaled_state: &mut JournaledState,
1411        inspector: &mut dyn for<'db> FoundryInspectorExt<
1412            <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
1413        >,
1414    ) -> eyre::Result<()> {
1415        trace!("execute signed transaction");
1416
1417        self.commit(journaled_state.state.clone());
1418
1419        let res = {
1420            let mut db = self.clone();
1421            let depth = journaled_state.depth + 1;
1422            let mut evm =
1423                FEN::EvmFactory::default().create_foundry_nested_evm(&mut db, evm_env, inspector);
1424            evm.journal_inner_mut().depth = depth;
1425            evm.transact_raw(tx_env)?
1426        };
1427
1428        self.commit(res.state);
1429        update_state(&mut journaled_state.state, self, None)?;
1430
1431        Ok(())
1432    }
1433
1434    fn active_fork_id(&self) -> Option<LocalForkId> {
1435        self.active_fork_ids.map(|(id, _)| id)
1436    }
1437
1438    fn active_fork_url(&self) -> Option<String> {
1439        let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
1440        self.forks.get_fork_url(fork.clone()).ok()?
1441    }
1442
1443    fn active_fork_block_number(&self) -> Option<u64> {
1444        let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
1445        let fork_block = fork_block_number(fork);
1446        let env_block = self
1447            .forks
1448            .get_evm_env(fork.clone())
1449            .ok()
1450            .flatten()
1451            .map(|env| env.block_env.number().saturating_to::<u64>());
1452
1453        // On Arbitrum, `fork_block` is the L2 fork pin while `env_block` can be remapped to the
1454        // lower L1 block number. For tx-level forks, the fork pin is the parent state block while
1455        // the env is updated to the transaction's block. The larger value is the current L2 block.
1456        match (fork_block, env_block) {
1457            (Some(fork_block), Some(env_block)) => Some(fork_block.max(env_block)),
1458            (Some(fork_block), None) => Some(fork_block),
1459            (None, Some(env_block)) => Some(env_block),
1460            (None, None) => None,
1461        }
1462    }
1463
1464    fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
1465        if let Some(id) = id {
1466            if self.inner.issued_local_fork_ids.contains_key(&id) {
1467                return Ok(id);
1468            }
1469            eyre::bail!("Requested fork `{}` does not exist", id);
1470        }
1471        if let Some(id) = self.active_fork_id() {
1472            Ok(id)
1473        } else {
1474            eyre::bail!("No fork active");
1475        }
1476    }
1477
1478    fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
1479        self.inner.ensure_fork_id(id)
1480    }
1481
1482    fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic> {
1483        let active_id = self.active_fork_id()?;
1484        let active_fork = self.active_fork()?;
1485
1486        if self.inner.forks.len() == 1 {
1487            // we only want to provide additional diagnostics here when in multifork mode with > 1
1488            // forks
1489            return None;
1490        }
1491
1492        if !active_fork.is_contract(callee) && !is_contract_in_state(evm_state, callee) {
1493            // no contract for `callee` available on current fork, check if available on other forks
1494            let mut available_on = Vec::new();
1495            for (id, fork) in self.inner.forks_iter().filter(|(id, _)| *id != active_id) {
1496                trace!(?id, address=?callee, "checking if account exists");
1497                if fork.is_contract(callee) {
1498                    available_on.push(id);
1499                }
1500            }
1501
1502            return if available_on.is_empty() {
1503                Some(RevertDiagnostic::ContractDoesNotExist {
1504                    contract: callee,
1505                    active: active_id,
1506                    persistent: self.is_persistent(&callee),
1507                })
1508            } else {
1509                // likely user error: called a contract that's not available on active fork but is
1510                // present other forks
1511                Some(RevertDiagnostic::ContractExistsOnOtherForks {
1512                    contract: callee,
1513                    active: active_id,
1514                    available_on,
1515                })
1516            };
1517        }
1518        None
1519    }
1520
1521    /// Loads the account allocs from the given `allocs` map into the passed [JournaledState].
1522    ///
1523    /// Returns [Ok] if all accounts were successfully inserted into the journal, [Err] otherwise.
1524    fn load_allocs(
1525        &mut self,
1526        allocs: &BTreeMap<Address, GenesisAccount>,
1527        journaled_state: &mut JournaledState,
1528    ) -> Result<(), BackendError> {
1529        // Loop through all of the allocs defined in the map and commit them to the journal.
1530        for (addr, acc) in allocs {
1531            self.clone_account(acc, addr, journaled_state)?;
1532        }
1533
1534        Ok(())
1535    }
1536
1537    /// Copies bytecode, storage, nonce and balance from the given genesis account to the target
1538    /// address.
1539    ///
1540    /// Returns [Ok] if data was successfully inserted into the journal, [Err] otherwise.
1541    fn clone_account(
1542        &mut self,
1543        source: &GenesisAccount,
1544        target: &Address,
1545        journaled_state: &mut JournaledState,
1546    ) -> Result<(), BackendError> {
1547        // Fetch the account from the journaled state. Will create a new account if it does
1548        // not already exist.
1549        let mut state_acc = journaled_state.load_account_mut(self, *target)?;
1550
1551        // Set the account's bytecode and code hash, if the `bytecode` field is present.
1552        if let Some(bytecode) = source.code.as_ref() {
1553            let bytecode_hash = keccak256(bytecode);
1554            let bytecode = Bytecode::new_raw(bytecode.0.clone().into());
1555            state_acc.set_code(bytecode_hash, bytecode);
1556        }
1557
1558        // Set the account's balance.
1559        state_acc.set_balance(source.balance);
1560
1561        // Set the account's storage, if the `storage` field is present.
1562        if let Some(acc) = journaled_state.state.get_mut(target) {
1563            if let Some(storage) = source.storage.as_ref() {
1564                for (slot, value) in storage {
1565                    let slot = U256::from_be_bytes(slot.0);
1566                    acc.storage.insert(
1567                        slot,
1568                        EvmStorageSlot::new_changed(
1569                            acc.storage.get(&slot).map(|s| s.present_value).unwrap_or_default(),
1570                            U256::from_be_bytes(value.0),
1571                            TransactionId::ZERO,
1572                        ),
1573                    );
1574                }
1575            }
1576
1577            // Set the account's nonce.
1578            acc.info.nonce = source.nonce.unwrap_or_default();
1579        };
1580
1581        // Touch the account to ensure the loaded information persists if called in `setUp`.
1582        journaled_state.touch(*target);
1583
1584        Ok(())
1585    }
1586
1587    fn add_persistent_account(&mut self, account: Address) -> bool {
1588        trace!(?account, "add persistent account");
1589        self.inner.persistent_accounts.insert(account)
1590    }
1591
1592    fn invalidate_fork_cache_account(&mut self, address: Address) {
1593        let Some(fork_db) = self.active_fork_db_mut() else { return };
1594        trace!(?address, "invalidate fork cache account");
1595        fork_db.db.data().accounts.write().remove(&address);
1596        // Keep the local entry if it holds in-script modifications (e.g. `vm.deal`).
1597        if fork_db
1598            .cache
1599            .accounts
1600            .get(&address)
1601            .is_some_and(|account| account.account_state == AccountState::None)
1602        {
1603            fork_db.cache.accounts.remove(&address);
1604        }
1605    }
1606
1607    fn invalidate_fork_cache_storage(&mut self, address: Address, slot: U256) {
1608        let Some(fork_db) = self.active_fork_db_mut() else { return };
1609        trace!(?address, ?slot, "invalidate fork cache storage");
1610        if let Some(storage) = fork_db.db.data().storage.write().get_mut(&address) {
1611            storage.remove(&slot);
1612        }
1613        // Keep the local slot if the account holds in-script modifications.
1614        if let Some(account) = fork_db.cache.accounts.get_mut(&address)
1615            && account.account_state == AccountState::None
1616        {
1617            account.storage.remove(&slot);
1618        }
1619    }
1620
1621    fn remove_persistent_account(&mut self, account: &Address) -> bool {
1622        trace!(?account, "remove persistent account");
1623        self.inner.persistent_accounts.remove(account)
1624    }
1625
1626    fn is_persistent(&self, acc: &Address) -> bool {
1627        self.inner.persistent_accounts.contains(acc)
1628    }
1629
1630    fn allow_cheatcode_access(&mut self, account: Address) -> bool {
1631        trace!(?account, "allow cheatcode access");
1632        self.inner.cheatcode_access_accounts.insert(account)
1633    }
1634
1635    fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
1636        trace!(?account, "revoke cheatcode access");
1637        self.inner.cheatcode_access_accounts.remove(account)
1638    }
1639
1640    fn has_cheatcode_access(&self, account: &Address) -> bool {
1641        self.inner.cheatcode_access_accounts.contains(account)
1642    }
1643
1644    fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
1645        if let Some(db) = self.active_fork_db_mut() {
1646            db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
1647        } else {
1648            self.mem_db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
1649        }
1650    }
1651}
1652
1653impl<FEN: FoundryEvmNetwork> DatabaseRef for Backend<FEN> {
1654    type Error = DatabaseError;
1655
1656    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
1657        if let Some(db) = self.active_fork_db() {
1658            db.basic_ref(address)
1659        } else {
1660            Ok(self.mem_db.basic_ref(address)?)
1661        }
1662    }
1663
1664    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
1665        if let Some(db) = self.active_fork_db() {
1666            db.code_by_hash_ref(code_hash)
1667        } else {
1668            Ok(self.mem_db.code_by_hash_ref(code_hash)?)
1669        }
1670    }
1671
1672    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
1673        if let Some(db) = self.active_fork_db() {
1674            DatabaseRef::storage_ref(db, address, index)
1675        } else {
1676            Ok(DatabaseRef::storage_ref(&self.mem_db, address, index)?)
1677        }
1678    }
1679
1680    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
1681        if let Some(db) = self.active_fork_db() {
1682            db.block_hash_ref(number)
1683        } else {
1684            Ok(self.mem_db.block_hash_ref(number)?)
1685        }
1686    }
1687}
1688
1689impl<FEN: FoundryEvmNetwork> DatabaseCommit for Backend<FEN> {
1690    fn commit(&mut self, changes: AddressMap<Account>) {
1691        if let Some(db) = self.active_fork_db_mut() {
1692            db.commit(changes)
1693        } else {
1694            self.mem_db.commit(changes)
1695        }
1696    }
1697}
1698
1699impl<FEN: FoundryEvmNetwork> Database for Backend<FEN> {
1700    type Error = DatabaseError;
1701    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
1702        if let Some(db) = self.active_fork_db_mut() {
1703            Ok(db.basic(address)?)
1704        } else {
1705            Ok(self.mem_db.basic(address)?)
1706        }
1707    }
1708
1709    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
1710        if let Some(db) = self.active_fork_db_mut() {
1711            Ok(db.code_by_hash(code_hash)?)
1712        } else {
1713            Ok(self.mem_db.code_by_hash(code_hash)?)
1714        }
1715    }
1716
1717    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
1718        if let Some(db) = self.active_fork_db_mut() {
1719            Ok(Database::storage(db, address, index)?)
1720        } else {
1721            Ok(Database::storage(&mut self.mem_db, address, index)?)
1722        }
1723    }
1724
1725    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
1726        if let Some(db) = self.active_fork_db_mut() {
1727            Ok(db.block_hash(number)?)
1728        } else {
1729            Ok(self.mem_db.block_hash(number)?)
1730        }
1731    }
1732}
1733
1734/// Variants of a [revm::Database]
1735#[derive(Clone, Debug)]
1736pub enum BackendDatabaseSnapshot<N: Network, B: ForkBlockEnv = BlockEnv> {
1737    /// Simple in-memory [revm::Database]
1738    InMemory(FoundryEvmInMemoryDB),
1739    /// Contains the entire forking mode database
1740    Forked(LocalForkId, ForkId, ForkLookupIndex, Box<Fork<N, B>>),
1741}
1742
1743/// Represents a fork
1744#[derive(Clone, Debug)]
1745pub struct Fork<N: Network, B: ForkBlockEnv = BlockEnv> {
1746    db: ForkDB<N, B>,
1747    journaled_state: JournaledState,
1748}
1749
1750impl<N: Network, B: ForkBlockEnv> Fork<N, B> {
1751    /// Returns a reference to the underlying [`SharedBackend`].
1752    pub const fn backend(&self) -> &SharedBackend<N, B> {
1753        &self.db.db
1754    }
1755
1756    /// Returns true if the account is a contract
1757    pub fn is_contract(&self, acc: Address) -> bool {
1758        if let Ok(Some(acc)) = self.db.basic_ref(acc)
1759            && acc.code_hash != KECCAK_EMPTY
1760        {
1761            return true;
1762        }
1763        is_contract_in_state(&self.journaled_state.state, acc)
1764    }
1765
1766    /// Refreshes the given journaled state and the fork's own journaled state from the
1767    /// database, preserving persistent accounts.
1768    fn refresh_journaled_states(
1769        &mut self,
1770        journaled_state: &mut JournaledState,
1771        persistent_accounts: &HashSet<Address>,
1772    ) -> Result<(), BackendError> {
1773        update_state(&mut journaled_state.state, &mut self.db, Some(persistent_accounts))?;
1774        update_state(&mut self.journaled_state.state, &mut self.db, Some(persistent_accounts))?;
1775        Ok(())
1776    }
1777}
1778
1779/// Container type for various Backend related data
1780pub struct BackendInner<FEN: FoundryEvmNetwork> {
1781    /// Stores the `ForkId` of the fork the `Backend` launched with from the start.
1782    ///
1783    /// In other words if [`Backend::spawn()`] was called with a `CreateFork` command, to launch
1784    /// directly in fork mode, this holds the corresponding fork identifier of this fork.
1785    pub launched_with_fork: Option<(ForkId, LocalForkId, ForkLookupIndex)>,
1786    /// This tracks numeric fork ids and the `ForkId` used by the handler.
1787    ///
1788    /// This is necessary, because there can be multiple `Backends` associated with a single
1789    /// `ForkId` which is only a pair of endpoint + block. Since an existing fork can be
1790    /// modified (e.g. `roll_fork`), but this should only affect the fork that's unique for the
1791    /// test and not the `ForkId`
1792    ///
1793    /// This ensures we can treat forks as unique from the context of a test, so rolling to another
1794    /// is basically creating(or reusing) another `ForkId` that's then mapped to the previous
1795    /// issued _local_ numeric identifier, that remains constant, even if the underlying fork
1796    /// backend changes.
1797    pub issued_local_fork_ids: HashMap<LocalForkId, ForkId>,
1798    /// tracks all the created forks
1799    /// Contains the index of the corresponding `ForkDB` in the `forks` vec
1800    pub created_forks: HashMap<ForkId, ForkLookupIndex>,
1801    /// Holds all created fork databases
1802    // Note: data is stored in an `Option` so we can remove it without reshuffling
1803    pub forks: Vec<Option<Fork<AnyNetwork, BlockEnvFor<FEN>>>>,
1804    /// Contains state snapshots made at a certain point
1805    #[allow(clippy::type_complexity)]
1806    pub state_snapshots: StateSnapshots<
1807        BackendStateSnapshot<
1808            BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
1809            SpecFor<FEN>,
1810            BlockEnvFor<FEN>,
1811        >,
1812    >,
1813    /// Tracks whether there was a failure in a snapshot that was reverted
1814    ///
1815    /// The Test contract contains a bool variable that is set to true when an `assert` function
1816    /// failed. When a snapshot is reverted, it reverts the state of the evm, but we still want
1817    /// to know if there was an `assert` that failed after the snapshot was taken so that we can
1818    /// check if the test function passed all asserts even across snapshots. When a snapshot is
1819    /// reverted we get the _current_ `revm::JournaledState` which contains the state that we can
1820    /// check if the `_failed` variable is set,
1821    /// additionally
1822    pub has_state_snapshot_failure: bool,
1823    /// Tracks the caller of the test function
1824    pub caller: Option<Address>,
1825    /// Tracks numeric identifiers for forks
1826    pub next_fork_id: LocalForkId,
1827    /// All accounts that should be kept persistent when switching forks.
1828    /// This means all accounts stored here _don't_ use a separate storage section on each fork
1829    /// instead the use only one that's persistent across fork swaps.
1830    pub persistent_accounts: HashSet<Address>,
1831    /// The configured spec id
1832    pub spec_id: SpecFor<FEN>,
1833    /// All accounts that are allowed to execute cheatcodes
1834    pub cheatcode_access_accounts: HashSet<Address>,
1835}
1836
1837impl<FEN: FoundryEvmNetwork> Clone for BackendInner<FEN> {
1838    fn clone(&self) -> Self {
1839        Self {
1840            launched_with_fork: self.launched_with_fork.clone(),
1841            issued_local_fork_ids: self.issued_local_fork_ids.clone(),
1842            created_forks: self.created_forks.clone(),
1843            forks: self.forks.clone(),
1844            state_snapshots: self.state_snapshots.clone(),
1845            has_state_snapshot_failure: self.has_state_snapshot_failure,
1846            caller: self.caller,
1847            next_fork_id: self.next_fork_id,
1848            persistent_accounts: self.persistent_accounts.clone(),
1849            spec_id: self.spec_id,
1850            cheatcode_access_accounts: self.cheatcode_access_accounts.clone(),
1851        }
1852    }
1853}
1854
1855impl<FEN: FoundryEvmNetwork> Debug for BackendInner<FEN> {
1856    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1857        f.debug_struct("BackendInner")
1858            .field("launched_with_fork", &self.launched_with_fork)
1859            .field("issued_local_fork_ids", &self.issued_local_fork_ids)
1860            .field("created_forks", &self.created_forks)
1861            .field("forks", &self.forks)
1862            .field("state_snapshots", &self.state_snapshots)
1863            .field("has_state_snapshot_failure", &self.has_state_snapshot_failure)
1864            .field("caller", &self.caller)
1865            .field("next_fork_id", &self.next_fork_id)
1866            .field("persistent_accounts", &self.persistent_accounts)
1867            .field("spec_id", &self.spec_id)
1868            .field("cheatcode_access_accounts", &self.cheatcode_access_accounts)
1869            .finish()
1870    }
1871}
1872
1873impl<FEN: FoundryEvmNetwork> BackendInner<FEN> {
1874    pub fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
1875        self.issued_local_fork_ids
1876            .get(&id)
1877            .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
1878    }
1879
1880    pub fn ensure_fork_index(&self, id: &ForkId) -> eyre::Result<ForkLookupIndex> {
1881        self.created_forks
1882            .get(id)
1883            .copied()
1884            .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
1885    }
1886
1887    pub fn ensure_fork_index_by_local_id(&self, id: LocalForkId) -> eyre::Result<ForkLookupIndex> {
1888        self.ensure_fork_index(self.ensure_fork_id(id)?)
1889    }
1890
1891    /// Returns the underlying fork mapped to the index
1892    #[track_caller]
1893    fn get_fork(&self, idx: ForkLookupIndex) -> &Fork<AnyNetwork, BlockEnvFor<FEN>> {
1894        debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1895        self.forks[idx].as_ref().unwrap()
1896    }
1897
1898    /// Returns the underlying fork mapped to the index
1899    #[track_caller]
1900    fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork<AnyNetwork, BlockEnvFor<FEN>> {
1901        debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1902        self.forks[idx].as_mut().unwrap()
1903    }
1904
1905    /// Returns the underlying fork corresponding to the id
1906    #[track_caller]
1907    fn get_fork_by_id_mut(
1908        &mut self,
1909        id: LocalForkId,
1910    ) -> eyre::Result<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
1911        let idx = self.ensure_fork_index_by_local_id(id)?;
1912        Ok(self.get_fork_mut(idx))
1913    }
1914
1915    /// Returns the underlying fork corresponding to the id
1916    #[track_caller]
1917    fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
1918        let idx = self.ensure_fork_index_by_local_id(id)?;
1919        Ok(self.get_fork(idx))
1920    }
1921
1922    /// Removes the fork
1923    fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork<AnyNetwork, BlockEnvFor<FEN>> {
1924        debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
1925        self.forks[idx].take().unwrap()
1926    }
1927
1928    fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork<AnyNetwork, BlockEnvFor<FEN>>) {
1929        self.forks[idx] = Some(fork)
1930    }
1931
1932    /// Returns an iterator over Forks
1933    pub fn forks_iter(
1934        &self,
1935    ) -> impl Iterator<Item = (LocalForkId, &Fork<AnyNetwork, BlockEnvFor<FEN>>)> + '_ {
1936        self.issued_local_fork_ids
1937            .iter()
1938            .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id])))
1939    }
1940
1941    /// Returns a mutable iterator over all Forks
1942    pub fn forks_iter_mut(
1943        &mut self,
1944    ) -> impl Iterator<Item = &mut Fork<AnyNetwork, BlockEnvFor<FEN>>> + '_ {
1945        self.forks.iter_mut().filter_map(|f| f.as_mut())
1946    }
1947
1948    /// Reverts the entire fork database
1949    pub fn revert_state_snapshot(
1950        &mut self,
1951        id: LocalForkId,
1952        fork_id: ForkId,
1953        idx: ForkLookupIndex,
1954        fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
1955    ) {
1956        self.created_forks.insert(fork_id.clone(), idx);
1957        self.issued_local_fork_ids.insert(id, fork_id);
1958        self.set_fork(idx, fork)
1959    }
1960
1961    /// Updates the fork and the local mapping and returns the new index for the `fork_db`
1962    pub fn update_fork_mapping(
1963        &mut self,
1964        id: LocalForkId,
1965        fork_id: ForkId,
1966        db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
1967        journaled_state: JournaledState,
1968    ) -> ForkLookupIndex {
1969        let idx = self.forks.len();
1970        self.issued_local_fork_ids.insert(id, fork_id.clone());
1971        self.created_forks.insert(fork_id, idx);
1972
1973        let fork = Fork { db, journaled_state };
1974        self.forks.push(Some(fork));
1975        idx
1976    }
1977
1978    pub fn roll_fork(
1979        &mut self,
1980        id: LocalForkId,
1981        new_fork_id: ForkId,
1982        backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
1983    ) -> eyre::Result<ForkLookupIndex> {
1984        let fork_id = self.ensure_fork_id(id)?;
1985        let idx = self.ensure_fork_index(fork_id)?;
1986
1987        if let Some(active) = self.forks[idx].as_mut() {
1988            // we initialize a _new_ `ForkDB` but keep the state of persistent accounts
1989            let mut new_db = ForkDB::new(backend);
1990            for addr in self.persistent_accounts.iter().copied() {
1991                merge_db_account_data(addr, &active.db, &mut new_db);
1992            }
1993            active.db = new_db;
1994        }
1995        // update mappings
1996        self.issued_local_fork_ids.insert(id, new_fork_id.clone());
1997        self.created_forks.insert(new_fork_id, idx);
1998        Ok(idx)
1999    }
2000
2001    /// Inserts a _new_ `ForkDB` and issues a new local fork identifier
2002    ///
2003    /// Also returns the index where the `ForDB` is stored
2004    pub fn insert_new_fork(
2005        &mut self,
2006        fork_id: ForkId,
2007        db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
2008        journaled_state: JournaledState,
2009    ) -> (LocalForkId, ForkLookupIndex) {
2010        let idx = self.forks.len();
2011        self.created_forks.insert(fork_id.clone(), idx);
2012        let id = self.next_id();
2013        self.issued_local_fork_ids.insert(id, fork_id);
2014        let fork = Fork { db, journaled_state };
2015        self.forks.push(Some(fork));
2016        (id, idx)
2017    }
2018
2019    fn next_id(&mut self) -> U256 {
2020        let id = self.next_fork_id;
2021        self.next_fork_id += U256::from(1);
2022        id
2023    }
2024
2025    /// Returns the number of issued ids
2026    pub fn len(&self) -> usize {
2027        self.issued_local_fork_ids.len()
2028    }
2029
2030    /// Returns true if no forks are issued
2031    pub fn is_empty(&self) -> bool {
2032        self.issued_local_fork_ids.is_empty()
2033    }
2034
2035    pub fn precompile_addresses(&self) -> AddressSet {
2036        let evm = FEN::EvmFactory::default().create_evm(
2037            EmptyDB::default(),
2038            EvmEnv::new(CfgEnv::new_with_spec(self.spec_id), Default::default()),
2039        );
2040        evm.precompiles().addresses().copied().collect()
2041    }
2042
2043    /// Returns a new, empty, `JournaledState` with set precompiles
2044    pub fn new_journaled_state(&self) -> JournaledState {
2045        let mut journal = {
2046            let mut journal_inner = JournalInner::new();
2047            journal_inner.set_spec_id(self.spec_id.into());
2048            journal_inner
2049        };
2050        let precompile_addresses = self.precompile_addresses();
2051        journal.warm_addresses.set_precompile_addresses(&precompile_addresses);
2052        journal
2053    }
2054}
2055
2056impl<FEN: FoundryEvmNetwork> Default for BackendInner<FEN> {
2057    fn default() -> Self {
2058        Self {
2059            launched_with_fork: None,
2060            issued_local_fork_ids: Default::default(),
2061            created_forks: Default::default(),
2062            forks: vec![],
2063            state_snapshots: Default::default(),
2064            has_state_snapshot_failure: false,
2065            caller: None,
2066            next_fork_id: Default::default(),
2067            persistent_accounts: Default::default(),
2068            spec_id: SpecFor::<FEN>::default(),
2069            // grant the cheatcode,default test and caller address access to execute cheatcodes
2070            // itself
2071            cheatcode_access_accounts: HashSet::from([
2072                CHEATCODE_ADDRESS,
2073                TEST_CONTRACT_ADDRESS,
2074                CALLER,
2075            ]),
2076        }
2077    }
2078}
2079
2080/// Clones the data of the given `accounts` from the `active` database into the `fork_db`
2081/// This includes the data held in storage (`CacheDB`) and kept in the `JournaledState`.
2082pub(crate) fn merge_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
2083    accounts: impl IntoIterator<Item = Address>,
2084    active: &CacheDB<ExtDB>,
2085    active_journaled_state: &mut JournaledState,
2086    target_fork: &mut Fork<N, B>,
2087) {
2088    for addr in accounts {
2089        merge_db_account_data(addr, active, &mut target_fork.db);
2090        merge_journaled_state_data(addr, active_journaled_state, &mut target_fork.journaled_state);
2091    }
2092
2093    *active_journaled_state = target_fork.journaled_state.clone();
2094}
2095
2096/// Clones the account data from the `active_journaled_state`  into the `fork_journaled_state`
2097fn merge_journaled_state_data(
2098    addr: Address,
2099    active_journaled_state: &JournaledState,
2100    fork_journaled_state: &mut JournaledState,
2101) {
2102    if let Some(mut acc) = active_journaled_state.state.get(&addr).cloned() {
2103        trace!(?addr, "updating journaled_state account data");
2104        if let Some(fork_account) = fork_journaled_state.state.get_mut(&addr) {
2105            // This will merge the fork's tracked storage with active storage and update values
2106            fork_account.storage.extend(std::mem::take(&mut acc.storage));
2107            // swap them so we can insert the account as whole in the next step
2108            std::mem::swap(&mut fork_account.storage, &mut acc.storage);
2109        }
2110        fork_journaled_state.state.insert(addr, acc);
2111    }
2112}
2113
2114/// Clones the account data from the `active` db into the `ForkDB`
2115fn merge_db_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
2116    addr: Address,
2117    active: &CacheDB<ExtDB>,
2118    fork_db: &mut ForkDB<N, B>,
2119) {
2120    trace!(?addr, "merging database data");
2121
2122    let Some(acc) = active.cache.accounts.get(&addr) else { return };
2123
2124    // port contract cache over
2125    if let Some(code) = active.cache.contracts.get(&acc.info.code_hash) {
2126        trace!("merging contract cache");
2127        fork_db.cache.contracts.insert(acc.info.code_hash, code.clone());
2128    }
2129
2130    // port account storage over
2131    use std::collections::hash_map::Entry;
2132    match fork_db.cache.accounts.entry(addr) {
2133        Entry::Vacant(vacant) => {
2134            trace!("target account not present - inserting from active");
2135            // if the fork_db doesn't have the target account
2136            // insert the entire thing
2137            vacant.insert(acc.clone());
2138        }
2139        Entry::Occupied(mut occupied) => {
2140            trace!("target account present - merging storage slots");
2141            // if the fork_db does have the system,
2142            // extend the existing storage (overriding)
2143            let fork_account = occupied.get_mut();
2144            fork_account.storage.extend(&acc.storage);
2145        }
2146    }
2147}
2148
2149/// Returns true of the address is a contract
2150fn is_contract_in_state(evm_state: &EvmState, acc: Address) -> bool {
2151    evm_state.get(&acc).map(|acc| acc.info.code_hash != KECCAK_EMPTY).unwrap_or_default()
2152}
2153
2154/// Updates the evm env's block with the block's data
2155fn update_env_block<SPEC, BLOCK: FoundryBlock>(
2156    evm_env: &mut EvmEnv<SPEC, BLOCK>,
2157    header: &impl BlockHeader,
2158) {
2159    let block_env = &mut evm_env.block_env;
2160    block_env.set_timestamp(U256::from(header.timestamp()));
2161    block_env.set_beneficiary(header.beneficiary());
2162    block_env.set_difficulty(header.difficulty());
2163    block_env.set_prevrandao(header.mix_hash());
2164    block_env.set_basefee(header.base_fee_per_gas().unwrap_or_default());
2165    block_env.set_gas_limit(header.gas_limit());
2166    block_env.set_number(U256::from(header.number()));
2167
2168    if let Some(excess_blob_gas) = header.excess_blob_gas() {
2169        evm_env.block_env.set_blob_excess_gas_and_price(
2170            excess_blob_gas,
2171            get_blob_base_fee_update_fraction(evm_env.cfg_env.chain_id, header.timestamp()),
2172        );
2173    }
2174}
2175
2176/// Executes the given transaction and commits state changes to the database _and_ the journaled
2177/// state, with an inspector.
2178fn commit_transaction<FEN: FoundryEvmNetwork>(
2179    evm_env: EvmEnvFor<FEN>,
2180    tx_env: TxEnvFor<FEN>,
2181    journaled_state: &mut JournaledState,
2182    fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
2183    fork_id: &ForkId,
2184    persistent_accounts: &HashSet<Address>,
2185    inspector: &mut dyn for<'db> FoundryInspectorExt<
2186        <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2187    >,
2188) -> eyre::Result<()> {
2189    let now = Instant::now();
2190    let res = {
2191        let fork = fork.clone();
2192        let journaled_state = journaled_state.clone();
2193        let depth = journaled_state.depth;
2194        let mut db: Backend<FEN> = Backend::new_with_fork(fork_id, fork, journaled_state)?;
2195
2196        let mut evm =
2197            FEN::EvmFactory::default().create_foundry_nested_evm(&mut db, evm_env, inspector);
2198        evm.journal_inner_mut().depth = depth + 1;
2199        evm.transact_raw(tx_env).wrap_err("backend: failed committing transaction")?
2200    };
2201    trace!(elapsed = ?now.elapsed(), "transacted transaction");
2202
2203    apply_state_changeset(res.state, journaled_state, fork, persistent_accounts)?;
2204    Ok(())
2205}
2206
2207/// Helper method which updates data in the state with the data from the database.
2208/// Does not change state for persistent accounts (for roll fork to transaction and transact).
2209pub fn update_state<DB: Database>(
2210    state: &mut EvmState,
2211    db: &mut DB,
2212    persistent_accounts: Option<&HashSet<Address>>,
2213) -> Result<(), DB::Error> {
2214    for (addr, acc) in state.iter_mut() {
2215        if persistent_accounts.is_none_or(|accounts| !accounts.contains(addr)) {
2216            acc.info = db.basic(*addr)?.unwrap_or_default();
2217            for (key, val) in &mut acc.storage {
2218                val.present_value = db.storage(*addr, *key)?;
2219            }
2220        }
2221    }
2222
2223    Ok(())
2224}
2225
2226/// Applies the changeset of a transaction to the active journaled state and also commits it in the
2227/// forked db
2228fn apply_state_changeset<N: Network, B: ForkBlockEnv>(
2229    state: EvmState,
2230    journaled_state: &mut JournaledState,
2231    fork: &mut Fork<N, B>,
2232    persistent_accounts: &HashSet<Address>,
2233) -> Result<(), BackendError> {
2234    // commit the state and update the loaded accounts
2235    fork.db.commit(state);
2236    fork.refresh_journaled_states(journaled_state, persistent_accounts)
2237}
2238
2239fn fork_block_number(fork: &ForkId) -> Option<u64> {
2240    let (_, block) = fork.as_str().rsplit_once('@')?;
2241    let block = block.split_once('-').map_or(block, |(block, _)| block);
2242    let block = block.strip_prefix("0x")?;
2243    u64::from_str_radix(block, 16).ok()
2244}
2245
2246#[cfg(test)]
2247mod tests {
2248    use crate::{backend::Backend, evm::EthEvmNetwork, fork::ForkId, opts::EvmOpts};
2249    use alloy_primitives::{U256, address};
2250    use alloy_provider::Provider;
2251    use foundry_common::provider::get_http_provider;
2252    use foundry_config::{Config, NamedChain};
2253    use foundry_fork_db::cache::{BlockchainDb, BlockchainDbMeta};
2254    use revm::{
2255        context::{BlockEnv, TxEnv},
2256        database::DatabaseRef,
2257        primitives::hardfork::SpecId,
2258    };
2259
2260    #[tokio::test(flavor = "multi_thread")]
2261    async fn can_read_write_cache() {
2262        let endpoint = &*foundry_test_utils::rpc::next_http_rpc_endpoint();
2263        let provider = get_http_provider(endpoint);
2264
2265        let block_num = provider.get_block_number().await.unwrap();
2266
2267        let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
2268        evm_opts.fork_url = Some(endpoint.to_string());
2269        evm_opts.fork_block_number = Some(block_num);
2270
2271        let (evm_env, _, fork_block) = evm_opts.env::<SpecId, BlockEnv, TxEnv>().await.unwrap();
2272
2273        let fork =
2274            evm_opts.get_fork(&Config::default(), evm_env.cfg_env.chain_id, fork_block).unwrap();
2275
2276        let backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
2277
2278        // some rng contract from etherscan
2279        let address = address!("0x63091244180ae240c87d1f528f5f269134cb07b3");
2280
2281        let num_slots = 5;
2282        let _account = backend.basic_ref(address);
2283        for idx in 0..num_slots {
2284            let _ = backend.storage_ref(address, U256::from(idx));
2285        }
2286        drop(backend);
2287
2288        let meta = BlockchainDbMeta {
2289            chain: None,
2290            block_env: evm_env.block_env,
2291            hosts: Default::default(),
2292        };
2293
2294        let db = BlockchainDb::new(
2295            meta,
2296            Some(Config::foundry_block_cache_dir(NamedChain::Mainnet, block_num).unwrap()),
2297        );
2298        assert!(db.accounts().read().contains_key(&address));
2299        assert!(db.storage().read().contains_key(&address));
2300        assert_eq!(db.storage().read().get(&address).unwrap().len(), num_slots as usize);
2301    }
2302
2303    #[test]
2304    fn parses_fork_block_number_from_fork_id() {
2305        let fork = ForkId::new("https://example.com/@rpc", Some(75_219_831));
2306        assert_eq!(super::fork_block_number(&fork), Some(75_219_831));
2307        assert_eq!(
2308            super::fork_block_number(&format!("{}-1", fork.as_str()).into()),
2309            Some(75_219_831)
2310        );
2311        assert_eq!(super::fork_block_number(&ForkId::new("https://example.com", None)), None);
2312    }
2313}