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    EthInspectorExt,
6    backend::{
7        Backend, DatabaseExt, JournaledState, LocalForkId, RevertStateSnapshotAction,
8        diagnostic::RevertDiagnostic,
9    },
10    fork::{CreateFork, ForkId},
11};
12use alloy_evm::{Evm, EvmEnv};
13use alloy_genesis::GenesisAccount;
14use alloy_primitives::{Address, B256, TxKind, U256};
15use eyre::WrapErr;
16use foundry_fork_db::DatabaseError;
17use revm::{
18    Database, DatabaseCommit,
19    bytecode::Bytecode,
20    context::TxEnv,
21    context_interface::result::ResultAndState,
22    database::DatabaseRef,
23    primitives::{HashMap as Map, hardfork::SpecId},
24    state::{Account, AccountInfo, EvmState},
25};
26use std::{borrow::Cow, collections::BTreeMap};
27
28/// A wrapper around `Backend` that ensures only `revm::DatabaseRef` functions are called.
29///
30/// Any changes made during its existence that affect the caching layer of the underlying Database
31/// will result in a clone of the initial Database. Therefore, this backend type is basically
32/// a clone-on-write `Backend`, where cloning is only necessary if cheatcodes will modify the
33/// `Backend`
34///
35/// Entire purpose of this type is for fuzzing. A test function fuzzer will repeatedly execute the
36/// function via immutable raw (no state changes) calls.
37///
38/// **N.B.**: we're assuming cheatcodes that alter the state (like multi fork swapping) are niche.
39/// If they executed, it will require a clone of the initial input database.
40/// This way we can support these cheatcodes cheaply without adding overhead for tests that
41/// don't make use of them. Alternatively each test case would require its own `Backend` clone,
42/// which would add significant overhead for large fuzz sets even if the Database is not big after
43/// setup.
44#[derive(Clone, Debug)]
45pub struct CowBackend<'a> {
46    /// The underlying `Backend`.
47    ///
48    /// No calls on the `CowBackend` will ever persistently modify the `backend`'s state.
49    pub backend: Cow<'a, Backend>,
50    /// Pending initialization params for the backend on first mutable access.
51    /// `None` means the backend has already been initialized for the current call.
52    pending_init: Option<(SpecId, Address, TxKind)>,
53}
54
55impl<'a> CowBackend<'a> {
56    /// Creates a new `CowBackend` with the given `Backend`.
57    pub fn new_borrowed(backend: &'a Backend) -> Self {
58        Self { backend: Cow::Borrowed(backend), pending_init: None }
59    }
60
61    /// Executes the configured transaction of the `env` without committing state changes
62    ///
63    /// Note: in case there are any cheatcodes executed that modify the environment, this will
64    /// update the given `env` with the new values.
65    #[instrument(name = "inspect", level = "debug", skip_all)]
66    pub fn inspect<I: EthInspectorExt>(
67        &mut self,
68        evm_env: &mut EvmEnv,
69        tx_env: &mut TxEnv,
70        inspector: I,
71    ) -> eyre::Result<ResultAndState> {
72        // this is a new call to inspect with a new env, so even if we've cloned the backend
73        // already, we reset the initialized state
74        self.pending_init = Some((evm_env.cfg_env.spec, tx_env.caller, tx_env.kind));
75
76        let mut evm = crate::evm::new_eth_evm_with_inspector(
77            self,
78            evm_env.clone(),
79            tx_env.clone(),
80            inspector,
81        );
82
83        let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?;
84
85        *evm_env = EvmEnv::new(evm.cfg.clone(), evm.block.clone());
86        *tx_env = evm.tx.clone();
87
88        Ok(res)
89    }
90
91    /// Returns whether there was a state snapshot failure in the backend.
92    ///
93    /// This is bubbled up from the underlying Copy-On-Write backend when a revert occurs.
94    pub fn has_state_snapshot_failure(&self) -> bool {
95        self.backend.has_state_snapshot_failure()
96    }
97
98    /// Returns a mutable instance of the Backend.
99    ///
100    /// If this is the first time this is called, the backed is cloned and initialized.
101    fn backend_mut(&mut self) -> &mut Backend {
102        if let Some((spec_id, caller, tx_kind)) = self.pending_init.take() {
103            let backend = self.backend.to_mut();
104            backend.initialize(spec_id, caller, tx_kind);
105            return backend;
106        }
107        self.backend.to_mut()
108    }
109
110    /// Returns a mutable instance of the Backend if it is initialized.
111    fn initialized_backend_mut(&mut self) -> Option<&mut Backend> {
112        if self.pending_init.is_none() {
113            return Some(self.backend.to_mut());
114        }
115        None
116    }
117}
118
119impl DatabaseExt for CowBackend<'_> {
120    fn snapshot_state(&mut self, journaled_state: &JournaledState, evm_env: &EvmEnv) -> U256 {
121        self.backend_mut().snapshot_state(journaled_state, evm_env)
122    }
123
124    fn revert_state(
125        &mut self,
126        id: U256,
127        journaled_state: &JournaledState,
128        evm_env: &mut EvmEnv,
129        tx_env: &mut TxEnv,
130        action: RevertStateSnapshotAction,
131    ) -> Option<JournaledState> {
132        self.backend_mut().revert_state(id, journaled_state, evm_env, tx_env, action)
133    }
134
135    fn delete_state_snapshot(&mut self, id: U256) -> bool {
136        // delete state snapshot requires a previous snapshot to be initialized
137        if let Some(backend) = self.initialized_backend_mut() {
138            return backend.delete_state_snapshot(id);
139        }
140        false
141    }
142
143    fn delete_state_snapshots(&mut self) {
144        if let Some(backend) = self.initialized_backend_mut() {
145            backend.delete_state_snapshots()
146        }
147    }
148
149    fn create_fork(&mut self, fork: CreateFork) -> eyre::Result<LocalForkId> {
150        self.backend.to_mut().create_fork(fork)
151    }
152
153    fn create_fork_at_transaction(
154        &mut self,
155        fork: CreateFork,
156        transaction: B256,
157    ) -> eyre::Result<LocalForkId> {
158        self.backend.to_mut().create_fork_at_transaction(fork, transaction)
159    }
160
161    fn select_fork(
162        &mut self,
163        id: LocalForkId,
164        evm_env: &mut EvmEnv,
165        tx_env: &mut TxEnv,
166        journaled_state: &mut JournaledState,
167    ) -> eyre::Result<()> {
168        self.backend_mut().select_fork(id, evm_env, tx_env, journaled_state)
169    }
170
171    fn roll_fork(
172        &mut self,
173        id: Option<LocalForkId>,
174        block_number: u64,
175        evm_env: &mut EvmEnv,
176        tx_env: &mut TxEnv,
177        journaled_state: &mut JournaledState,
178    ) -> eyre::Result<()> {
179        self.backend_mut().roll_fork(id, block_number, evm_env, tx_env, journaled_state)
180    }
181
182    fn roll_fork_to_transaction(
183        &mut self,
184        id: Option<LocalForkId>,
185        transaction: B256,
186        evm_env: &mut EvmEnv,
187        tx_env: &mut TxEnv,
188        journaled_state: &mut JournaledState,
189    ) -> eyre::Result<()> {
190        self.backend_mut().roll_fork_to_transaction(
191            id,
192            transaction,
193            evm_env,
194            tx_env,
195            journaled_state,
196        )
197    }
198
199    fn transact(
200        &mut self,
201        id: Option<LocalForkId>,
202        transaction: B256,
203        evm_env: EvmEnv,
204        tx_env: TxEnv,
205        journaled_state: &mut JournaledState,
206        inspector: &mut dyn EthInspectorExt,
207    ) -> eyre::Result<()> {
208        self.backend_mut().transact(id, transaction, evm_env, tx_env, journaled_state, inspector)
209    }
210
211    fn transact_from_tx(
212        &mut self,
213        tx_env: &TxEnv,
214        evm_env: EvmEnv,
215        journaled_state: &mut JournaledState,
216        inspector: &mut dyn EthInspectorExt,
217    ) -> eyre::Result<()> {
218        self.backend_mut().transact_from_tx(tx_env, evm_env, journaled_state, inspector)
219    }
220
221    fn active_fork_id(&self) -> Option<LocalForkId> {
222        self.backend.active_fork_id()
223    }
224
225    fn active_fork_url(&self) -> Option<String> {
226        self.backend.active_fork_url()
227    }
228
229    fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
230        self.backend.ensure_fork(id)
231    }
232
233    fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
234        self.backend.ensure_fork_id(id)
235    }
236
237    fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic> {
238        self.backend.diagnose_revert(callee, evm_state)
239    }
240
241    fn load_allocs(
242        &mut self,
243        allocs: &BTreeMap<Address, GenesisAccount>,
244        journaled_state: &mut JournaledState,
245    ) -> Result<(), BackendError> {
246        self.backend.to_mut().load_allocs(allocs, journaled_state)
247    }
248
249    fn clone_account(
250        &mut self,
251        source: &GenesisAccount,
252        target: &Address,
253        journaled_state: &mut JournaledState,
254    ) -> Result<(), BackendError> {
255        self.backend.to_mut().clone_account(source, target, journaled_state)
256    }
257
258    fn is_persistent(&self, acc: &Address) -> bool {
259        self.backend.is_persistent(acc)
260    }
261
262    fn remove_persistent_account(&mut self, account: &Address) -> bool {
263        self.backend.to_mut().remove_persistent_account(account)
264    }
265
266    fn add_persistent_account(&mut self, account: Address) -> bool {
267        self.backend.to_mut().add_persistent_account(account)
268    }
269
270    fn allow_cheatcode_access(&mut self, account: Address) -> bool {
271        self.backend.to_mut().allow_cheatcode_access(account)
272    }
273
274    fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
275        self.backend.to_mut().revoke_cheatcode_access(account)
276    }
277
278    fn has_cheatcode_access(&self, account: &Address) -> bool {
279        self.backend.has_cheatcode_access(account)
280    }
281
282    fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
283        self.backend.to_mut().set_blockhash(block_number, block_hash);
284    }
285}
286
287impl DatabaseRef for CowBackend<'_> {
288    type Error = DatabaseError;
289
290    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
291        DatabaseRef::basic_ref(self.backend.as_ref(), address)
292    }
293
294    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
295        DatabaseRef::code_by_hash_ref(self.backend.as_ref(), code_hash)
296    }
297
298    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
299        DatabaseRef::storage_ref(self.backend.as_ref(), address, index)
300    }
301
302    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
303        DatabaseRef::block_hash_ref(self.backend.as_ref(), number)
304    }
305}
306
307impl Database for CowBackend<'_> {
308    type Error = DatabaseError;
309
310    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
311        DatabaseRef::basic_ref(self, address)
312    }
313
314    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
315        DatabaseRef::code_by_hash_ref(self, code_hash)
316    }
317
318    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
319        DatabaseRef::storage_ref(self, address, index)
320    }
321
322    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
323        DatabaseRef::block_hash_ref(self, number)
324    }
325}
326
327impl DatabaseCommit for CowBackend<'_> {
328    fn commit(&mut self, changes: Map<Address, Account>) {
329        self.backend.to_mut().commit(changes)
330    }
331}