Skip to content

Commit 4e50046

Browse files
authored
feat: reintroduce backend.inspect (#802)
* reintroduce backend.inspect * fix downcast
1 parent 6234614 commit 4e50046

File tree

12 files changed

+261
-143
lines changed

12 files changed

+261
-143
lines changed

crates/evm/core/src/backend/cow.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ use alloy_rpc_types::TransactionRequest;
1414
use foundry_fork_db::DatabaseError;
1515
use revm::{
1616
db::DatabaseRef,
17-
primitives::{Account, AccountInfo, Bytecode, Env, EnvWithHandlerCfg, HashMap as Map, SpecId},
17+
primitives::{
18+
Account, AccountInfo, Bytecode, Env, EnvWithHandlerCfg, HashMap as Map, ResultAndState,
19+
SpecId,
20+
},
1821
Database, DatabaseCommit, JournaledState,
1922
};
20-
use std::{borrow::Cow, collections::BTreeMap};
23+
use std::{any::Any, borrow::Cow, collections::BTreeMap};
2124

2225
/// A wrapper around `Backend` that ensures only `revm::DatabaseRef` functions are called.
2326
///
@@ -53,6 +56,30 @@ impl<'a> CowBackend<'a> {
5356
Self { backend: Cow::Borrowed(backend), is_initialized: false, spec_id: SpecId::LATEST }
5457
}
5558

59+
/// Executes the configured transaction of the `env` without committing state changes
60+
///
61+
/// Note: in case there are any cheatcodes executed that modify the environment, this will
62+
/// update the given `env` with the new values.
63+
#[instrument(name = "inspect", level = "debug", skip_all)]
64+
pub fn inspect<I: InspectorExt>(
65+
&mut self,
66+
env: &mut EnvWithHandlerCfg,
67+
inspector: &mut I,
68+
inspect_ctx: Box<dyn Any>,
69+
) -> eyre::Result<ResultAndState> {
70+
// this is a new call to inspect with a new env, so even if we've cloned the backend
71+
// already, we reset the initialized state
72+
self.is_initialized = false;
73+
self.spec_id = env.handler_cfg.spec_id;
74+
75+
self.backend.strategy.runner.clone().inspect(
76+
self.backend.to_mut(),
77+
env,
78+
inspector,
79+
inspect_ctx,
80+
)
81+
}
82+
5683
pub fn new_borrowed(backend: &'a Backend) -> Self {
5784
Self { backend: Cow::Borrowed(backend), is_initialized: false, spec_id: SpecId::LATEST }
5885
}

crates/evm/core/src/backend/mod.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ use revm::{
2222
precompile::{PrecompileSpecId, Precompiles},
2323
primitives::{
2424
Account, AccountInfo, BlobExcessGasAndPrice, Bytecode, Env, EnvWithHandlerCfg, EvmState,
25-
EvmStorageSlot, HashMap as Map, Log, SpecId, KECCAK_EMPTY,
25+
EvmStorageSlot, HashMap as Map, Log, ResultAndState, SpecId, KECCAK_EMPTY,
2626
},
2727
Database, DatabaseCommit, JournaledState,
2828
};
2929
use std::{
30+
any::Any,
3031
collections::{BTreeMap, HashSet},
3132
time::Instant,
3233
};
@@ -773,11 +774,26 @@ impl Backend {
773774
/// Initializes settings we need to keep track of.
774775
///
775776
/// We need to track these mainly to prevent issues when switching between different evms
776-
pub fn initialize(&mut self, env: &EnvWithHandlerCfg) {
777+
pub(crate) fn initialize(&mut self, env: &EnvWithHandlerCfg) {
777778
self.set_caller(env.tx.caller);
778779
self.set_spec_id(env.handler_cfg.spec_id);
779780
}
780781

782+
/// Executes the configured test call of the `env` without committing state changes.
783+
///
784+
/// Note: in case there are any cheatcodes executed that modify the environment, this will
785+
/// update the given `env` with the new values.
786+
#[instrument(name = "inspect", level = "debug", skip_all)]
787+
pub fn inspect<I: InspectorExt>(
788+
&mut self,
789+
env: &mut EnvWithHandlerCfg,
790+
inspector: &mut I,
791+
inspect_ctx: Box<dyn Any>,
792+
) -> eyre::Result<ResultAndState> {
793+
self.initialize(env);
794+
self.strategy.runner.clone().inspect(self, env, inspector, inspect_ctx)
795+
}
796+
781797
/// Returns the `EnvWithHandlerCfg` with the current `spec_id` set.
782798
fn env_with_handler_cfg(&self, env: Env) -> EnvWithHandlerCfg {
783799
EnvWithHandlerCfg::new_with_spec_id(Box::new(env), self.inner.spec_id)

crates/evm/core/src/backend/strategy.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
use std::{any::Any, fmt::Debug};
22

3-
use super::{BackendInner, Fork, ForkDB, ForkType, FoundryEvmInMemoryDB};
3+
use crate::InspectorExt;
4+
5+
use super::{Backend, BackendInner, Fork, ForkDB, ForkType, FoundryEvmInMemoryDB};
46
use alloy_primitives::{Address, U256};
5-
use revm::{db::CacheDB, primitives::HashSet, DatabaseRef, JournaledState};
7+
use eyre::{Context, Result};
8+
use revm::{
9+
db::CacheDB,
10+
primitives::{EnvWithHandlerCfg, HashSet, ResultAndState},
11+
DatabaseRef, JournaledState,
12+
};
613
use serde::{Deserialize, Serialize};
714

815
pub struct BackendStrategyForkInfo<'a> {
@@ -62,6 +69,14 @@ pub trait BackendStrategyRunner: Debug + Send + Sync + BackendStrategyRunnerExt
6269

6370
fn new_cloned(&self) -> Box<dyn BackendStrategyRunner>;
6471

72+
fn inspect(
73+
&self,
74+
backend: &mut Backend,
75+
env: &mut EnvWithHandlerCfg,
76+
inspector: &mut dyn InspectorExt,
77+
inspect_ctx: Box<dyn Any>,
78+
) -> Result<ResultAndState>;
79+
6580
/// When creating or switching forks, we update the AccountInfo of the contract
6681
fn update_fork_db(
6782
&self,
@@ -121,6 +136,22 @@ impl BackendStrategyRunner for EvmBackendStrategyRunner {
121136
Box::new(self.clone())
122137
}
123138

139+
fn inspect(
140+
&self,
141+
backend: &mut Backend,
142+
env: &mut EnvWithHandlerCfg,
143+
inspector: &mut dyn InspectorExt,
144+
_inspect_ctx: Box<dyn Any>,
145+
) -> Result<ResultAndState> {
146+
let mut evm = crate::utils::new_evm_with_inspector(backend, env.clone(), inspector);
147+
148+
let res = evm.transact().wrap_err("backend: failed while inspecting")?;
149+
150+
env.env = evm.context.evm.inner.env;
151+
152+
Ok(res)
153+
}
154+
124155
fn update_fork_db(
125156
&self,
126157
_ctx: &mut dyn BackendStrategyContext,

crates/evm/evm/src/executors/mod.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use alloy_primitives::{
1515
map::{AddressHashMap, HashMap},
1616
Address, Bytes, Log, U256,
1717
};
18-
use alloy_serde::OtherFields;
1918
use alloy_sol_types::{sol, SolCall};
2019
use foundry_evm_core::{
2120
backend::{Backend, BackendError, BackendResult, CowBackend, DatabaseExt, GLOBAL_FAIL_SLOT},
@@ -93,7 +92,7 @@ pub struct Executor {
9392
/// Whether `failed()` should be called on the test contract to determine if the test failed.
9493
legacy_assertions: bool,
9594

96-
strategy: ExecutorStrategy,
95+
pub strategy: ExecutorStrategy,
9796
}
9897

9998
impl Clone for Executor {
@@ -258,11 +257,6 @@ impl Executor {
258257
self
259258
}
260259

261-
#[inline]
262-
pub fn set_transaction_other_fields(&mut self, other_fields: OtherFields) {
263-
self.strategy.runner.set_inspect_context(self.strategy.context.as_mut(), other_fields);
264-
}
265-
266260
/// Deploys a contract and commits the new state to the underlying database.
267261
///
268262
/// Executes a CREATE transaction with the contract `code` and persistent database state
@@ -438,15 +432,12 @@ impl Executor {
438432
pub fn call_with_env(&self, mut env: EnvWithHandlerCfg) -> eyre::Result<RawCallResult> {
439433
let mut inspector = self.inspector().clone();
440434
let mut backend = CowBackend::new_borrowed(self.backend());
441-
// this is a new call to inspect with a new env, so even if we've cloned the backend
442-
// already, we reset the initialized state
443-
backend.is_initialized = false;
444-
backend.spec_id = env.spec_id();
445435

446-
let result = self.strategy.runner.call_inspect(
436+
let result = self.strategy.runner.call(
447437
self.strategy.context.as_ref(),
448438
&mut backend,
449439
&mut env,
440+
&self.env,
450441
&mut inspector,
451442
)?;
452443

@@ -463,9 +454,8 @@ impl Executor {
463454
pub fn transact_with_env(&mut self, mut env: EnvWithHandlerCfg) -> eyre::Result<RawCallResult> {
464455
let mut inspector = self.inspector.clone();
465456
let backend = &mut self.backend;
466-
backend.initialize(&env);
467457

468-
let result_and_state = self.strategy.runner.transact_inspect(
458+
let result_and_state = self.strategy.runner.transact(
469459
self.strategy.context.as_mut(),
470460
backend,
471461
&mut env,

crates/evm/evm/src/executors/strategy.rs

Lines changed: 51 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,19 @@ use std::{any::Any, fmt::Debug};
22

33
use alloy_primitives::{Address, U256};
44
use alloy_serde::OtherFields;
5-
use eyre::{Context, Result};
5+
use eyre::Result;
66
use foundry_cheatcodes::strategy::{
77
CheatcodeInspectorStrategy, EvmCheatcodeInspectorStrategyRunner,
88
};
9-
use foundry_evm_core::{
10-
backend::{strategy::BackendStrategy, BackendResult, DatabaseExt},
11-
InspectorExt,
12-
};
9+
use foundry_evm_core::backend::{strategy::BackendStrategy, Backend, BackendResult, CowBackend};
1310
use foundry_zksync_compiler::DualCompiledContracts;
1411
use revm::{
1512
primitives::{Env, EnvWithHandlerCfg, ResultAndState},
1613
DatabaseRef,
1714
};
1815

16+
use crate::inspectors::InspectorStack;
17+
1918
use super::Executor;
2019

2120
pub trait ExecutorStrategyContext: Debug + Send + Sync + Any {
@@ -76,24 +75,25 @@ pub trait ExecutorStrategyRunner: Debug + Send + Sync + ExecutorStrategyExt {
7675
fn set_nonce(&self, executor: &mut Executor, address: Address, nonce: u64)
7776
-> BackendResult<()>;
7877

79-
fn set_inspect_context(&self, ctx: &mut dyn ExecutorStrategyContext, other_fields: OtherFields);
80-
81-
fn call_inspect(
78+
/// Execute a transaction and *WITHOUT* applying state changes.
79+
fn call(
8280
&self,
8381
ctx: &dyn ExecutorStrategyContext,
84-
db: &mut dyn DatabaseExt,
82+
backend: &mut CowBackend<'_>,
8583
env: &mut EnvWithHandlerCfg,
86-
inspector: &mut dyn InspectorExt,
87-
) -> eyre::Result<ResultAndState>;
84+
executor_env: &EnvWithHandlerCfg,
85+
inspector: &mut InspectorStack,
86+
) -> Result<ResultAndState>;
8887

89-
fn transact_inspect(
88+
/// Execute a transaction and apply state changes.
89+
fn transact(
9090
&self,
9191
ctx: &mut dyn ExecutorStrategyContext,
92-
db: &mut dyn DatabaseExt,
92+
backend: &mut Backend,
9393
env: &mut EnvWithHandlerCfg,
94-
_executor_env: &EnvWithHandlerCfg,
95-
inspector: &mut dyn InspectorExt,
96-
) -> eyre::Result<ResultAndState>;
94+
executor_env: &EnvWithHandlerCfg,
95+
inspector: &mut InspectorStack,
96+
) -> Result<ResultAndState>;
9797

9898
fn new_backend_strategy(&self) -> BackendStrategy;
9999
fn new_cheatcode_inspector_strategy(
@@ -123,6 +123,19 @@ pub trait ExecutorStrategyExt {
123123
) -> Result<()> {
124124
Ok(())
125125
}
126+
127+
/// Sets the transaction context for the next [ExecutorStrategyRunner::call] or
128+
/// [ExecutorStrategyRunner::transact]. This selects whether to run the transaction on zkEVM
129+
/// or the EVM.
130+
/// This is based if the [OtherFields] contains
131+
/// [foundry_zksync_core::ZKSYNC_TRANSACTION_OTHER_FIELDS_KEY] with
132+
/// [foundry_zksync_core::ZkTransactionMetadata].
133+
fn zksync_set_transaction_context(
134+
&self,
135+
_ctx: &mut dyn ExecutorStrategyContext,
136+
_other_fields: OtherFields,
137+
) {
138+
}
126139
}
127140

128141
/// Implements [ExecutorStrategyRunner] for EVM.
@@ -138,55 +151,6 @@ impl ExecutorStrategyRunner for EvmExecutorStrategyRunner {
138151
Box::new(self.clone())
139152
}
140153

141-
fn set_inspect_context(
142-
&self,
143-
_ctx: &mut dyn ExecutorStrategyContext,
144-
_other_fields: OtherFields,
145-
) {
146-
}
147-
148-
/// Executes the configured test call of the `env` without committing state changes.
149-
///
150-
/// Note: in case there are any cheatcodes executed that modify the environment, this will
151-
/// update the given `env` with the new values.
152-
fn call_inspect(
153-
&self,
154-
_ctx: &dyn ExecutorStrategyContext,
155-
db: &mut dyn DatabaseExt,
156-
env: &mut EnvWithHandlerCfg,
157-
inspector: &mut dyn InspectorExt,
158-
) -> eyre::Result<ResultAndState> {
159-
let mut evm = crate::utils::new_evm_with_inspector(db, env.clone(), inspector);
160-
161-
let res = evm.transact().wrap_err("backend: failed while inspecting")?;
162-
163-
env.env = evm.context.evm.inner.env;
164-
165-
Ok(res)
166-
}
167-
168-
/// Executes the configured test call of the `env` without committing state changes.
169-
/// Modifications to the state are however allowed.
170-
///
171-
/// Note: in case there are any cheatcodes executed that modify the environment, this will
172-
/// update the given `env` with the new values.
173-
fn transact_inspect(
174-
&self,
175-
_ctx: &mut dyn ExecutorStrategyContext,
176-
db: &mut dyn DatabaseExt,
177-
env: &mut EnvWithHandlerCfg,
178-
_executor_env: &EnvWithHandlerCfg,
179-
inspector: &mut dyn InspectorExt,
180-
) -> eyre::Result<ResultAndState> {
181-
let mut evm = crate::utils::new_evm_with_inspector(db, env.clone(), inspector);
182-
183-
let res = evm.transact().wrap_err("backend: failed while inspecting")?;
184-
185-
env.env = evm.context.evm.inner.env;
186-
187-
Ok(res)
188-
}
189-
190154
fn set_balance(
191155
&self,
192156
executor: &mut Executor,
@@ -214,6 +178,28 @@ impl ExecutorStrategyRunner for EvmExecutorStrategyRunner {
214178
Ok(())
215179
}
216180

181+
fn call(
182+
&self,
183+
_ctx: &dyn ExecutorStrategyContext,
184+
backend: &mut CowBackend<'_>,
185+
env: &mut EnvWithHandlerCfg,
186+
_executor_env: &EnvWithHandlerCfg,
187+
inspector: &mut InspectorStack,
188+
) -> Result<ResultAndState> {
189+
backend.inspect(env, inspector, Box::new(()))
190+
}
191+
192+
fn transact(
193+
&self,
194+
_ctx: &mut dyn ExecutorStrategyContext,
195+
backend: &mut Backend,
196+
env: &mut EnvWithHandlerCfg,
197+
_executor_env: &EnvWithHandlerCfg,
198+
inspector: &mut InspectorStack,
199+
) -> Result<ResultAndState> {
200+
backend.inspect(env, inspector, Box::new(()))
201+
}
202+
217203
fn new_backend_strategy(&self) -> BackendStrategy {
218204
BackendStrategy::new_evm()
219205
}

crates/script/src/broadcast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub async fn send_transaction(
7373
) -> Result<TxHash> {
7474
let zk_tx_meta =
7575
if let SendTransactionKind::Raw(tx, _) | SendTransactionKind::Unlocked(tx) = &mut kind {
76-
foundry_strategy_zksync::get_zksync_transaction_metadata(&tx.other)
76+
foundry_strategy_zksync::try_get_zksync_transaction_metadata(&tx.other)
7777
} else {
7878
None
7979
};

crates/script/src/runner.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,10 @@ impl ScriptRunner {
257257
other_fields: Option<OtherFields>,
258258
) -> Result<ScriptResult> {
259259
if let Some(other_fields) = other_fields {
260-
self.executor.set_transaction_other_fields(other_fields);
260+
self.executor.strategy.runner.zksync_set_transaction_context(
261+
self.executor.strategy.context.as_mut(),
262+
other_fields,
263+
);
261264
}
262265

263266
if let Some(to) = to {

0 commit comments

Comments
 (0)