foundry_evm_core/backend/
cow.rs1use 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#[derive(Clone, Debug)]
45pub struct CowBackend<'a> {
46 pub backend: Cow<'a, Backend>,
50 spec_id: Option<SpecId>,
53}
54
55impl<'a> CowBackend<'a> {
56 pub fn new_borrowed(backend: &'a Backend) -> Self {
58 Self { backend: Cow::Borrowed(backend), spec_id: Some(SpecId::default()) }
59 }
60
61 #[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 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 pub fn has_state_snapshot_failure(&self) -> bool {
88 self.backend.has_state_snapshot_failure()
89 }
90
91 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 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 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}