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