Skip to main content

foundry_evm_core/backend/
cow.rs

1//! A wrapper around `Backend` that is clone-on-write used for fuzzing.
2
3use super::BackendError;
4use crate::{
5    FoundryInspectorExt,
6    backend::{
7        Backend, ContextUpdateFor, DatabaseExt, ForkAccountField, JournaledState, LocalForkId,
8        RevertStateSnapshotAction, diagnostic::RevertDiagnostic,
9    },
10    evm::{
11        ChainFor, EvmEnvFor, FoundryContextFor, FoundryEvmFactory, FoundryEvmNetwork,
12        HaltReasonFor, SpecFor, TxEnvFor,
13    },
14    fork::{CreateFork, ForkId},
15};
16use alloy_evm::Evm;
17use alloy_genesis::GenesisAccount;
18use alloy_primitives::{Address, B256, TxKind, U256};
19use eyre::WrapErr;
20use foundry_fork_db::DatabaseError;
21use revm::{
22    Database, DatabaseCommit,
23    bytecode::Bytecode,
24    context::{ContextTr, Transaction},
25    context_interface::result::ResultAndState,
26    database::DatabaseRef,
27    primitives::AddressMap,
28    state::{Account, AccountInfo, EvmState},
29};
30use std::{borrow::Cow, collections::BTreeMap, fmt::Debug};
31
32#[cfg(feature = "monad")]
33use crate::evm::MonadEvmNetwork;
34
35/// A wrapper around `Backend` that ensures only `revm::DatabaseRef` functions are called.
36///
37/// Any changes made during its existence that affect the caching layer of the underlying Database
38/// will result in a clone of the initial Database. Therefore, this backend type is basically
39/// a clone-on-write `Backend`, where cloning is only necessary if cheatcodes will modify the
40/// `Backend`
41///
42/// Entire purpose of this type is for fuzzing. A test function fuzzer will repeatedly execute the
43/// function via immutable raw (no state changes) calls.
44///
45/// **N.B.**: we're assuming cheatcodes that alter the state (like multi fork swapping) are niche.
46/// If they executed, it will require a clone of the initial input database.
47/// This way we can support these cheatcodes cheaply without adding overhead for tests that
48/// don't make use of them. Alternatively each test case would require its own `Backend` clone,
49/// which would add significant overhead for large fuzz sets even if the Database is not big after
50/// setup.
51pub struct CowBackend<'a, FEN: FoundryEvmNetwork> {
52    /// The underlying `Backend`.
53    ///
54    /// No calls on the `CowBackend` will ever persistently modify the `backend`'s state.
55    pub backend: Cow<'a, Backend<FEN>>,
56    /// Pending initialization params for the backend on first mutable access.
57    /// `None` means the backend has already been initialized for the current call.
58    pending_init: Option<(SpecFor<FEN>, Address, TxKind)>,
59}
60
61impl<FEN: FoundryEvmNetwork> Clone for CowBackend<'_, FEN> {
62    fn clone(&self) -> Self {
63        Self { backend: self.backend.clone(), pending_init: self.pending_init }
64    }
65}
66
67impl<FEN: FoundryEvmNetwork> Debug for CowBackend<'_, FEN> {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        f.debug_struct("CowBackend")
70            .field("backend", &self.backend)
71            .field("pending_init", &self.pending_init)
72            .finish()
73    }
74}
75
76impl<'a, FEN: FoundryEvmNetwork> CowBackend<'a, FEN> {
77    /// Creates a new `CowBackend` with the given `Backend`.
78    pub const fn new_borrowed(backend: &'a Backend<FEN>) -> Self {
79        Self { backend: Cow::Borrowed(backend), pending_init: None }
80    }
81
82    /// Executes the configured transaction of the `env` without committing state changes
83    ///
84    /// Note: in case there are any cheatcodes executed that modify the environment, this will
85    /// update the given `env` with the new values.
86    #[instrument(name = "inspect", level = "debug", skip_all)]
87    pub fn inspect<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
88        &mut self,
89        evm_env: &mut EvmEnvFor<FEN>,
90        tx_env: &mut TxEnvFor<FEN>,
91        inspector: I,
92    ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
93        let chain_context = self.chain_context_for_synthetic_transaction(tx_env)?;
94        self.inspect_with_context(evm_env, tx_env, chain_context, inspector)
95    }
96
97    /// Executes the configured transaction with explicit network-specific context.
98    #[instrument(name = "inspect", level = "debug", skip_all)]
99    pub fn inspect_with_context<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
100        &mut self,
101        evm_env: &mut EvmEnvFor<FEN>,
102        tx_env: &mut TxEnvFor<FEN>,
103        chain_context: ChainFor<FEN>,
104        inspector: I,
105    ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
106        // this is a new call to inspect with a new env, so even if we've cloned the backend
107        // already, we reset the initialized state
108        self.pending_init = Some((evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind()));
109
110        let factory = FEN::EvmFactory::default();
111        let mut evm = factory.create_foundry_evm_with_inspector(self, evm_env.clone(), inspector);
112        *evm.chain_mut() = chain_context;
113
114        let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?;
115
116        *tx_env = evm.tx().clone();
117        *evm_env = evm.finish().1;
118
119        Ok(res)
120    }
121
122    /// Returns whether there was a state snapshot failure in the backend.
123    ///
124    /// This is bubbled up from the underlying Copy-On-Write backend when a revert occurs.
125    pub fn has_state_snapshot_failure(&self) -> bool {
126        self.backend.has_state_snapshot_failure()
127    }
128
129    /// Returns a mutable instance of the Backend.
130    ///
131    /// If this is the first time this is called, the backed is cloned and initialized.
132    fn backend_mut(&mut self) -> &mut Backend<FEN> {
133        if let Some((spec_id, caller, tx_kind)) = self.pending_init.take() {
134            let backend = self.backend.to_mut();
135            backend.initialize(spec_id, caller, tx_kind);
136            return backend;
137        }
138        self.backend.to_mut()
139    }
140}
141
142#[cfg(feature = "monad")]
143impl CowBackend<'_, MonadEvmNetwork> {
144    /// Tries to execute a canonical system transaction with explicit network-specific context.
145    #[instrument(name = "inspect_system_replay", level = "debug", skip_all)]
146    pub fn try_inspect_system_replay_with_context<
147        I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, MonadEvmNetwork>>,
148    >(
149        &mut self,
150        evm_env: &mut EvmEnvFor<MonadEvmNetwork>,
151        tx_env: &mut TxEnvFor<MonadEvmNetwork>,
152        chain_context: ChainFor<MonadEvmNetwork>,
153        inspector: &mut I,
154    ) -> eyre::Result<Option<ResultAndState<revm::context_interface::result::HaltReason>>> {
155        if crate::evm::protocol_system_call(tx_env)?.is_none() {
156            return Ok(None);
157        }
158
159        self.pending_init = Some((evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind()));
160
161        let factory = <MonadEvmNetwork as FoundryEvmNetwork>::EvmFactory::default();
162        let mut evm = factory.create_nested_evm_with_inspector(self, evm_env.clone(), inspector);
163        *evm.chain_mut() = chain_context;
164        let result = evm.transact_raw(tx_env.clone())?;
165
166        // A successful specialized replay replaces the EVM transaction with its synthetic system
167        // call. Keep the canonical envelope in `tx_env`; ordinary execution uses
168        // `inspect_with_context` above and copies inspector mutations back normally.
169        *evm_env = evm.to_evm_env();
170
171        Ok(Some(result))
172    }
173}
174
175impl<FEN: FoundryEvmNetwork> DatabaseExt<FEN::EvmFactory> for CowBackend<'_, FEN> {
176    fn chain_context_for_synthetic_transaction(
177        &self,
178        tx: &TxEnvFor<FEN>,
179    ) -> eyre::Result<ChainFor<FEN>> {
180        self.backend.chain_context_for_synthetic_transaction(tx)
181    }
182
183    fn snapshot_state(
184        &mut self,
185        journaled_state: &JournaledState,
186        evm_env: &EvmEnvFor<FEN>,
187    ) -> U256 {
188        self.backend_mut().snapshot_state(journaled_state, evm_env)
189    }
190
191    fn revert_state(
192        &mut self,
193        id: U256,
194        journaled_state: &JournaledState,
195        evm_env: &mut EvmEnvFor<FEN>,
196        caller: Address,
197        action: RevertStateSnapshotAction,
198    ) -> Option<JournaledState> {
199        self.backend_mut().revert_state(id, journaled_state, evm_env, caller, action)
200    }
201
202    fn delete_state_snapshot(&mut self, id: U256) -> bool {
203        // Snapshots can predate initialization; avoid cloning when the snapshot is missing.
204        if self.backend.state_snapshots().get(id).is_none() {
205            return false;
206        }
207        self.backend_mut().delete_state_snapshot(id)
208    }
209
210    fn delete_state_snapshots(&mut self) {
211        // Avoid cloning when there are no snapshots.
212        if self.backend.state_snapshots().is_empty() {
213            return;
214        }
215        self.backend_mut().delete_state_snapshots()
216    }
217
218    fn create_fork(&mut self, fork: CreateFork) -> eyre::Result<LocalForkId> {
219        self.backend.to_mut().create_fork(fork)
220    }
221
222    fn create_fork_at_transaction(
223        &mut self,
224        fork: CreateFork,
225        transaction: B256,
226    ) -> eyre::Result<LocalForkId> {
227        self.backend.to_mut().create_fork_at_transaction(fork, transaction)
228    }
229
230    fn select_fork(
231        &mut self,
232        id: LocalForkId,
233        evm_env: &mut EvmEnvFor<FEN>,
234        tx_env: &mut TxEnvFor<FEN>,
235        journaled_state: &mut JournaledState,
236    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
237        self.backend_mut().select_fork(id, evm_env, tx_env, journaled_state)
238    }
239
240    fn roll_fork(
241        &mut self,
242        id: Option<LocalForkId>,
243        block_number: u64,
244        evm_env: &mut EvmEnvFor<FEN>,
245        tx_env: &TxEnvFor<FEN>,
246        journaled_state: &mut JournaledState,
247    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
248        self.backend_mut().roll_fork(id, block_number, evm_env, tx_env, journaled_state)
249    }
250
251    fn roll_fork_to_transaction(
252        &mut self,
253        id: Option<LocalForkId>,
254        transaction: B256,
255        evm_env: &mut EvmEnvFor<FEN>,
256        tx_env: &TxEnvFor<FEN>,
257        journaled_state: &mut JournaledState,
258    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
259        self.backend_mut().roll_fork_to_transaction(
260            id,
261            transaction,
262            evm_env,
263            tx_env,
264            journaled_state,
265        )
266    }
267
268    fn transact(
269        &mut self,
270        id: Option<LocalForkId>,
271        transaction: B256,
272        evm_env: EvmEnvFor<FEN>,
273        outer_tx_env: &TxEnvFor<FEN>,
274        journaled_state: &mut JournaledState,
275        inspector: &mut dyn for<'db> FoundryInspectorExt<
276            <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
277        >,
278    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
279        self.backend_mut().transact(
280            id,
281            transaction,
282            evm_env,
283            outer_tx_env,
284            journaled_state,
285            inspector,
286        )
287    }
288
289    fn transact_from_tx(
290        &mut self,
291        tx_env: TxEnvFor<FEN>,
292        evm_env: EvmEnvFor<FEN>,
293        journaled_state: &mut JournaledState,
294        inspector: &mut dyn for<'db> FoundryInspectorExt<
295            <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
296        >,
297    ) -> eyre::Result<()> {
298        self.backend_mut().transact_from_tx(tx_env, evm_env, journaled_state, inspector)
299    }
300
301    fn active_fork_id(&self) -> Option<LocalForkId> {
302        self.backend.active_fork_id()
303    }
304
305    fn active_fork_url(&self) -> Option<String> {
306        self.backend.active_fork_url()
307    }
308
309    fn active_fork_source_chain_id(&self) -> Option<u64> {
310        self.backend.active_fork_source_chain_id()
311    }
312
313    fn active_fork_block_number(&self) -> Option<u64> {
314        self.backend.active_fork_block_number()
315    }
316
317    fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
318        self.backend.ensure_fork(id)
319    }
320
321    fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
322        self.backend.ensure_fork_id(id)
323    }
324
325    fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic> {
326        self.backend.diagnose_revert(callee, evm_state)
327    }
328
329    fn load_allocs(
330        &mut self,
331        allocs: &BTreeMap<Address, GenesisAccount>,
332        journaled_state: &mut JournaledState,
333    ) -> Result<(), BackendError> {
334        self.backend.to_mut().load_allocs(allocs, journaled_state)
335    }
336
337    fn clone_account(
338        &mut self,
339        source: &GenesisAccount,
340        target: &Address,
341        journaled_state: &mut JournaledState,
342    ) -> Result<(), BackendError> {
343        self.backend.to_mut().clone_account(source, target, journaled_state)
344    }
345
346    fn is_persistent(&self, acc: &Address) -> bool {
347        self.backend.is_persistent(acc)
348    }
349
350    fn refresh_fork_account(
351        &mut self,
352        address: Address,
353        field: ForkAccountField,
354        journaled_state: &mut JournaledState,
355    ) -> Result<(), BackendError> {
356        self.backend.to_mut().refresh_fork_account(address, field, journaled_state)
357    }
358
359    fn refresh_fork_storage(
360        &mut self,
361        address: Address,
362        slot: U256,
363        journaled_state: &mut JournaledState,
364    ) -> Result<(), BackendError> {
365        self.backend.to_mut().refresh_fork_storage(address, slot, journaled_state)
366    }
367
368    fn remove_persistent_account(&mut self, account: &Address) -> bool {
369        self.backend.to_mut().remove_persistent_account(account)
370    }
371
372    fn add_persistent_account(&mut self, account: Address) -> bool {
373        self.backend.to_mut().add_persistent_account(account)
374    }
375
376    fn allow_cheatcode_access(&mut self, account: Address) -> bool {
377        self.backend.to_mut().allow_cheatcode_access(account)
378    }
379
380    fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
381        self.backend.to_mut().revoke_cheatcode_access(account)
382    }
383
384    fn has_cheatcode_access(&self, account: &Address) -> bool {
385        self.backend.has_cheatcode_access(account)
386    }
387
388    fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
389        self.backend.to_mut().set_blockhash(block_number, block_hash);
390    }
391}
392
393impl<FEN: FoundryEvmNetwork> DatabaseRef for CowBackend<'_, FEN> {
394    type Error = DatabaseError;
395
396    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
397        DatabaseRef::basic_ref(self.backend.as_ref(), address)
398    }
399
400    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
401        DatabaseRef::code_by_hash_ref(self.backend.as_ref(), code_hash)
402    }
403
404    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
405        DatabaseRef::storage_ref(self.backend.as_ref(), address, index)
406    }
407
408    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
409        DatabaseRef::block_hash_ref(self.backend.as_ref(), number)
410    }
411}
412
413impl<FEN: FoundryEvmNetwork> Database for CowBackend<'_, FEN> {
414    type Error = DatabaseError;
415
416    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
417        DatabaseRef::basic_ref(self, address)
418    }
419
420    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
421        DatabaseRef::code_by_hash_ref(self, code_hash)
422    }
423
424    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
425        DatabaseRef::storage_ref(self, address, index)
426    }
427
428    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
429        DatabaseRef::block_hash_ref(self, number)
430    }
431}
432
433impl<FEN: FoundryEvmNetwork> DatabaseCommit for CowBackend<'_, FEN> {
434    fn commit(&mut self, changes: AddressMap<Account>) {
435        self.backend.to_mut().commit(changes)
436    }
437}