Skip to main content

foundry_evm_core/backend/
mod.rs

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