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