foundry_evm_core/backend/
strategy.rs1use std::{any::Any, fmt::Debug};
2
3use crate::{
4 Env, InspectorExt,
5 backend::{JournaledState, update_state},
6 env::AsEnvMut,
7 evm::new_evm_with_inspector,
8 utils::configure_tx_req_env,
9};
10
11use super::{Backend, BackendInner, Fork, ForkDB, FoundryEvmInMemoryDB};
12use alloy_evm::Evm;
13use alloy_primitives::Address;
14use alloy_rpc_types::TransactionRequest;
15use eyre::{Context, Result};
16use revm::{
17 DatabaseCommit, DatabaseRef, context_interface::result::ResultAndState, database::CacheDB,
18};
19use serde::{Deserialize, Serialize};
20
21pub trait BackendStrategyContext: Debug + Send + Sync + Any {
23 fn new_cloned(&self) -> Box<dyn BackendStrategyContext>;
25 fn as_any_ref(&self) -> &dyn Any;
27 fn as_any_mut(&mut self) -> &mut dyn Any;
29}
30
31impl BackendStrategyContext for () {
32 fn new_cloned(&self) -> Box<dyn BackendStrategyContext> {
33 Box::new(())
34 }
35
36 fn as_any_ref(&self) -> &dyn Any {
37 self
38 }
39
40 fn as_any_mut(&mut self) -> &mut dyn Any {
41 self
42 }
43}
44
45#[derive(Debug)]
47pub struct BackendStrategy {
48 pub runner: &'static dyn BackendStrategyRunner,
50 pub context: Box<dyn BackendStrategyContext>,
52}
53
54impl BackendStrategy {
55 pub fn new_evm() -> Self {
57 Self { runner: &EvmBackendStrategyRunner, context: Box::new(()) }
58 }
59}
60
61impl Clone for BackendStrategy {
62 fn clone(&self) -> Self {
63 Self { runner: self.runner, context: self.context.new_cloned() }
64 }
65}
66
67pub trait BackendStrategyRunner: Debug + Send + Sync {
68 fn inspect(
69 &self,
70 backend: &mut Backend,
71 env: &mut Env,
72 inspector: &mut dyn InspectorExt,
73 inspect_ctx: Box<dyn Any>,
74 ) -> Result<ResultAndState>;
75
76 fn update_fork_db(
78 &self,
79 ctx: &mut dyn BackendStrategyContext,
80 active_fork: Option<&Fork>,
81 mem_db: &FoundryEvmInMemoryDB,
82 backend_inner: &BackendInner,
83 active_journaled_state: &mut JournaledState,
84 target_fork: &mut Fork,
85 );
86
87 fn merge_journaled_state_data(
89 &self,
90 ctx: &mut dyn BackendStrategyContext,
91 addr: Address,
92 active_journaled_state: &JournaledState,
93 fork_journaled_state: &mut JournaledState,
94 );
95
96 fn merge_db_account_data(
97 &self,
98 ctx: &mut dyn BackendStrategyContext,
99 addr: Address,
100 active: &ForkDB,
101 fork_db: &mut ForkDB,
102 );
103
104 fn transact_from_tx(
105 &self,
106 backend: &mut Backend,
107 tx: &TransactionRequest,
108 env: Env,
109 journaled_state: &mut JournaledState,
110 inspector: &mut dyn InspectorExt,
111 inspect_ctx: Box<dyn Any>,
112 ) -> eyre::Result<()>;
113}
114
115#[derive(Debug, Default, Clone, Serialize, Deserialize)]
116pub struct EvmBackendStrategyRunner;
117
118impl BackendStrategyRunner for EvmBackendStrategyRunner {
119 fn inspect(
120 &self,
121 backend: &mut Backend,
122 env: &mut Env,
123 inspector: &mut dyn InspectorExt,
124 _inspect_ctx: Box<dyn Any>,
125 ) -> Result<ResultAndState> {
126 let mut evm = crate::evm::new_evm_with_inspector(backend, env.to_owned(), inspector);
127
128 let res = evm.transact(env.tx.clone()).wrap_err("EVM error")?;
129
130 *env = evm.as_env_mut().to_owned();
131
132 Ok(res)
133 }
134
135 fn update_fork_db(
136 &self,
137 _ctx: &mut dyn BackendStrategyContext,
138 active_fork: Option<&Fork>,
139 mem_db: &FoundryEvmInMemoryDB,
140 backend_inner: &BackendInner,
141 active_journaled_state: &mut JournaledState,
142 target_fork: &mut Fork,
143 ) {
144 self.update_fork_db_contracts(
145 active_fork,
146 mem_db,
147 backend_inner,
148 active_journaled_state,
149 target_fork,
150 )
151 }
152
153 fn merge_journaled_state_data(
154 &self,
155 _ctx: &mut dyn BackendStrategyContext,
156 addr: Address,
157 active_journaled_state: &JournaledState,
158 fork_journaled_state: &mut JournaledState,
159 ) {
160 EvmBackendMergeStrategy::merge_journaled_state_data(
161 addr,
162 active_journaled_state,
163 fork_journaled_state,
164 );
165 }
166
167 fn merge_db_account_data(
168 &self,
169 _ctx: &mut dyn BackendStrategyContext,
170 addr: Address,
171 active: &ForkDB,
172 fork_db: &mut ForkDB,
173 ) {
174 EvmBackendMergeStrategy::merge_db_account_data(addr, active, fork_db);
175 }
176
177 fn transact_from_tx(
178 &self,
179 backend: &mut Backend,
180 tx: &TransactionRequest,
181 mut env: Env,
182 journaled_state: &mut JournaledState,
183 inspector: &mut dyn InspectorExt,
184 _inspect_ctx: Box<dyn Any>,
185 ) -> eyre::Result<()> {
186 backend.commit(journaled_state.state.clone());
187
188 let res = {
189 configure_tx_req_env(&mut env.as_env_mut(), tx, None)?;
190
191 let mut db = backend.clone();
192 let mut evm = new_evm_with_inspector(&mut db, env.to_owned(), inspector);
193 evm.journaled_state.depth = journaled_state.depth + 1;
194 evm.transact(env.tx)?
195 };
196
197 backend.commit(res.state);
198 update_state(&mut journaled_state.state, backend, None)?;
199
200 Ok(())
201 }
202}
203
204impl EvmBackendStrategyRunner {
205 pub(crate) fn update_fork_db_contracts(
207 &self,
208 active_fork: Option<&Fork>,
209 mem_db: &FoundryEvmInMemoryDB,
210 backend_inner: &BackendInner,
211 active_journaled_state: &mut JournaledState,
212 target_fork: &mut Fork,
213 ) {
214 let accounts = backend_inner.persistent_accounts.iter().copied();
215 if let Some(db) = active_fork.map(|f| &f.db) {
216 EvmBackendMergeStrategy::merge_account_data(
217 accounts,
218 db,
219 active_journaled_state,
220 target_fork,
221 )
222 } else {
223 EvmBackendMergeStrategy::merge_account_data(
224 accounts,
225 mem_db,
226 active_journaled_state,
227 target_fork,
228 )
229 }
230 }
231}
232
233pub struct EvmBackendMergeStrategy;
234impl EvmBackendMergeStrategy {
235 pub fn merge_account_data<ExtDB: DatabaseRef>(
238 accounts: impl IntoIterator<Item = Address>,
239 active: &CacheDB<ExtDB>,
240 active_journaled_state: &mut JournaledState,
241 target_fork: &mut Fork,
242 ) {
243 for addr in accounts.into_iter() {
244 Self::merge_db_account_data(addr, active, &mut target_fork.db);
245 Self::merge_journaled_state_data(
246 addr,
247 active_journaled_state,
248 &mut target_fork.journaled_state,
249 );
250 }
251
252 *active_journaled_state = target_fork.journaled_state.clone();
253 }
254
255 pub fn merge_journaled_state_data(
257 addr: Address,
258 active_journaled_state: &JournaledState,
259 fork_journaled_state: &mut JournaledState,
260 ) {
261 if let Some(mut acc) = active_journaled_state.state.get(&addr).cloned() {
262 trace!(?addr, "updating journaled_state account data");
263 if let Some(fork_account) = fork_journaled_state.state.get_mut(&addr) {
264 fork_account.storage.extend(std::mem::take(&mut acc.storage));
266 std::mem::swap(&mut fork_account.storage, &mut acc.storage);
268 }
269 fork_journaled_state.state.insert(addr, acc);
270 }
271 }
272
273 pub fn merge_db_account_data<ExtDB: DatabaseRef>(
275 addr: Address,
276 active: &CacheDB<ExtDB>,
277 fork_db: &mut ForkDB,
278 ) {
279 let mut acc = if let Some(acc) = active.cache.accounts.get(&addr).cloned() {
280 acc
281 } else {
282 return;
284 };
285
286 if let Some(code) = active.cache.contracts.get(&acc.info.code_hash).cloned() {
287 fork_db.cache.contracts.insert(acc.info.code_hash, code);
288 }
289
290 if let Some(fork_account) = fork_db.cache.accounts.get_mut(&addr) {
291 fork_account.storage.extend(std::mem::take(&mut acc.storage));
293 std::mem::swap(&mut fork_account.storage, &mut acc.storage);
295 }
296
297 fork_db.cache.accounts.insert(addr, acc);
298 }
299}