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