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