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    backend::{
6        diagnostic::RevertDiagnostic, Backend, DatabaseExt, JournaledState, LocalForkId,
7        RevertStateSnapshotAction,
8    },
9    fork::{CreateFork, ForkId},
10    AsEnvMut, Env, EnvMut, InspectorExt,
11};
12use alloy_evm::Evm;
13use alloy_genesis::GenesisAccount;
14use alloy_primitives::{Address, B256, U256};
15use alloy_rpc_types::TransactionRequest;
16use eyre::WrapErr;
17use foundry_fork_db::DatabaseError;
18use revm::{
19    bytecode::Bytecode,
20    context_interface::result::ResultAndState,
21    database::DatabaseRef,
22    primitives::{hardfork::SpecId, HashMap as Map},
23    state::{Account, AccountInfo},
24    Database, DatabaseCommit,
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    /// Keeps track of whether the backed is already initialized
51    is_initialized: bool,
52    /// The [SpecId] of the current backend.
53    spec_id: SpecId,
54}
55
56impl<'a> CowBackend<'a> {
57    /// Creates a new `CowBackend` with the given `Backend`.
58    pub fn new_borrowed(backend: &'a Backend) -> Self {
59        Self { backend: Cow::Borrowed(backend), is_initialized: false, spec_id: SpecId::default() }
60    }
61
62    /// Executes the configured transaction of the `env` without committing state changes
63    ///
64    /// Note: in case there are any cheatcodes executed that modify the environment, this will
65    /// update the given `env` with the new values.
66    #[instrument(name = "inspect", level = "debug", skip_all)]
67    pub fn inspect<I: InspectorExt>(
68        &mut self,
69        env: &mut Env,
70        inspector: &mut 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.is_initialized = false;
75        self.spec_id = env.evm_env.cfg_env.spec;
76
77        let mut evm = crate::evm::new_evm_with_inspector(self, env.to_owned(), inspector);
78
79        let res = evm.transact(env.tx.clone()).wrap_err("EVM error")?;
80
81        *env = evm.as_env_mut().to_owned();
82
83        Ok(res)
84    }
85
86    /// Returns whether there was a state snapshot failure in the backend.
87    ///
88    /// This is bubbled up from the underlying Copy-On-Write backend when a revert occurs.
89    pub fn has_state_snapshot_failure(&self) -> bool {
90        self.backend.has_state_snapshot_failure()
91    }
92
93    /// Returns a mutable instance of the Backend.
94    ///
95    /// If this is the first time this is called, the backed is cloned and initialized.
96    fn backend_mut(&mut self, env: &EnvMut<'_>) -> &mut Backend {
97        if !self.is_initialized {
98            let backend = self.backend.to_mut();
99            let mut env = env.to_owned();
100            env.evm_env.cfg_env.spec = self.spec_id;
101            backend.initialize(&env);
102            self.is_initialized = true;
103            return backend
104        }
105        self.backend.to_mut()
106    }
107
108    /// Returns a mutable instance of the Backend if it is initialized.
109    fn initialized_backend_mut(&mut self) -> Option<&mut Backend> {
110        if self.is_initialized {
111            return Some(self.backend.to_mut())
112        }
113        None
114    }
115}
116
117impl DatabaseExt for CowBackend<'_> {
118    fn snapshot_state(&mut self, journaled_state: &JournaledState, env: &mut EnvMut<'_>) -> U256 {
119        self.backend_mut(env).snapshot_state(journaled_state, env)
120    }
121
122    fn revert_state(
123        &mut self,
124        id: U256,
125        journaled_state: &JournaledState,
126        current: &mut EnvMut<'_>,
127        action: RevertStateSnapshotAction,
128    ) -> Option<JournaledState> {
129        self.backend_mut(current).revert_state(id, journaled_state, current, action)
130    }
131
132    fn delete_state_snapshot(&mut self, id: U256) -> bool {
133        // delete state snapshot requires a previous snapshot to be initialized
134        if let Some(backend) = self.initialized_backend_mut() {
135            return backend.delete_state_snapshot(id)
136        }
137        false
138    }
139
140    fn delete_state_snapshots(&mut self) {
141        if let Some(backend) = self.initialized_backend_mut() {
142            backend.delete_state_snapshots()
143        }
144    }
145
146    fn create_fork(&mut self, fork: CreateFork) -> eyre::Result<LocalForkId> {
147        self.backend.to_mut().create_fork(fork)
148    }
149
150    fn create_fork_at_transaction(
151        &mut self,
152        fork: CreateFork,
153        transaction: B256,
154    ) -> eyre::Result<LocalForkId> {
155        self.backend.to_mut().create_fork_at_transaction(fork, transaction)
156    }
157
158    fn select_fork(
159        &mut self,
160        id: LocalForkId,
161        env: &mut EnvMut<'_>,
162        journaled_state: &mut JournaledState,
163    ) -> eyre::Result<()> {
164        self.backend_mut(env).select_fork(id, env, journaled_state)
165    }
166
167    fn roll_fork(
168        &mut self,
169        id: Option<LocalForkId>,
170        block_number: u64,
171        env: &mut EnvMut<'_>,
172        journaled_state: &mut JournaledState,
173    ) -> eyre::Result<()> {
174        self.backend_mut(env).roll_fork(id, block_number, env, journaled_state)
175    }
176
177    fn roll_fork_to_transaction(
178        &mut self,
179        id: Option<LocalForkId>,
180        transaction: B256,
181        env: &mut EnvMut<'_>,
182        journaled_state: &mut JournaledState,
183    ) -> eyre::Result<()> {
184        self.backend_mut(env).roll_fork_to_transaction(id, transaction, env, journaled_state)
185    }
186
187    fn transact(
188        &mut self,
189        id: Option<LocalForkId>,
190        transaction: B256,
191        mut env: Env,
192        journaled_state: &mut JournaledState,
193        inspector: &mut dyn InspectorExt,
194    ) -> eyre::Result<()> {
195        self.backend_mut(&env.as_env_mut()).transact(
196            id,
197            transaction,
198            env,
199            journaled_state,
200            inspector,
201        )
202    }
203
204    fn transact_from_tx(
205        &mut self,
206        transaction: &TransactionRequest,
207        mut env: Env,
208        journaled_state: &mut JournaledState,
209        inspector: &mut dyn InspectorExt,
210    ) -> eyre::Result<()> {
211        self.backend_mut(&env.as_env_mut()).transact_from_tx(
212            transaction,
213            env,
214            journaled_state,
215            inspector,
216        )
217    }
218
219    fn active_fork_id(&self) -> Option<LocalForkId> {
220        self.backend.active_fork_id()
221    }
222
223    fn active_fork_url(&self) -> Option<String> {
224        self.backend.active_fork_url()
225    }
226
227    fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
228        self.backend.ensure_fork(id)
229    }
230
231    fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
232        self.backend.ensure_fork_id(id)
233    }
234
235    fn diagnose_revert(
236        &self,
237        callee: Address,
238        journaled_state: &JournaledState,
239    ) -> Option<RevertDiagnostic> {
240        self.backend.diagnose_revert(callee, journaled_state)
241    }
242
243    fn load_allocs(
244        &mut self,
245        allocs: &BTreeMap<Address, GenesisAccount>,
246        journaled_state: &mut JournaledState,
247    ) -> Result<(), BackendError> {
248        self.backend_mut(&Env::default().as_env_mut()).load_allocs(allocs, journaled_state)
249    }
250
251    fn clone_account(
252        &mut self,
253        source: &GenesisAccount,
254        target: &Address,
255        journaled_state: &mut JournaledState,
256    ) -> Result<(), BackendError> {
257        self.backend_mut(&Env::default().as_env_mut()).clone_account(
258            source,
259            target,
260            journaled_state,
261        )
262    }
263
264    fn is_persistent(&self, acc: &Address) -> bool {
265        self.backend.is_persistent(acc)
266    }
267
268    fn remove_persistent_account(&mut self, account: &Address) -> bool {
269        self.backend.to_mut().remove_persistent_account(account)
270    }
271
272    fn add_persistent_account(&mut self, account: Address) -> bool {
273        self.backend.to_mut().add_persistent_account(account)
274    }
275
276    fn allow_cheatcode_access(&mut self, account: Address) -> bool {
277        self.backend.to_mut().allow_cheatcode_access(account)
278    }
279
280    fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
281        self.backend.to_mut().revoke_cheatcode_access(account)
282    }
283
284    fn has_cheatcode_access(&self, account: &Address) -> bool {
285        self.backend.has_cheatcode_access(account)
286    }
287
288    fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
289        self.backend.to_mut().set_blockhash(block_number, block_hash);
290    }
291}
292
293impl DatabaseRef for CowBackend<'_> {
294    type Error = DatabaseError;
295
296    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
297        DatabaseRef::basic_ref(self.backend.as_ref(), address)
298    }
299
300    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
301        DatabaseRef::code_by_hash_ref(self.backend.as_ref(), code_hash)
302    }
303
304    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
305        DatabaseRef::storage_ref(self.backend.as_ref(), address, index)
306    }
307
308    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
309        DatabaseRef::block_hash_ref(self.backend.as_ref(), number)
310    }
311}
312
313impl Database for CowBackend<'_> {
314    type Error = DatabaseError;
315
316    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
317        DatabaseRef::basic_ref(self, address)
318    }
319
320    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
321        DatabaseRef::code_by_hash_ref(self, code_hash)
322    }
323
324    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
325        DatabaseRef::storage_ref(self, address, index)
326    }
327
328    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
329        DatabaseRef::block_hash_ref(self, number)
330    }
331}
332
333impl DatabaseCommit for CowBackend<'_> {
334    fn commit(&mut self, changes: Map<Address, Account>) {
335        self.backend.to_mut().commit(changes)
336    }
337}