From 02d1a406805da1e2948d9ee8d3060c1b0c68c78f Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 17 Jun 2024 06:46:13 +0300 Subject: [PATCH 01/26] wip --- crates/cheatcodes/spec/src/vm.rs | 4 + crates/cheatcodes/src/evm/fork.rs | 2 + crates/cheatcodes/src/fs.rs | 30 ++- crates/cheatcodes/src/inspector.rs | 80 +++++-- crates/cheatcodes/src/lib.rs | 43 +++- crates/cheatcodes/src/script.rs | 2 +- crates/evm/core/src/backend/mod.rs | 2 +- crates/evm/core/src/utils.rs | 21 +- crates/evm/evm/src/inspectors/stack.rs | 308 ++++++++++++++++++++++--- 9 files changed, 428 insertions(+), 64 deletions(-) diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 201bc90d7bf4b..a47813ead88b4 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1474,6 +1474,10 @@ interface Vm { #[cheatcode(group = Filesystem)] function getCode(string calldata artifactPath) external view returns (bytes memory creationBytecode); + /// Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the + #[cheatcode(group = Filesystem)] + function deployCode(string calldata artifactPath) external returns (address deployedAddress); + /// Gets the deployed bytecode from an artifact file. Takes in the relative path to the json file or the path to the /// artifact in the form of :: where and parts are optional. #[cheatcode(group = Filesystem)] diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index a767520288f21..bb3ef43e2e29f 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -129,6 +129,7 @@ impl Cheatcode for selectForkCall { impl Cheatcode for transact_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { txHash } = *self; + #[cfg(ignore)] ccx.ecx.db.transact( None, txHash, @@ -143,6 +144,7 @@ impl Cheatcode for transact_0Call { impl Cheatcode for transact_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, txHash } = *self; + #[cfg(ignore)] ccx.ecx.db.transact( Some(forkId), txHash, diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index e4ee12513e4e4..0822452693108 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -1,7 +1,7 @@ //! Implementations of [`Filesystem`](spec::Group::Filesystem) cheatcodes. use super::string::parse; -use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*}; use alloy_dyn_abi::DynSolType; use alloy_json_abi::ContractObject; use alloy_primitives::{Bytes, U256}; @@ -9,6 +9,8 @@ use alloy_sol_types::SolValue; use dialoguer::{Input, Password}; use foundry_common::fs; use foundry_config::fs_permissions::FsAccessKind; +use foundry_evm_core::backend::DatabaseExt; +use revm::interpreter::CreateInputs; use semver::Version; use std::{ collections::hash_map::Entry, @@ -262,6 +264,32 @@ impl Cheatcode for getDeployedCodeCall { } } +impl Cheatcode for deployCodeCall { + fn apply_full_with_executor( + &self, + ccx: &mut CheatsCtxt, + executor: &mut E, + ) -> Result { + let Self { artifactPath: path } = self; + let bytecode = get_artifact_code(&ccx.state, path, false)?; + let output = executor + .exec_create( + CreateInputs { + caller: ccx.caller, + scheme: revm::primitives::CreateScheme::Create, + value: U256::ZERO, + init_code: bytecode, + gas_limit: ccx.gas_limit, + }, + &mut ccx.state, + &mut ccx.ecx, + ) + .unwrap(); + + Ok(output.address.unwrap().abi_encode()) + } +} + /// Returns the path to the json artifact depending on the input /// /// Can parse following input formats: diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 449ebdaae85a9..8f33537407e09 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -32,8 +32,8 @@ use revm::{ opcode, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, - primitives::{BlockEnv, CreateScheme, TransactTo}, - EvmContext, InnerEvmContext, Inspector, + primitives::{BlockEnv, CreateScheme, EVMError, TransactTo}, + ContextPrecompiles, EvmContext, InnerEvmContext, Inspector, }; use rustc_hash::FxHashMap; use serde_json::Value; @@ -46,6 +46,15 @@ use std::{ sync::Arc, }; +pub trait CheatcodesExecutor { + fn exec_create( + &mut self, + inputs: CreateInputs, + cheats: &mut Cheatcodes, + ecx: &mut InnerEvmContext<&mut DB>, + ) -> Result>; +} + macro_rules! try_or_continue { ($e:expr) => { match $e { @@ -254,10 +263,11 @@ impl Cheatcodes { self.config.script_wallets.as_ref() } - fn apply_cheatcode( + fn apply_cheatcode( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut DB>, call: &CallInputs, + executor: &mut E, ) -> Result { // decode the cheatcode call let decoded = Vm::VmCalls::abi_decode(&call.input, false).map_err(|e| { @@ -283,8 +293,10 @@ impl Cheatcodes { state: self, ecx: &mut ecx.inner, precompiles: &mut ecx.precompiles, + gas_limit: call.gas_limit, caller, }, + executor, ) } @@ -321,7 +333,7 @@ impl Cheatcodes { /// /// Cleanup any previously applied cheatcodes that altered the state in such a way that revm's /// revert would run into issues. - pub fn on_revert(&mut self, ecx: &mut EvmContext) { + pub fn on_revert(&mut self, ecx: &mut EvmContext<&mut DB>) { trace!(deals=?self.eth_deals.len(), "rolling back deals"); // Delay revert clean up until expected revert is handled, if set. @@ -345,9 +357,13 @@ impl Cheatcodes { } } -impl Inspector for Cheatcodes { +impl Cheatcodes { #[inline] - fn initialize_interp(&mut self, _: &mut Interpreter, ecx: &mut EvmContext) { + pub fn initialize_interp( + &mut self, + _: &mut Interpreter, + ecx: &mut EvmContext<&mut DB>, + ) { // When the first interpreter is initialized we've circumvented the balance and gas checks, // so we apply our actual block data with the correct fees and all. if let Some(block) = self.block.take() { @@ -358,7 +374,11 @@ impl Inspector for Cheatcodes { } } - fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + pub fn step( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut EvmContext<&mut DB>, + ) { let ecx = &mut ecx.inner; self.pc = interpreter.program_counter(); @@ -781,7 +801,14 @@ impl Inspector for Cheatcodes { } } - fn log(&mut self, _context: &mut EvmContext, log: &Log) { + pub fn step_end( + &mut self, + _interpreter: &mut Interpreter, + _context: &mut EvmContext, + ) { + } + + pub fn log(&mut self, _context: &mut EvmContext, log: &Log) { if !self.expected_emits.is_empty() { expect::handle_expect_emit(self, log); } @@ -796,7 +823,12 @@ impl Inspector for Cheatcodes { } } - fn call(&mut self, ecx: &mut EvmContext, call: &mut CallInputs) -> Option { + pub fn call( + &mut self, + ecx: &mut EvmContext<&mut DB>, + call: &mut CallInputs, + executor: &mut impl CheatcodesExecutor, + ) -> Option { let gas = Gas::new(call.gas_limit); // At the root call to test function or script `run()`/`setUp()` functions, we are @@ -826,7 +858,7 @@ impl Inspector for Cheatcodes { } if call.target_address == CHEATCODE_ADDRESS { - return match self.apply_cheatcode(ecx, call) { + return match self.apply_cheatcode(ecx, call, executor) { Ok(retdata) => Some(CallOutcome { result: InterpreterResult { result: InstructionResult::Return, @@ -1051,9 +1083,9 @@ impl Inspector for Cheatcodes { None } - fn call_end( + pub fn call_end( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut DB>, call: &CallInputs, mut outcome: CallOutcome, ) -> CallOutcome { @@ -1333,9 +1365,9 @@ impl Inspector for Cheatcodes { outcome } - fn create( + pub fn create( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut DB>, call: &mut CreateInputs, ) -> Option { let ecx = &mut ecx.inner; @@ -1439,9 +1471,9 @@ impl Inspector for Cheatcodes { None } - fn create_end( + pub fn create_end( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut DB>, _call: &CreateInputs, mut outcome: CreateOutcome, ) -> CreateOutcome { @@ -1549,12 +1581,10 @@ impl Inspector for Cheatcodes { outcome } -} -impl InspectorExt for Cheatcodes { - fn should_use_create2_factory( + pub fn should_use_create2_factory( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut DB>, inputs: &mut CreateInputs, ) -> bool { if let CreateScheme::Create2 { .. } = inputs.scheme { @@ -1620,11 +1650,15 @@ fn check_if_fixed_gas_limit( } /// Dispatches the cheatcode call to the appropriate function. -fn apply_dispatch(calls: &Vm::VmCalls, ccx: &mut CheatsCtxt) -> Result { +fn apply_dispatch( + calls: &Vm::VmCalls, + ccx: &mut CheatsCtxt, + executor: &mut E, +) -> Result { macro_rules! match_ { ($($variant:ident),*) => { match calls { - $(Vm::VmCalls::$variant(cheat) => crate::Cheatcode::apply_traced(cheat, ccx),)* + $(Vm::VmCalls::$variant(cheat) => crate::Cheatcode::apply_traced(cheat, ccx, executor),)* } }; } diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index be13f0f8ede88..5f0ba8a10ff75 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -11,13 +11,15 @@ pub extern crate foundry_cheatcodes_spec as spec; #[macro_use] extern crate tracing; -use alloy_primitives::Address; +use alloy_primitives::{Address, U256}; use foundry_evm_core::backend::DatabaseExt; use revm::{ContextPrecompiles, InnerEvmContext}; pub use config::CheatsConfig; pub use error::{Error, ErrorKind, Result}; -pub use inspector::{BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, Context}; +pub use inspector::{ + BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, CheatcodesExecutor, Context, +}; pub use spec::{CheatcodeDef, Vm}; pub use Vm::ForgeContext; @@ -69,10 +71,25 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { self.apply(ccx.state) } + /// Applies this cheatcode to the given context and executor. + /// + /// Implement this function if you need access to the executor. + fn apply_full_with_executor( + &self, + ccx: &mut CheatsCtxt, + _executor: &mut E, + ) -> Result { + self.apply_full(ccx) + } + #[inline] - fn apply_traced(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_traced( + &self, + ccx: &mut CheatsCtxt, + executor: &mut E, + ) -> Result { let _span = trace_span_and_call(self); - let result = self.apply_full(ccx); + let result = self.apply_full_with_executor(ccx, executor); trace_return(&result); return result; @@ -121,19 +138,21 @@ impl DynCheatcode for T { } /// The cheatcode context, used in [`Cheatcode`]. -pub(crate) struct CheatsCtxt<'cheats, 'evm, DB: DatabaseExt> { +pub(crate) struct CheatsCtxt<'cheats, 'evm, 'db, DB: DatabaseExt> { /// The cheatcodes inspector state. pub(crate) state: &'cheats mut Cheatcodes, /// The EVM data. - pub(crate) ecx: &'evm mut InnerEvmContext, + pub(crate) ecx: &'evm mut InnerEvmContext<&'db mut DB>, /// The precompiles context. - pub(crate) precompiles: &'evm mut ContextPrecompiles, + pub(crate) precompiles: &'evm mut ContextPrecompiles<&'db mut DB>, /// The original `msg.sender`. pub(crate) caller: Address, + /// Gas limit of the current cheatcode call. + pub(crate) gas_limit: u64, } -impl<'cheats, 'evm, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'evm, DB> { - type Target = InnerEvmContext; +impl<'cheats, 'evm, 'db, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'evm, 'db, DB> { + type Target = InnerEvmContext<&'db mut DB>; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -141,14 +160,16 @@ impl<'cheats, 'evm, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'ev } } -impl<'cheats, 'evm, DB: DatabaseExt> std::ops::DerefMut for CheatsCtxt<'cheats, 'evm, DB> { +impl<'cheats, 'evm, 'db, DB: DatabaseExt> std::ops::DerefMut + for CheatsCtxt<'cheats, 'evm, 'db, DB> +{ #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.ecx } } -impl<'cheats, 'evm, DB: DatabaseExt> CheatsCtxt<'cheats, 'evm, DB> { +impl<'cheats, 'evm, 'db, DB: DatabaseExt> CheatsCtxt<'cheats, 'evm, 'db, DB> { #[inline] pub(crate) fn is_precompile(&self, address: &Address) -> bool { self.precompiles.contains_key(address) diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 97c65106a1605..0252c85b61560 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -1,6 +1,6 @@ //! Implementations of [`Scripting`](spec::Group::Scripting) cheatcodes. -use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*}; +use crate::{Cheatcode, CheatcodesExecutor, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{Address, B256, U256}; use alloy_signer_wallet::LocalWallet; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index b96829196a99d..b72f0caed40c0 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -65,7 +65,7 @@ const GLOBAL_FAILURE_SLOT: B256 = /// An extension trait that allows us to easily extend the `revm::Inspector` capabilities #[auto_impl::auto_impl(&mut)] -pub trait DatabaseExt: Database { +pub trait DatabaseExt: Database + DatabaseCommit { /// Creates a new snapshot at the current point of execution. /// /// A snapshot is associated with a new unique id that's created for the snapshot. diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index d8114f209e81e..67e925ed355da 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -11,7 +11,7 @@ use revm::{ return_ok, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, Gas, InstructionResult, InterpreterResult, }, - primitives::{CreateScheme, EVMError, SpecId, TransactTo, KECCAK_EMPTY}, + primitives::{CreateScheme, EVMError, HandlerCfg, SpecId, TransactTo, KECCAK_EMPTY}, FrameOrResult, FrameResult, }; use std::{cell::RefCell, rc::Rc, sync::Arc}; @@ -268,6 +268,25 @@ where new_evm_with_inspector(WrapDatabaseRef(db), env, inspector) } +pub fn new_evm_with_existing_context<'a, DB, I>( + inner: revm::InnerEvmContext, + inspector: I, +) -> revm::Evm<'a, I, DB> +where + DB: revm::Database, + I: InspectorExt, +{ + let handler_cfg = HandlerCfg::new(inner.spec_id()); + let context = revm::Context::new( + revm::EvmContext { inner, precompiles: Default::default() }, + inspector, + ); + let mut handler = revm::Handler::new(handler_cfg); + handler.append_handler_register_plain(revm::inspector_handle_register); + handler.append_handler_register_plain(create2_handler_register); + revm::Evm::new(context, handler) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 52949e8e194ea..e99efb48e7b16 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -3,6 +3,7 @@ use super::{ StackSnapshotType, TracingInspector, TracingInspectorConfig, }; use alloy_primitives::{Address, Bytes, Log, U256}; +use foundry_cheatcodes::CheatcodesExecutor; use foundry_evm_core::{ backend::{update_state, DatabaseExt}, debug::DebugArena, @@ -16,8 +17,12 @@ use revm::{ CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult, Interpreter, InterpreterResult, }, - primitives::{BlockEnv, Env, EnvWithHandlerCfg, ExecutionResult, Output, TransactTo}, - DatabaseCommit, EvmContext, Inspector, + primitives::{ + BlockEnv, EVMError, Env, EnvWithHandlerCfg, ExecutionResult, HandlerCfg, Output, + TransactTo, TxEnv, + }, + ContextPrecompiles, DatabaseCommit, EvmContext, FrameOrResult, FrameResult, InnerEvmContext, + Inspector, JournaledState, }; use std::{collections::HashMap, sync::Arc}; @@ -292,6 +297,141 @@ pub struct InspectorStack { pub inner_context_data: Option, } +struct InspectorStackRefMut<'a> { + pub cheatcodes: Option<&'a mut Cheatcodes>, + pub chisel_state: Option<&'a mut ChiselState>, + pub coverage: Option<&'a mut CoverageCollector>, + pub debugger: Option<&'a mut Debugger>, + pub fuzzer: Option<&'a mut Fuzzer>, + pub log_collector: Option<&'a mut LogCollector>, + pub printer: Option<&'a mut CustomPrintTracer>, + pub tracer: Option<&'a mut TracingInspector>, + + pub enable_isolation: bool, + pub in_inner_context: bool, + pub inner_context_data: &'a mut Option, +} + +impl<'a> From<&'a mut InspectorStack> for InspectorStackRefMut<'a> { + fn from(stack: &'a mut InspectorStack) -> Self { + Self { + cheatcodes: stack.cheatcodes.as_mut(), + chisel_state: stack.chisel_state.as_mut(), + coverage: stack.coverage.as_mut(), + debugger: stack.debugger.as_mut(), + fuzzer: stack.fuzzer.as_mut(), + log_collector: stack.log_collector.as_mut(), + printer: stack.printer.as_mut(), + tracer: stack.tracer.as_mut(), + + enable_isolation: stack.enable_isolation, + in_inner_context: stack.in_inner_context, + inner_context_data: &mut stack.inner_context_data, + } + } +} + +struct CheatsInspectors<'a> { + pub chisel_state: Option<&'a mut ChiselState>, + pub coverage: Option<&'a mut CoverageCollector>, + pub debugger: Option<&'a mut Debugger>, + pub fuzzer: Option<&'a mut Fuzzer>, + pub log_collector: Option<&'a mut LogCollector>, + pub printer: Option<&'a mut CustomPrintTracer>, + pub tracer: Option<&'a mut TracingInspector>, + + pub enable_isolation: bool, + pub in_inner_context: bool, + pub inner_context_data: &'a mut Option, +} + +impl<'a> CheatsInspectors<'a> { + fn with_inspector_ref<'life1, 'life2, O>( + &'life2 mut self, + cheatcodes: &'life1 mut Cheatcodes, + f: impl FnOnce(&mut InspectorStackRefMut<'life1>) -> O, + ) -> O + where + 'a: 'life1, + 'life2: 'life1, + { + let mut stack = InspectorStackRefMut { + cheatcodes: Some(cheatcodes), + chisel_state: self.chisel_state.as_mut().map(|t| &mut **t), + coverage: self.coverage.as_mut().map(|t| &mut **t), + debugger: self.debugger.as_mut().map(|t| &mut **t), + fuzzer: self.fuzzer.as_mut().map(|t| &mut **t), + log_collector: self.log_collector.as_mut().map(|t| &mut **t), + printer: self.printer.as_mut().map(|t| &mut **t), + tracer: self.tracer.as_mut().map(|t| &mut **t), + + enable_isolation: self.enable_isolation, + in_inner_context: self.in_inner_context, + inner_context_data: &mut *self.inner_context_data, + }; + + let output = f(&mut stack); + + output + } +} + +impl CheatcodesExecutor for CheatsInspectors<'_> { + fn exec_create( + &mut self, + inputs: CreateInputs, + cheats: &mut Cheatcodes, + ecx: &mut InnerEvmContext<&mut DB>, + ) -> Result> { + self.with_inspector_ref(cheats, move |stack| { + let error = std::mem::replace(&mut ecx.error, Ok(())); + let l1_block_info = std::mem::take(&mut ecx.l1_block_info); + + let inner = revm::InnerEvmContext { + env: ecx.env.clone(), + journaled_state: std::mem::replace( + &mut ecx.journaled_state, + JournaledState::new(Default::default(), Default::default()), + ), + db: &mut *ecx.db, + error, + l1_block_info, + }; + + let mut evm = crate::utils::new_evm_with_existing_context( + inner, + stack, + ); + + evm.context.evm.inner.journaled_state.depth += 1; + + let first_frame_or_result = + evm.handler.execution().create(&mut evm.context, Box::new(inputs))?; + + let mut result = match first_frame_or_result { + FrameOrResult::Frame(first_frame) => evm.run_the_loop(first_frame)?, + FrameOrResult::Result(result) => result, + }; + + evm.handler.execution().last_frame_return(&mut evm.context, &mut result)?; + + let outcome = match result { + FrameResult::Call(_) | FrameResult::EOFCreate(_) => unreachable!(), + FrameResult::Create(create) => create, + }; + + evm.context.evm.inner.journaled_state.depth -= 1; + + ecx.journaled_state = evm.context.evm.inner.journaled_state; + ecx.env = evm.context.evm.inner.env; + ecx.l1_block_info = evm.context.evm.inner.l1_block_info; + ecx.error = evm.context.evm.inner.error; + + Ok(outcome) + }) + } +} + impl InspectorStack { /// Creates a new inspector stack. /// @@ -407,6 +547,33 @@ impl InspectorStack { } } + fn as_stack_ref<'a>(&'a mut self) -> InspectorStackRefMut<'a> { + self.into() + } +} + +impl<'a> InspectorStackRefMut<'a> { + /// Adjusts the EVM data for the inner EVM context. + /// Should be called on the top-level call of inner context (depth == 0 && + /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility + /// Updates tx.origin to the value before entering inner context + fn adjust_evm_data_for_inner_context( + &mut self, + ecx: &mut EvmContext<&mut DB>, + ) { + let inner_context_data = + self.inner_context_data.as_ref().expect("should be called in inner context"); + let sender_acc = ecx + .journaled_state + .state + .get_mut(&inner_context_data.sender) + .expect("failed to load sender"); + if !inner_context_data.is_create { + sender_acc.info.nonce = inner_context_data.original_sender_nonce; + } + ecx.env.tx.caller = inner_context_data.original_origin; + } + fn do_call_end( &mut self, ecx: &mut EvmContext<&mut DB>, @@ -478,7 +645,7 @@ impl InspectorStack { } ecx.env.tx.gas_price = U256::ZERO; - self.inner_context_data = Some(InnerContextData { + *self.inner_context_data = Some(InnerContextData { sender: ecx.env.tx.caller, original_origin: cached_env.tx.caller, original_sender_nonce: nonce, @@ -497,7 +664,7 @@ impl InspectorStack { }; self.in_inner_context = false; - self.inner_context_data = None; + *self.inner_context_data = None; ecx.env.tx = cached_env.tx; ecx.env.block.basefee = cached_env.block.basefee; @@ -566,25 +733,22 @@ impl InspectorStack { (InterpreterResult { result, output, gas }, address) } - /// Adjusts the EVM data for the inner EVM context. - /// Should be called on the top-level call of inner context (depth == 0 && - /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility - /// Updates tx.origin to the value before entering inner context - fn adjust_evm_data_for_inner_context( - &mut self, - ecx: &mut EvmContext<&mut DB>, - ) { - let inner_context_data = - self.inner_context_data.as_ref().expect("should be called in inner context"); - let sender_acc = ecx - .journaled_state - .state - .get_mut(&inner_context_data.sender) - .expect("failed to load sender"); - if !inner_context_data.is_create { - sender_acc.info.nonce = inner_context_data.original_sender_nonce; - } - ecx.env.tx.caller = inner_context_data.original_origin; + fn as_cheats_stack(&mut self) -> (CheatsInspectors<'_>, Option<&mut Cheatcodes>) { + let cheats_stack = CheatsInspectors { + chisel_state: self.chisel_state.as_deref_mut(), + coverage: self.coverage.as_deref_mut(), + debugger: self.debugger.as_deref_mut(), + fuzzer: self.fuzzer.as_deref_mut(), + log_collector: self.log_collector.as_deref_mut(), + printer: self.printer.as_deref_mut(), + tracer: self.tracer.as_deref_mut(), + + enable_isolation: self.enable_isolation, + in_inner_context: self.in_inner_context, + inner_context_data: &mut self.inner_context_data, + }; + + (cheats_stack, self.cheatcodes.as_deref_mut()) } } @@ -593,7 +757,7 @@ impl InspectorStack { // implementation. This currently works because internally we only use `&mut DB` anyways, but if // this ever needs to be changed, this can be reverted back to using just `DB`, and instead using // dynamic dispatch (`&mut dyn ...`) in `transact_inner`. -impl Inspector<&mut DB> for InspectorStack { +impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStackRefMut<'a> { fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { call_inspectors_adjust_depth!( #[no_ret] @@ -651,13 +815,17 @@ impl Inspector<&mut DB> for InspectorStack { return None; } + println!( + "CALL {:?} {:?} depth {:?}", + call.target_address, call.input, ecx.journaled_state.depth + ); + call_inspectors_adjust_depth!( [ &mut self.fuzzer, &mut self.debugger, &mut self.tracer, &mut self.log_collector, - &mut self.cheatcodes, &mut self.printer, ], |inspector| { @@ -673,6 +841,15 @@ impl Inspector<&mut DB> for InspectorStack { ecx ); + let (mut cheats_stack, cheatcodes) = self.as_cheats_stack(); + if let Some(cheatcodes) = cheatcodes { + if let Some(output) = cheatcodes.call(ecx, call, &mut cheats_stack) { + if output.result.result != InstructionResult::Continue { + return Some(output) + } + } + } + if self.enable_isolation && call.scheme == CallScheme::Call && !self.in_inner_context && @@ -704,6 +881,11 @@ impl Inspector<&mut DB> for InspectorStack { return outcome } + println!( + "CALL END {:?} {:?} depth {:?}", + inputs.target_address, inputs.input, ecx.journaled_state.depth + ); + let outcome = self.do_call_end(ecx, inputs, outcome); if outcome.result.is_revert() { // Encountered a revert, since cheatcodes may have altered the evm state in such a way @@ -727,6 +909,8 @@ impl Inspector<&mut DB> for InspectorStack { return None; } + println!("CREATE depth {:?}", ecx.journaled_state.depth); + call_inspectors_adjust_depth!( [&mut self.debugger, &mut self.tracer, &mut self.coverage, &mut self.cheatcodes], |inspector| inspector.create(ecx, create).map(Some), @@ -761,6 +945,8 @@ impl Inspector<&mut DB> for InspectorStack { return outcome } + println!("CREATE END depth {:?}", ecx.journaled_state.depth); + let result = outcome.result.result; call_inspectors_adjust_depth!( @@ -789,7 +975,7 @@ impl Inspector<&mut DB> for InspectorStack { } } -impl InspectorExt<&mut DB> for InspectorStack { +impl<'a, DB: DatabaseExt + DatabaseCommit> InspectorExt<&mut DB> for InspectorStackRefMut<'a> { fn should_use_create2_factory( &mut self, ecx: &mut EvmContext<&mut DB>, @@ -805,3 +991,73 @@ impl InspectorExt<&mut DB> for InspectorStack false } } + +impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&'a mut DB> for InspectorStack { + fn call( + &mut self, + context: &mut EvmContext<&'a mut DB>, + inputs: &mut CallInputs, + ) -> Option { + self.as_stack_ref().call(context, inputs) + } + + fn call_end( + &mut self, + context: &mut EvmContext<&'a mut DB>, + inputs: &CallInputs, + outcome: CallOutcome, + ) -> CallOutcome { + self.as_stack_ref().call_end(context, inputs, outcome) + } + + fn create( + &mut self, + context: &mut EvmContext<&'a mut DB>, + create: &mut CreateInputs, + ) -> Option { + self.as_stack_ref().create(context, create) + } + + fn create_end( + &mut self, + context: &mut EvmContext<&'a mut DB>, + call: &CreateInputs, + outcome: CreateOutcome, + ) -> CreateOutcome { + self.as_stack_ref().create_end(context, call, outcome) + } + + fn initialize_interp( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut EvmContext<&'a mut DB>, + ) { + self.as_stack_ref().initialize_interp(interpreter, ecx) + } + + fn log(&mut self, ecx: &mut EvmContext<&'a mut DB>, log: &Log) { + self.as_stack_ref().log(ecx, log) + } + + fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { + Inspector::<&mut DB>::selfdestruct(&mut self.as_stack_ref(), contract, target, value) + } + + fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&'a mut DB>) { + self.as_stack_ref().step(interpreter, ecx) + } + + fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&'a mut DB>) { + self.as_stack_ref().step_end(interpreter, ecx) + } +} + +impl<'a, DB: DatabaseExt + DatabaseCommit> InspectorExt<&'a mut DB> for InspectorStack { + fn should_use_create2_factory( + &mut self, + ecx: &mut EvmContext<&'a mut DB>, + inputs: &mut CreateInputs, + ) -> bool { + self.as_stack_ref().should_use_create2_factory(ecx, inputs) + } +} From 4ba8ba41e9cbea33e0b0c7bac1706a015e918150 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 17 Jun 2024 19:00:37 +0300 Subject: [PATCH 02/26] wip --- crates/cheatcodes/src/evm/fork.rs | 2 - crates/evm/core/src/utils.rs | 6 +- crates/evm/evm/src/inspectors/stack.rs | 185 +++++++++---------------- 3 files changed, 71 insertions(+), 122 deletions(-) diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index bb3ef43e2e29f..a767520288f21 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -129,7 +129,6 @@ impl Cheatcode for selectForkCall { impl Cheatcode for transact_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { txHash } = *self; - #[cfg(ignore)] ccx.ecx.db.transact( None, txHash, @@ -144,7 +143,6 @@ impl Cheatcode for transact_0Call { impl Cheatcode for transact_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, txHash } = *self; - #[cfg(ignore)] ccx.ecx.db.transact( Some(forkId), txHash, diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 67e925ed355da..095eebc05b664 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -277,10 +277,8 @@ where I: InspectorExt, { let handler_cfg = HandlerCfg::new(inner.spec_id()); - let context = revm::Context::new( - revm::EvmContext { inner, precompiles: Default::default() }, - inspector, - ); + let context = + revm::Context::new(revm::EvmContext { inner, precompiles: Default::default() }, inspector); let mut handler = revm::Handler::new(handler_cfg); handler.append_handler_register_plain(revm::inspector_handle_register); handler.append_handler_register_plain(create2_handler_register); diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index e99efb48e7b16..796c46a8951b1 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -17,14 +17,15 @@ use revm::{ CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult, Interpreter, InterpreterResult, }, - primitives::{ - BlockEnv, EVMError, Env, EnvWithHandlerCfg, ExecutionResult, HandlerCfg, Output, - TransactTo, TxEnv, - }, - ContextPrecompiles, DatabaseCommit, EvmContext, FrameOrResult, FrameResult, InnerEvmContext, - Inspector, JournaledState, + primitives::{BlockEnv, EVMError, Env, EnvWithHandlerCfg, ExecutionResult, Output, TransactTo}, + DatabaseCommit, EvmContext, FrameOrResult, FrameResult, InnerEvmContext, Inspector, + JournaledState, +}; +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, + sync::Arc, }; -use std::{collections::HashMap, sync::Arc}; #[derive(Clone, Debug, Default)] #[must_use = "builders do nothing unless you call `build` on them"] @@ -283,6 +284,11 @@ pub struct InnerContextData { #[derive(Clone, Debug, Default)] pub struct InspectorStack { pub cheatcodes: Option, + pub inner: InspectorStackInner, +} + +#[derive(Default, Clone, Debug)] +pub struct InspectorStackInner { pub chisel_state: Option, pub coverage: Option, pub debugger: Option, @@ -299,84 +305,20 @@ pub struct InspectorStack { struct InspectorStackRefMut<'a> { pub cheatcodes: Option<&'a mut Cheatcodes>, - pub chisel_state: Option<&'a mut ChiselState>, - pub coverage: Option<&'a mut CoverageCollector>, - pub debugger: Option<&'a mut Debugger>, - pub fuzzer: Option<&'a mut Fuzzer>, - pub log_collector: Option<&'a mut LogCollector>, - pub printer: Option<&'a mut CustomPrintTracer>, - pub tracer: Option<&'a mut TracingInspector>, - - pub enable_isolation: bool, - pub in_inner_context: bool, - pub inner_context_data: &'a mut Option, -} - -impl<'a> From<&'a mut InspectorStack> for InspectorStackRefMut<'a> { - fn from(stack: &'a mut InspectorStack) -> Self { - Self { - cheatcodes: stack.cheatcodes.as_mut(), - chisel_state: stack.chisel_state.as_mut(), - coverage: stack.coverage.as_mut(), - debugger: stack.debugger.as_mut(), - fuzzer: stack.fuzzer.as_mut(), - log_collector: stack.log_collector.as_mut(), - printer: stack.printer.as_mut(), - tracer: stack.tracer.as_mut(), - - enable_isolation: stack.enable_isolation, - in_inner_context: stack.in_inner_context, - inner_context_data: &mut stack.inner_context_data, - } - } + pub inner: &'a mut InspectorStackInner, } -struct CheatsInspectors<'a> { - pub chisel_state: Option<&'a mut ChiselState>, - pub coverage: Option<&'a mut CoverageCollector>, - pub debugger: Option<&'a mut Debugger>, - pub fuzzer: Option<&'a mut Fuzzer>, - pub log_collector: Option<&'a mut LogCollector>, - pub printer: Option<&'a mut CustomPrintTracer>, - pub tracer: Option<&'a mut TracingInspector>, - - pub enable_isolation: bool, - pub in_inner_context: bool, - pub inner_context_data: &'a mut Option, -} - -impl<'a> CheatsInspectors<'a> { - fn with_inspector_ref<'life1, 'life2, O>( - &'life2 mut self, - cheatcodes: &'life1 mut Cheatcodes, - f: impl FnOnce(&mut InspectorStackRefMut<'life1>) -> O, - ) -> O - where - 'a: 'life1, - 'life2: 'life1, - { - let mut stack = InspectorStackRefMut { - cheatcodes: Some(cheatcodes), - chisel_state: self.chisel_state.as_mut().map(|t| &mut **t), - coverage: self.coverage.as_mut().map(|t| &mut **t), - debugger: self.debugger.as_mut().map(|t| &mut **t), - fuzzer: self.fuzzer.as_mut().map(|t| &mut **t), - log_collector: self.log_collector.as_mut().map(|t| &mut **t), - printer: self.printer.as_mut().map(|t| &mut **t), - tracer: self.tracer.as_mut().map(|t| &mut **t), - - enable_isolation: self.enable_isolation, - in_inner_context: self.in_inner_context, - inner_context_data: &mut *self.inner_context_data, - }; - - let output = f(&mut stack); - - output +impl InspectorStackInner { + fn with_inspector_ref( + &mut self, + cheatcodes: &mut Cheatcodes, + f: impl FnOnce(&mut InspectorStackRefMut<'_>) -> O, + ) -> O { + f(&mut InspectorStackRefMut { cheatcodes: Some(cheatcodes), inner: self }) } } -impl CheatcodesExecutor for CheatsInspectors<'_> { +impl CheatcodesExecutor for InspectorStackInner { fn exec_create( &mut self, inputs: CreateInputs, @@ -398,10 +340,7 @@ impl CheatcodesExecutor for CheatsInspectors<'_> { l1_block_info, }; - let mut evm = crate::utils::new_evm_with_existing_context( - inner, - stack, - ); + let mut evm = crate::utils::new_evm_with_existing_context(inner, stack); evm.context.evm.inner.journaled_state.depth += 1; @@ -532,23 +471,28 @@ impl InspectorStack { /// Collects all the data gathered during inspection into a single struct. #[inline] pub fn collect(self) -> InspectorData { + let Self { + cheatcodes, + inner: + InspectorStackInner { chisel_state, coverage, debugger, log_collector, tracer, .. }, + } = self; + InspectorData { - logs: self.log_collector.map(|logs| logs.logs).unwrap_or_default(), - labels: self - .cheatcodes + logs: log_collector.map(|logs| logs.logs).unwrap_or_default(), + labels: cheatcodes .as_ref() .map(|cheatcodes| cheatcodes.labels.clone()) .unwrap_or_default(), - traces: self.tracer.map(|tracer| tracer.get_traces().clone()), - debug: self.debugger.map(|debugger| debugger.arena), - coverage: self.coverage.map(|coverage| coverage.maps), - cheatcodes: self.cheatcodes, - chisel_state: self.chisel_state.and_then(|state| state.state), + traces: tracer.map(|tracer| tracer.get_traces().clone()), + debug: debugger.map(|debugger| debugger.arena), + coverage: coverage.map(|coverage| coverage.maps), + cheatcodes, + chisel_state: chisel_state.and_then(|state| state.state), } } fn as_stack_ref<'a>(&'a mut self) -> InspectorStackRefMut<'a> { - self.into() + InspectorStackRefMut { cheatcodes: self.cheatcodes.as_mut(), inner: &mut self.inner } } } @@ -645,7 +589,7 @@ impl<'a> InspectorStackRefMut<'a> { } ecx.env.tx.gas_price = U256::ZERO; - *self.inner_context_data = Some(InnerContextData { + self.inner_context_data = Some(InnerContextData { sender: ecx.env.tx.caller, original_origin: cached_env.tx.caller, original_sender_nonce: nonce, @@ -664,7 +608,7 @@ impl<'a> InspectorStackRefMut<'a> { }; self.in_inner_context = false; - *self.inner_context_data = None; + self.inner_context_data = None; ecx.env.tx = cached_env.tx; ecx.env.block.basefee = cached_env.block.basefee; @@ -732,24 +676,6 @@ impl<'a> InspectorStackRefMut<'a> { }; (InterpreterResult { result, output, gas }, address) } - - fn as_cheats_stack(&mut self) -> (CheatsInspectors<'_>, Option<&mut Cheatcodes>) { - let cheats_stack = CheatsInspectors { - chisel_state: self.chisel_state.as_deref_mut(), - coverage: self.coverage.as_deref_mut(), - debugger: self.debugger.as_deref_mut(), - fuzzer: self.fuzzer.as_deref_mut(), - log_collector: self.log_collector.as_deref_mut(), - printer: self.printer.as_deref_mut(), - tracer: self.tracer.as_deref_mut(), - - enable_isolation: self.enable_isolation, - in_inner_context: self.in_inner_context, - inner_context_data: &mut self.inner_context_data, - }; - - (cheats_stack, self.cheatcodes.as_deref_mut()) - } } // NOTE: `&mut DB` is required because we recurse inside of `transact_inner` and we need to use the @@ -841,9 +767,8 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStack ecx ); - let (mut cheats_stack, cheatcodes) = self.as_cheats_stack(); - if let Some(cheatcodes) = cheatcodes { - if let Some(output) = cheatcodes.call(ecx, call, &mut cheats_stack) { + if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() { + if let Some(output) = cheatcodes.call(ecx, call, self.inner) { if output.result.result != InstructionResult::Continue { return Some(output) } @@ -1061,3 +986,31 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> InspectorExt<&'a mut DB> for Inspecto self.as_stack_ref().should_use_create2_factory(ecx, inputs) } } + +impl<'a> Deref for InspectorStackRefMut<'a> { + type Target = &'a mut InspectorStackInner; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for InspectorStackRefMut<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl Deref for InspectorStack { + type Target = InspectorStackInner; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for InspectorStack { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} From e27b1567164e5148b289208a03fb73ca3b969941 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 17 Jun 2024 20:34:50 +0300 Subject: [PATCH 03/26] wip --- crates/cheatcodes/src/evm/fork.rs | 26 ++++-- crates/cheatcodes/src/inspector.rs | 73 +++++++++++++--- crates/cheatcodes/src/lib.rs | 18 ++-- crates/cheatcodes/src/script.rs | 2 +- crates/evm/core/src/backend/cow.rs | 4 +- crates/evm/core/src/backend/mod.rs | 32 ++----- crates/evm/evm/src/inspectors/stack.rs | 110 ++++++------------------- 7 files changed, 122 insertions(+), 143 deletions(-) diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index a767520288f21..e2a1f1010f12f 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -4,7 +4,7 @@ use alloy_provider::Provider; use alloy_rpc_types::Filter; use alloy_sol_types::SolValue; use foundry_common::provider::ProviderBuilder; -use foundry_evm_core::fork::CreateFork; +use foundry_evm_core::{fork::CreateFork, InspectorExt}; impl Cheatcode for activeForkCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { @@ -127,28 +127,36 @@ impl Cheatcode for selectForkCall { } impl Cheatcode for transact_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_full_with_executor( + &self, + ccx: &mut CheatsCtxt, + executor: &mut E, + ) -> Result { let Self { txHash } = *self; ccx.ecx.db.transact( None, txHash, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state, - ccx.state, + &mut executor.get_inspector(ccx.state) as &mut dyn InspectorExt<_>, )?; Ok(Default::default()) } } impl Cheatcode for transact_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_full_with_executor( + &self, + ccx: &mut CheatsCtxt, + executor: &mut E, + ) -> Result { let Self { forkId, txHash } = *self; ccx.ecx.db.transact( Some(forkId), txHash, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state, - ccx.state, + &mut executor.get_inspector(ccx.state) as &mut dyn InspectorExt<_>, )?; Ok(Default::default()) } @@ -192,7 +200,9 @@ impl Cheatcode for makePersistent_2Call { impl Cheatcode for makePersistent_3Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { accounts } = self; - ccx.ecx.db.extend_persistent_accounts(accounts.iter().copied()); + for account in accounts { + ccx.ecx.db.add_persistent_account(*account); + } Ok(Default::default()) } } @@ -208,7 +218,9 @@ impl Cheatcode for revokePersistent_0Call { impl Cheatcode for revokePersistent_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { accounts } = self; - ccx.ecx.db.remove_persistent_accounts(accounts.iter().copied()); + for account in accounts { + ccx.ecx.db.remove_persistent_account(account); + } Ok(Default::default()) } } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 8f33537407e09..814bf7c734a52 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -24,6 +24,7 @@ use foundry_evm_core::{ abi::Vm::stopExpectSafeMemoryCall, backend::{DatabaseExt, RevertDiagnostic}, constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, + utils::new_evm_with_existing_context, InspectorExt, }; use itertools::Itertools; @@ -33,7 +34,7 @@ use revm::{ InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, primitives::{BlockEnv, CreateScheme, EVMError, TransactTo}, - ContextPrecompiles, EvmContext, InnerEvmContext, Inspector, + EvmContext, InnerEvmContext, }; use rustc_hash::FxHashMap; use serde_json::Value; @@ -47,12 +48,60 @@ use std::{ }; pub trait CheatcodesExecutor { + fn get_inspector<'a, DB: DatabaseExt>( + &'a mut self, + cheats: &'a mut Cheatcodes, + ) -> impl InspectorExt + 'a; + fn exec_create( &mut self, inputs: CreateInputs, cheats: &mut Cheatcodes, - ecx: &mut InnerEvmContext<&mut DB>, - ) -> Result>; + ecx: &mut InnerEvmContext, + ) -> Result> { + let inspector = self.get_inspector(cheats); + let error = std::mem::replace(&mut ecx.error, Ok(())); + let l1_block_info = std::mem::take(&mut ecx.l1_block_info); + + let inner = revm::InnerEvmContext { + env: ecx.env.clone(), + journaled_state: std::mem::replace( + &mut ecx.journaled_state, + revm::JournaledState::new(Default::default(), Default::default()), + ), + db: &mut ecx.db as &mut dyn DatabaseExt, + error, + l1_block_info, + }; + + let mut evm = new_evm_with_existing_context(inner, inspector); + + evm.context.evm.inner.journaled_state.depth += 1; + + let first_frame_or_result = + evm.handler.execution().create(&mut evm.context, Box::new(inputs))?; + + let mut result = match first_frame_or_result { + revm::FrameOrResult::Frame(first_frame) => evm.run_the_loop(first_frame)?, + revm::FrameOrResult::Result(result) => result, + }; + + evm.handler.execution().last_frame_return(&mut evm.context, &mut result)?; + + let outcome = match result { + revm::FrameResult::Call(_) | revm::FrameResult::EOFCreate(_) => unreachable!(), + revm::FrameResult::Create(create) => create, + }; + + evm.context.evm.inner.journaled_state.depth -= 1; + + ecx.journaled_state = evm.context.evm.inner.journaled_state; + ecx.env = evm.context.evm.inner.env; + ecx.l1_block_info = evm.context.evm.inner.l1_block_info; + ecx.error = evm.context.evm.inner.error; + + Ok(outcome) + } } macro_rules! try_or_continue { @@ -265,7 +314,7 @@ impl Cheatcodes { fn apply_cheatcode( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, call: &CallInputs, executor: &mut E, ) -> Result { @@ -333,7 +382,7 @@ impl Cheatcodes { /// /// Cleanup any previously applied cheatcodes that altered the state in such a way that revm's /// revert would run into issues. - pub fn on_revert(&mut self, ecx: &mut EvmContext<&mut DB>) { + pub fn on_revert(&mut self, ecx: &mut EvmContext) { trace!(deals=?self.eth_deals.len(), "rolling back deals"); // Delay revert clean up until expected revert is handled, if set. @@ -362,7 +411,7 @@ impl Cheatcodes { pub fn initialize_interp( &mut self, _: &mut Interpreter, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, ) { // When the first interpreter is initialized we've circumvented the balance and gas checks, // so we apply our actual block data with the correct fees and all. @@ -377,7 +426,7 @@ impl Cheatcodes { pub fn step( &mut self, interpreter: &mut Interpreter, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, ) { let ecx = &mut ecx.inner; self.pc = interpreter.program_counter(); @@ -825,7 +874,7 @@ impl Cheatcodes { pub fn call( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, call: &mut CallInputs, executor: &mut impl CheatcodesExecutor, ) -> Option { @@ -1085,7 +1134,7 @@ impl Cheatcodes { pub fn call_end( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, call: &CallInputs, mut outcome: CallOutcome, ) -> CallOutcome { @@ -1367,7 +1416,7 @@ impl Cheatcodes { pub fn create( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, call: &mut CreateInputs, ) -> Option { let ecx = &mut ecx.inner; @@ -1473,7 +1522,7 @@ impl Cheatcodes { pub fn create_end( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, _call: &CreateInputs, mut outcome: CreateOutcome, ) -> CreateOutcome { @@ -1584,7 +1633,7 @@ impl Cheatcodes { pub fn should_use_create2_factory( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, inputs: &mut CreateInputs, ) -> bool { if let CreateScheme::Create2 { .. } = inputs.scheme { diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 5f0ba8a10ff75..916c7717fdb93 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -11,7 +11,7 @@ pub extern crate foundry_cheatcodes_spec as spec; #[macro_use] extern crate tracing; -use alloy_primitives::{Address, U256}; +use alloy_primitives::Address; use foundry_evm_core::backend::DatabaseExt; use revm::{ContextPrecompiles, InnerEvmContext}; @@ -138,21 +138,21 @@ impl DynCheatcode for T { } /// The cheatcode context, used in [`Cheatcode`]. -pub(crate) struct CheatsCtxt<'cheats, 'evm, 'db, DB: DatabaseExt> { +pub(crate) struct CheatsCtxt<'cheats, 'evm, DB: DatabaseExt> { /// The cheatcodes inspector state. pub(crate) state: &'cheats mut Cheatcodes, /// The EVM data. - pub(crate) ecx: &'evm mut InnerEvmContext<&'db mut DB>, + pub(crate) ecx: &'evm mut InnerEvmContext, /// The precompiles context. - pub(crate) precompiles: &'evm mut ContextPrecompiles<&'db mut DB>, + pub(crate) precompiles: &'evm mut ContextPrecompiles, /// The original `msg.sender`. pub(crate) caller: Address, /// Gas limit of the current cheatcode call. pub(crate) gas_limit: u64, } -impl<'cheats, 'evm, 'db, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'evm, 'db, DB> { - type Target = InnerEvmContext<&'db mut DB>; +impl<'cheats, 'evm, 'db, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'evm, DB> { + type Target = InnerEvmContext; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -160,16 +160,14 @@ impl<'cheats, 'evm, 'db, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats } } -impl<'cheats, 'evm, 'db, DB: DatabaseExt> std::ops::DerefMut - for CheatsCtxt<'cheats, 'evm, 'db, DB> -{ +impl<'cheats, 'evm, 'db, DB: DatabaseExt> std::ops::DerefMut for CheatsCtxt<'cheats, 'evm, DB> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.ecx } } -impl<'cheats, 'evm, 'db, DB: DatabaseExt> CheatsCtxt<'cheats, 'evm, 'db, DB> { +impl<'cheats, 'evm, 'db, DB: DatabaseExt> CheatsCtxt<'cheats, 'evm, DB> { #[inline] pub(crate) fn is_precompile(&self, address: &Address) -> bool { self.precompiles.contains_key(address) diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 0252c85b61560..97c65106a1605 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -1,6 +1,6 @@ //! Implementations of [`Scripting`](spec::Group::Scripting) cheatcodes. -use crate::{Cheatcode, CheatcodesExecutor, CheatsCtxt, DatabaseExt, Result, Vm::*}; +use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{Address, B256, U256}; use alloy_signer_wallet::LocalWallet; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index 9b53fb4ee29aa..47c1f35658535 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -177,13 +177,13 @@ impl<'a> DatabaseExt for CowBackend<'a> { self.backend_mut(env).roll_fork_to_transaction(id, transaction, env, journaled_state) } - fn transact>( + fn transact( &mut self, id: Option, transaction: B256, env: &mut Env, journaled_state: &mut JournaledState, - inspector: &mut I, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { self.backend_mut(env).transact(id, transaction, env, journaled_state, inspector) } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index b72f0caed40c0..ca89dba7f2190 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -189,16 +189,14 @@ pub trait DatabaseExt: Database + DatabaseCommit { ) -> eyre::Result<()>; /// Fetches the given transaction for the fork and executes it, committing the state in the DB - fn transact>( + fn transact( &mut self, id: Option, transaction: B256, env: &mut Env, journaled_state: &mut JournaledState, - inspector: &mut I, - ) -> eyre::Result<()> - where - Self: Sized; + inspector: &mut dyn InspectorExt, + ) -> eyre::Result<()>; /// Returns the `ForkId` that's currently used in the database, if fork mode is on fn active_fork_id(&self) -> Option; @@ -276,26 +274,6 @@ pub trait DatabaseExt: Database + DatabaseCommit { /// Marks the given account as persistent. fn add_persistent_account(&mut self, account: Address) -> bool; - /// Removes persistent status from all given accounts - fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator) - where - Self: Sized, - { - for acc in accounts { - self.remove_persistent_account(&acc); - } - } - - /// Extends the persistent accounts with the accounts the iterator yields. - fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator) - where - Self: Sized, - { - for acc in accounts { - self.add_persistent_account(acc); - } - } - /// Grants cheatcode access for the given `account` /// /// Returns true if the `account` already has access @@ -1241,13 +1219,13 @@ impl DatabaseExt for Backend { Ok(()) } - fn transact>( + fn transact( &mut self, maybe_id: Option, transaction: B256, env: &mut Env, journaled_state: &mut JournaledState, - inspector: &mut I, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { trace!(?maybe_id, ?transaction, "execute transaction"); let persistent_accounts = self.inner.persistent_accounts.clone(); diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 796c46a8951b1..b2a4fcdb049a0 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -17,9 +17,8 @@ use revm::{ CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult, Interpreter, InterpreterResult, }, - primitives::{BlockEnv, EVMError, Env, EnvWithHandlerCfg, ExecutionResult, Output, TransactTo}, - DatabaseCommit, EvmContext, FrameOrResult, FrameResult, InnerEvmContext, Inspector, - JournaledState, + primitives::{BlockEnv, Env, EnvWithHandlerCfg, ExecutionResult, Output, TransactTo}, + DatabaseCommit, EvmContext, Inspector, }; use std::{ collections::HashMap, @@ -308,66 +307,12 @@ struct InspectorStackRefMut<'a> { pub inner: &'a mut InspectorStackInner, } -impl InspectorStackInner { - fn with_inspector_ref( - &mut self, - cheatcodes: &mut Cheatcodes, - f: impl FnOnce(&mut InspectorStackRefMut<'_>) -> O, - ) -> O { - f(&mut InspectorStackRefMut { cheatcodes: Some(cheatcodes), inner: self }) - } -} - impl CheatcodesExecutor for InspectorStackInner { - fn exec_create( - &mut self, - inputs: CreateInputs, - cheats: &mut Cheatcodes, - ecx: &mut InnerEvmContext<&mut DB>, - ) -> Result> { - self.with_inspector_ref(cheats, move |stack| { - let error = std::mem::replace(&mut ecx.error, Ok(())); - let l1_block_info = std::mem::take(&mut ecx.l1_block_info); - - let inner = revm::InnerEvmContext { - env: ecx.env.clone(), - journaled_state: std::mem::replace( - &mut ecx.journaled_state, - JournaledState::new(Default::default(), Default::default()), - ), - db: &mut *ecx.db, - error, - l1_block_info, - }; - - let mut evm = crate::utils::new_evm_with_existing_context(inner, stack); - - evm.context.evm.inner.journaled_state.depth += 1; - - let first_frame_or_result = - evm.handler.execution().create(&mut evm.context, Box::new(inputs))?; - - let mut result = match first_frame_or_result { - FrameOrResult::Frame(first_frame) => evm.run_the_loop(first_frame)?, - FrameOrResult::Result(result) => result, - }; - - evm.handler.execution().last_frame_return(&mut evm.context, &mut result)?; - - let outcome = match result { - FrameResult::Call(_) | FrameResult::EOFCreate(_) => unreachable!(), - FrameResult::Create(create) => create, - }; - - evm.context.evm.inner.journaled_state.depth -= 1; - - ecx.journaled_state = evm.context.evm.inner.journaled_state; - ecx.env = evm.context.evm.inner.env; - ecx.l1_block_info = evm.context.evm.inner.l1_block_info; - ecx.error = evm.context.evm.inner.error; - - Ok(outcome) - }) + fn get_inspector<'a, DB: DatabaseExt>( + &'a mut self, + cheats: &'a mut Cheatcodes, + ) -> impl InspectorExt + 'a { + InspectorStackRefMut { cheatcodes: Some(cheats), inner: self } } } @@ -501,10 +446,7 @@ impl<'a> InspectorStackRefMut<'a> { /// Should be called on the top-level call of inner context (depth == 0 && /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility /// Updates tx.origin to the value before entering inner context - fn adjust_evm_data_for_inner_context( - &mut self, - ecx: &mut EvmContext<&mut DB>, - ) { + fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EvmContext) { let inner_context_data = self.inner_context_data.as_ref().expect("should be called in inner context"); let sender_acc = ecx @@ -520,7 +462,7 @@ impl<'a> InspectorStackRefMut<'a> { fn do_call_end( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { @@ -552,7 +494,7 @@ impl<'a> InspectorStackRefMut<'a> { fn transact_inner( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, transact_to: TransactTo, caller: Address, input: Bytes, @@ -599,7 +541,11 @@ impl<'a> InspectorStackRefMut<'a> { let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id()); let res = { - let mut evm = crate::utils::new_evm_with_inspector(&mut *ecx.db, env, &mut *self); + let mut evm = crate::utils::new_evm_with_inspector( + (&mut ecx.db) as &mut dyn DatabaseExt, + env, + &mut *self, + ); let res = evm.transact(); // need to reset the env in case it was modified via cheatcodes during execution @@ -683,8 +629,8 @@ impl<'a> InspectorStackRefMut<'a> { // implementation. This currently works because internally we only use `&mut DB` anyways, but if // this ever needs to be changed, this can be reverted back to using just `DB`, and instead using // dynamic dispatch (`&mut dyn ...`) in `transact_inner`. -impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStackRefMut<'a> { - fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { +impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector for InspectorStackRefMut<'a> { + fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { call_inspectors_adjust_depth!( #[no_ret] [&mut self.coverage, &mut self.tracer, &mut self.cheatcodes, &mut self.printer], @@ -694,7 +640,7 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStack ); } - fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { + fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { call_inspectors_adjust_depth!( #[no_ret] [ @@ -711,7 +657,7 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStack ); } - fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { + fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { call_inspectors_adjust_depth!( #[no_ret] [&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state, &mut self.printer], @@ -721,7 +667,7 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStack ); } - fn log(&mut self, ecx: &mut EvmContext<&mut DB>, log: &Log) { + fn log(&mut self, ecx: &mut EvmContext, log: &Log) { call_inspectors_adjust_depth!( #[no_ret] [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer], @@ -731,11 +677,7 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStack ); } - fn call( - &mut self, - ecx: &mut EvmContext<&mut DB>, - call: &mut CallInputs, - ) -> Option { + fn call(&mut self, ecx: &mut EvmContext, call: &mut CallInputs) -> Option { if self.in_inner_context && ecx.journaled_state.depth == 0 { self.adjust_evm_data_for_inner_context(ecx); return None; @@ -796,7 +738,7 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStack fn call_end( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { @@ -826,7 +768,7 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStack fn create( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, create: &mut CreateInputs, ) -> Option { if self.in_inner_context && ecx.journaled_state.depth == 0 { @@ -860,7 +802,7 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStack fn create_end( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, call: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { @@ -900,10 +842,10 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&mut DB> for InspectorStack } } -impl<'a, DB: DatabaseExt + DatabaseCommit> InspectorExt<&mut DB> for InspectorStackRefMut<'a> { +impl<'a, DB: DatabaseExt + DatabaseCommit> InspectorExt for InspectorStackRefMut<'a> { fn should_use_create2_factory( &mut self, - ecx: &mut EvmContext<&mut DB>, + ecx: &mut EvmContext, inputs: &mut CreateInputs, ) -> bool { call_inspectors_adjust_depth!( From f3a97a4d787d83364797e58dd8b8f84db60b964a Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 17 Jun 2024 20:38:23 +0300 Subject: [PATCH 04/26] clean up --- crates/evm/evm/src/inspectors/stack.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index b2a4fcdb049a0..b739065b5c939 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -683,11 +683,6 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector for InspectorStackRefMu return None; } - println!( - "CALL {:?} {:?} depth {:?}", - call.target_address, call.input, ecx.journaled_state.depth - ); - call_inspectors_adjust_depth!( [ &mut self.fuzzer, @@ -748,11 +743,6 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector for InspectorStackRefMu return outcome } - println!( - "CALL END {:?} {:?} depth {:?}", - inputs.target_address, inputs.input, ecx.journaled_state.depth - ); - let outcome = self.do_call_end(ecx, inputs, outcome); if outcome.result.is_revert() { // Encountered a revert, since cheatcodes may have altered the evm state in such a way @@ -776,8 +766,6 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector for InspectorStackRefMu return None; } - println!("CREATE depth {:?}", ecx.journaled_state.depth); - call_inspectors_adjust_depth!( [&mut self.debugger, &mut self.tracer, &mut self.coverage, &mut self.cheatcodes], |inspector| inspector.create(ecx, create).map(Some), @@ -812,8 +800,6 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector for InspectorStackRefMu return outcome } - println!("CREATE END depth {:?}", ecx.journaled_state.depth); - let result = outcome.result.result; call_inspectors_adjust_depth!( From 7e49a1c6a3193a58f0925dc73da329f58b935b93 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 18 Jun 2024 19:07:48 +0300 Subject: [PATCH 05/26] fix vm.transact traces --- crates/evm/core/src/backend/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index ca89dba7f2190..0e1d10cdfc657 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1877,10 +1877,13 @@ fn commit_transaction>( let res = { let fork = fork.clone(); let journaled_state = journaled_state.clone(); + let depth = journaled_state.depth; let db = Backend::new_with_fork(fork_id, fork, journaled_state); - crate::utils::new_evm_with_inspector(db, env, inspector) - .transact() - .wrap_err("backend: failed committing transaction")? + + let mut evm = crate::utils::new_evm_with_inspector(db, env, inspector); + // Adjust inner EVM depth to ensure that inspectors receive accurate data. + evm.context.evm.inner.journaled_state.depth = depth + 1; + evm.transact().wrap_err("backend: failed committing transaction")? }; trace!(elapsed = ?now.elapsed(), "transacted transaction"); From 9be907aa010e79bfcabfe45d4b6693b1c18c1716 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Jun 2024 17:21:25 +0300 Subject: [PATCH 06/26] clean up --- crates/cheatcodes/spec/src/vm.rs | 1 + crates/cheatcodes/src/evm/fork.rs | 6 +++--- crates/evm/evm/src/inspectors/stack.rs | 22 +++++++++++++++------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index a47813ead88b4..ca5b7be191c54 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1475,6 +1475,7 @@ interface Vm { function getCode(string calldata artifactPath) external view returns (bytes memory creationBytecode); /// Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the + /// artifact in the form of :: where and parts are optional. #[cheatcode(group = Filesystem)] function deployCode(string calldata artifactPath) external returns (address deployedAddress); diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index e2a1f1010f12f..9be68d9939053 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -4,7 +4,7 @@ use alloy_provider::Provider; use alloy_rpc_types::Filter; use alloy_sol_types::SolValue; use foundry_common::provider::ProviderBuilder; -use foundry_evm_core::{fork::CreateFork, InspectorExt}; +use foundry_evm_core::fork::CreateFork; impl Cheatcode for activeForkCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { @@ -138,7 +138,7 @@ impl Cheatcode for transact_0Call { txHash, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state, - &mut executor.get_inspector(ccx.state) as &mut dyn InspectorExt<_>, + &mut executor.get_inspector(ccx.state), )?; Ok(Default::default()) } @@ -156,7 +156,7 @@ impl Cheatcode for transact_1Call { txHash, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state, - &mut executor.get_inspector(ccx.state) as &mut dyn InspectorExt<_>, + &mut executor.get_inspector(ccx.state), )?; Ok(Default::default()) } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index b739065b5c939..346949f9231e5 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -18,7 +18,7 @@ use revm::{ Interpreter, InterpreterResult, }, primitives::{BlockEnv, Env, EnvWithHandlerCfg, ExecutionResult, Output, TransactTo}, - DatabaseCommit, EvmContext, Inspector, + EvmContext, Inspector, }; use std::{ collections::HashMap, @@ -280,12 +280,17 @@ pub struct InnerContextData { /// /// If a call to an inspector returns a value other than [InstructionResult::Continue] (or /// equivalent) the remaining inspectors are not called. +/// +/// Stack is divided into [Cheatcodes] and [InspectorStackInner]. This is done to allow passing +/// mutable reference to [InspectorStackInner] into [Cheatcodes] functions to reassemble them into +/// [InspectorStackRefMut] and allow usage of it as [revm::Inspector]. #[derive(Clone, Debug, Default)] pub struct InspectorStack { pub cheatcodes: Option, pub inner: InspectorStackInner, } +/// All used inpectors besides [Cheatcodes]. #[derive(Default, Clone, Debug)] pub struct InspectorStackInner { pub chisel_state: Option, @@ -302,6 +307,9 @@ pub struct InspectorStackInner { pub inner_context_data: Option, } +/// Struct keeping mutable references to both parts of [InspectorStack] and implementing +/// [revm::Inspector]. This struct can be obtained via [InspectorStack::as_stack_ref] or via +/// [CheatcodesExecutor::get_inspector] method implemented for [InspectorStackInner]. struct InspectorStackRefMut<'a> { pub cheatcodes: Option<&'a mut Cheatcodes>, pub inner: &'a mut InspectorStackInner, @@ -492,7 +500,7 @@ impl<'a> InspectorStackRefMut<'a> { outcome } - fn transact_inner( + fn transact_inner( &mut self, ecx: &mut EvmContext, transact_to: TransactTo, @@ -542,7 +550,7 @@ impl<'a> InspectorStackRefMut<'a> { let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id()); let res = { let mut evm = crate::utils::new_evm_with_inspector( - (&mut ecx.db) as &mut dyn DatabaseExt, + &mut ecx.db as &mut dyn DatabaseExt, env, &mut *self, ); @@ -629,7 +637,7 @@ impl<'a> InspectorStackRefMut<'a> { // implementation. This currently works because internally we only use `&mut DB` anyways, but if // this ever needs to be changed, this can be reverted back to using just `DB`, and instead using // dynamic dispatch (`&mut dyn ...`) in `transact_inner`. -impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector for InspectorStackRefMut<'a> { +impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { call_inspectors_adjust_depth!( #[no_ret] @@ -828,7 +836,7 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector for InspectorStackRefMu } } -impl<'a, DB: DatabaseExt + DatabaseCommit> InspectorExt for InspectorStackRefMut<'a> { +impl<'a, DB: DatabaseExt> InspectorExt for InspectorStackRefMut<'a> { fn should_use_create2_factory( &mut self, ecx: &mut EvmContext, @@ -845,7 +853,7 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> InspectorExt for InspectorStackRe } } -impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&'a mut DB> for InspectorStack { +impl<'a, DB: DatabaseExt> Inspector<&'a mut DB> for InspectorStack { fn call( &mut self, context: &mut EvmContext<&'a mut DB>, @@ -905,7 +913,7 @@ impl<'a, DB: DatabaseExt + DatabaseCommit> Inspector<&'a mut DB> for InspectorSt } } -impl<'a, DB: DatabaseExt + DatabaseCommit> InspectorExt<&'a mut DB> for InspectorStack { +impl<'a, DB: DatabaseExt> InspectorExt<&'a mut DB> for InspectorStack { fn should_use_create2_factory( &mut self, ecx: &mut EvmContext<&'a mut DB>, From 72eec886b8594a3fcf86343dc995741ae4d6882c Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Jun 2024 19:35:52 +0300 Subject: [PATCH 07/26] clippy --- crates/cheatcodes/src/fs.rs | 6 +++--- crates/cheatcodes/src/inspector.rs | 2 +- crates/cheatcodes/src/lib.rs | 6 +++--- crates/evm/evm/src/inspectors/stack.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 0822452693108..7d563c9e4eca1 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -271,7 +271,7 @@ impl Cheatcode for deployCodeCall { executor: &mut E, ) -> Result { let Self { artifactPath: path } = self; - let bytecode = get_artifact_code(&ccx.state, path, false)?; + let bytecode = get_artifact_code(ccx.state, path, false)?; let output = executor .exec_create( CreateInputs { @@ -281,8 +281,8 @@ impl Cheatcode for deployCodeCall { init_code: bytecode, gas_limit: ccx.gas_limit, }, - &mut ccx.state, - &mut ccx.ecx, + ccx.state, + ccx.ecx, ) .unwrap(); diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 02c6cca00537b..8842fa09ada22 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -1,4 +1,4 @@ -//! Cheatcode EVM [Inspector]. +//! Cheatcode EVM inspector. use crate::{ evm::{ diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 766575b4bf314..9597aed928011 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -151,7 +151,7 @@ pub(crate) struct CheatsCtxt<'cheats, 'evm, DB: DatabaseExt> { pub(crate) gas_limit: u64, } -impl<'cheats, 'evm, 'db, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'evm, DB> { +impl<'cheats, 'evm, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'evm, DB> { type Target = InnerEvmContext; #[inline(always)] @@ -160,14 +160,14 @@ impl<'cheats, 'evm, 'db, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats } } -impl<'cheats, 'evm, 'db, DB: DatabaseExt> std::ops::DerefMut for CheatsCtxt<'cheats, 'evm, DB> { +impl<'cheats, 'evm, DB: DatabaseExt> std::ops::DerefMut for CheatsCtxt<'cheats, 'evm, DB> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.ecx } } -impl<'cheats, 'evm, 'db, DB: DatabaseExt> CheatsCtxt<'cheats, 'evm, DB> { +impl<'cheats, 'evm, DB: DatabaseExt> CheatsCtxt<'cheats, 'evm, DB> { #[inline] pub(crate) fn is_precompile(&self, address: &Address) -> bool { self.precompiles.contains(address) diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index c3136ba1ef748..7198d6d7a6317 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -461,7 +461,7 @@ impl InspectorStack { } } - fn as_stack_ref<'a>(&'a mut self) -> InspectorStackRefMut<'a> { + fn as_stack_ref(&mut self) -> InspectorStackRefMut<'_> { InspectorStackRefMut { cheatcodes: self.cheatcodes.as_mut(), inner: &mut self.inner } } } From 1895c8a657e1687699d8b0b8b32a6dedb2448e48 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Jun 2024 19:36:23 +0300 Subject: [PATCH 08/26] cargo cheats --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++++++++++ testdata/cheats/Vm.sol | 1 + 2 files changed, 21 insertions(+) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 85b2766da128e..3aea15cbb62d4 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3551,6 +3551,26 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "deployCode", + "description": "Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.", + "declaration": "function deployCode(string calldata artifactPath) external returns (address deployedAddress);", + "visibility": "external", + "mutability": "", + "signature": "deployCode(string)", + "selector": "0x9a8325a0", + "selectorBytes": [ + 154, + 131, + 37, + 160 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "deriveKey_0", diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 23e1f699c89d1..17051a32b15d0 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -173,6 +173,7 @@ interface Vm { function deal(address account, uint256 newBalance) external; function deleteSnapshot(uint256 snapshotId) external returns (bool success); function deleteSnapshots() external; + function deployCode(string calldata artifactPath) external returns (address deployedAddress); function deriveKey(string calldata mnemonic, uint32 index) external pure returns (uint256 privateKey); function deriveKey(string calldata mnemonic, string calldata derivationPath, uint32 index) external pure returns (uint256 privateKey); function deriveKey(string calldata mnemonic, uint32 index, string calldata language) external pure returns (uint256 privateKey); From 96dcd0b678e77c95f8cca380eac84d7193ce7a84 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Jun 2024 20:49:32 +0300 Subject: [PATCH 09/26] review fixes --- crates/cheatcodes/src/inspector.rs | 15 +++---- crates/cheatcodes/src/lib.rs | 1 + crates/evm/evm/src/inspectors/stack.rs | 57 +++++++++++--------------- 3 files changed, 34 insertions(+), 39 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 8842fa09ada22..72e08f09e645b 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -46,12 +46,20 @@ use std::{ path::PathBuf, sync::Arc, }; +/// Helper trait for obtaining complete [revm::Inspector] instance from mutable reference to +/// [Cheatcodes]. +/// +/// This is needed for cases when inspector itself needs mutable access to [Cheatcodes] state and +/// allows us to correctly execute arbitrary EVM frames from inside cheatcode implementations. pub trait CheatcodesExecutor { + /// Core trait method accepting mutable reference to [Cheatcodes] and returning + /// [revm::Inspector]. fn get_inspector<'a, DB: DatabaseExt>( &'a mut self, cheats: &'a mut Cheatcodes, ) -> impl InspectorExt + 'a; + /// Obtains [revm::Inspector] instance and executes the given CREATE frame. fn exec_create( &mut self, inputs: CreateInputs, @@ -459,13 +467,6 @@ impl Cheatcodes { } } - pub fn step_end( - &mut self, - _interpreter: &mut Interpreter, - _context: &mut EvmContext, - ) { - } - pub fn log(&mut self, _context: &mut EvmContext, log: &Log) { if !self.expected_emits.is_empty() { expect::handle_expect_emit(self, log); diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 9597aed928011..6bdb797bccea9 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -74,6 +74,7 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { /// Applies this cheatcode to the given context and executor. /// /// Implement this function if you need access to the executor. + #[inline(always)] fn apply_full_with_executor( &self, ccx: &mut CheatsCtxt, diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 7198d6d7a6317..9c6744479e7ba 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -287,6 +287,8 @@ pub struct InspectorStack { } /// All used inpectors besides [Cheatcodes]. +/// +/// See [`InspectorStack`]. #[derive(Default, Clone, Debug)] pub struct InspectorStackInner { pub chisel_state: Option, @@ -304,7 +306,7 @@ pub struct InspectorStackInner { } /// Struct keeping mutable references to both parts of [InspectorStack] and implementing -/// [revm::Inspector]. This struct can be obtained via [InspectorStack::as_stack_ref] or via +/// [revm::Inspector]. This struct can be obtained via [InspectorStack::as_mut] or via /// [CheatcodesExecutor::get_inspector] method implemented for [InspectorStackInner]. struct InspectorStackRefMut<'a> { pub cheatcodes: Option<&'a mut Cheatcodes>, @@ -461,7 +463,7 @@ impl InspectorStack { } } - fn as_stack_ref(&mut self) -> InspectorStackRefMut<'_> { + fn as_mut(&mut self) -> InspectorStackRefMut<'_> { InspectorStackRefMut { cheatcodes: self.cheatcodes.as_mut(), inner: &mut self.inner } } } @@ -649,11 +651,6 @@ impl<'a> InspectorStackRefMut<'a> { } } -// NOTE: `&mut DB` is required because we recurse inside of `transact_inner` and we need to use the -// same reference to the DB, otherwise there's infinite recursion and Rust fails to instatiate this -// implementation. This currently works because internally we only use `&mut DB` anyways, but if -// this ever needs to be changed, this can be reverted back to using just `DB`, and instead using -// dynamic dispatch (`&mut dyn ...`) in `transact_inner`. impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { call_inspectors_adjust_depth!( @@ -874,73 +871,69 @@ impl<'a, DB: DatabaseExt> InspectorExt for InspectorStackRefMut<'a> { } } -impl<'a, DB: DatabaseExt> Inspector<&'a mut DB> for InspectorStack { +impl Inspector for InspectorStack { fn call( &mut self, - context: &mut EvmContext<&'a mut DB>, + context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option { - self.as_stack_ref().call(context, inputs) + self.as_mut().call(context, inputs) } fn call_end( &mut self, - context: &mut EvmContext<&'a mut DB>, + context: &mut EvmContext, inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { - self.as_stack_ref().call_end(context, inputs, outcome) + self.as_mut().call_end(context, inputs, outcome) } fn create( &mut self, - context: &mut EvmContext<&'a mut DB>, + context: &mut EvmContext, create: &mut CreateInputs, ) -> Option { - self.as_stack_ref().create(context, create) + self.as_mut().create(context, create) } fn create_end( &mut self, - context: &mut EvmContext<&'a mut DB>, + context: &mut EvmContext, call: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { - self.as_stack_ref().create_end(context, call, outcome) + self.as_mut().create_end(context, call, outcome) } - fn initialize_interp( - &mut self, - interpreter: &mut Interpreter, - ecx: &mut EvmContext<&'a mut DB>, - ) { - self.as_stack_ref().initialize_interp(interpreter, ecx) + fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + self.as_mut().initialize_interp(interpreter, ecx) } - fn log(&mut self, ecx: &mut EvmContext<&'a mut DB>, log: &Log) { - self.as_stack_ref().log(ecx, log) + fn log(&mut self, ecx: &mut EvmContext, log: &Log) { + self.as_mut().log(ecx, log) } fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { - Inspector::<&mut DB>::selfdestruct(&mut self.as_stack_ref(), contract, target, value) + Inspector::::selfdestruct(&mut self.as_mut(), contract, target, value) } - fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&'a mut DB>) { - self.as_stack_ref().step(interpreter, ecx) + fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + self.as_mut().step(interpreter, ecx) } - fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&'a mut DB>) { - self.as_stack_ref().step_end(interpreter, ecx) + fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + self.as_mut().step_end(interpreter, ecx) } } -impl<'a, DB: DatabaseExt> InspectorExt<&'a mut DB> for InspectorStack { +impl<'a, DB: DatabaseExt> InspectorExt for InspectorStack { fn should_use_create2_factory( &mut self, - ecx: &mut EvmContext<&'a mut DB>, + ecx: &mut EvmContext, inputs: &mut CreateInputs, ) -> bool { - self.as_stack_ref().should_use_create2_factory(ecx, inputs) + self.as_mut().should_use_create2_factory(ecx, inputs) } } From eddf79b1c88d36c51160b72d732f2da8f1dea853 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Jun 2024 21:17:04 +0300 Subject: [PATCH 10/26] clippy --- crates/evm/evm/src/inspectors/stack.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 9c6744479e7ba..b10098460ecf3 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -927,7 +927,7 @@ impl Inspector for InspectorStack { } } -impl<'a, DB: DatabaseExt> InspectorExt for InspectorStack { +impl InspectorExt for InspectorStack { fn should_use_create2_factory( &mut self, ecx: &mut EvmContext, From 7fd940cd975d568fc0f95727d4dcc58bca1a8c8a Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Jun 2024 23:11:39 +0300 Subject: [PATCH 11/26] tests --- crates/cheatcodes/assets/cheatcodes.json | 22 +++++++- crates/cheatcodes/spec/src/vm.rs | 7 +++ crates/cheatcodes/src/fs.rs | 29 +++++++++- crates/forge/tests/it/main.rs | 1 + crates/forge/tests/it/test_helpers.rs | 68 +++++++++++++++++++----- crates/forge/tests/it/vyper.rs | 10 ++++ testdata/cheats/Vm.sol | 1 + testdata/default/cheats/DeployCode.t.sol | 44 +++++++++++++++ testdata/default/vyper/Counter.vy | 12 +++++ testdata/default/vyper/CounterTest.vy | 16 ++++++ testdata/default/vyper/ICounter.vyi | 12 +++++ 11 files changed, 207 insertions(+), 15 deletions(-) create mode 100644 crates/forge/tests/it/vyper.rs create mode 100644 testdata/default/cheats/DeployCode.t.sol create mode 100644 testdata/default/vyper/Counter.vy create mode 100644 testdata/default/vyper/CounterTest.vy create mode 100644 testdata/default/vyper/ICounter.vyi diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 3aea15cbb62d4..35dbfcb769b8d 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3553,7 +3553,7 @@ }, { "func": { - "id": "deployCode", + "id": "deployCode_0", "description": "Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.", "declaration": "function deployCode(string calldata artifactPath) external returns (address deployedAddress);", "visibility": "external", @@ -3571,6 +3571,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "deployCode_1", + "description": "Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.\n\nAdditionaly accepts abi-encoded constructor arguments.", + "declaration": "function deployCode(string calldata artifactPath, bytes calldata constructorArgs) external returns (address deployedAddress);", + "visibility": "external", + "mutability": "", + "signature": "deployCode(string,bytes)", + "selector": "0x29ce9dde", + "selectorBytes": [ + 41, + 206, + 157, + 222 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "deriveKey_0", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index fb193be7f028b..cd8aa08c509c6 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1479,6 +1479,13 @@ interface Vm { #[cheatcode(group = Filesystem)] function deployCode(string calldata artifactPath) external returns (address deployedAddress); + /// Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the + /// artifact in the form of :: where and parts are optional. + /// + /// Additionaly accepts abi-encoded constructor arguments. + #[cheatcode(group = Filesystem)] + function deployCode(string calldata artifactPath, bytes calldata constructorArgs) external returns (address deployedAddress); + /// Gets the deployed bytecode from an artifact file. Takes in the relative path to the json file or the path to the /// artifact in the form of :: where and parts are optional. #[cheatcode(group = Filesystem)] diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 7d563c9e4eca1..7eb4729b924aa 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -264,7 +264,7 @@ impl Cheatcode for getDeployedCodeCall { } } -impl Cheatcode for deployCodeCall { +impl Cheatcode for deployCode_0Call { fn apply_full_with_executor( &self, ccx: &mut CheatsCtxt, @@ -290,6 +290,33 @@ impl Cheatcode for deployCodeCall { } } +impl Cheatcode for deployCode_1Call { + fn apply_full_with_executor( + &self, + ccx: &mut CheatsCtxt, + executor: &mut E, + ) -> Result { + let Self { artifactPath: path, constructorArgs } = self; + let mut bytecode = get_artifact_code(ccx.state, path, false)?.to_vec(); + bytecode.extend_from_slice(&constructorArgs); + let output = executor + .exec_create( + CreateInputs { + caller: ccx.caller, + scheme: revm::primitives::CreateScheme::Create, + value: U256::ZERO, + init_code: bytecode.into(), + gas_limit: ccx.gas_limit, + }, + ccx.state, + ccx.ecx, + ) + .unwrap(); + + Ok(output.address.unwrap().abi_encode()) + } +} + /// Returns the path to the json artifact depending on the input /// /// Can parse following input formats: diff --git a/crates/forge/tests/it/main.rs b/crates/forge/tests/it/main.rs index 48c0d66351ab7..aaa129796a39a 100644 --- a/crates/forge/tests/it/main.rs +++ b/crates/forge/tests/it/main.rs @@ -10,3 +10,4 @@ mod inline; mod invariant; mod repros; mod spec; +mod vyper; diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 3bf2ae327bd68..f0af57e6eb082 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -7,7 +7,8 @@ use forge::{ }; use foundry_compilers::{ artifacts::{EvmVersion, Libraries, Settings}, - Project, ProjectCompileOutput, SolcConfig, + utils::RuntimeOrHandle, + Project, ProjectCompileOutput, SolcConfig, Vyper, }; use foundry_config::{ fs_permissions::PathPermission, Config, FsPermissions, FuzzConfig, FuzzDictionaryConfig, @@ -28,6 +29,7 @@ use std::{ pub const RE_PATH_SEPARATOR: &str = "/"; const TESTDATA: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata"); +const VYPER: Lazy = Lazy::new(|| std::env::temp_dir().join("vyper")); /// Profile for the tests group. Used to configure separate configurations for test runs. pub enum ForgeTestProfile { @@ -173,8 +175,8 @@ impl ForgeTestData { /// /// Uses [get_compiled] to lazily compile the project. pub fn new(profile: ForgeTestProfile) -> Self { - let project = profile.project(); - let output = get_compiled(&project); + let mut project = profile.project(); + let output = get_compiled(&mut project); let test_opts = profile.test_opts(&output); let config = profile.config(); let evm_opts = profile.evm_opts(); @@ -259,7 +261,41 @@ impl ForgeTestData { } } -pub fn get_compiled(project: &Project) -> ProjectCompileOutput { +/// Installs Vyper if it's not already present. +pub fn get_vyper() -> Vyper { + if let Ok(vyper) = Vyper::new("vyper") { + return vyper; + } + if let Ok(vyper) = Vyper::new(&*VYPER) { + return vyper; + } + RuntimeOrHandle::new().block_on(async { + #[cfg(target_family = "unix")] + use std::{fs::Permissions, os::unix::fs::PermissionsExt}; + + let url = match svm::platform() { + svm::Platform::MacOsAarch64 => "https://github.com/vyperlang/vyper/releases/download/v0.4.0rc6/vyper.0.4.0rc6+commit.33719560.darwin", + svm::Platform::LinuxAmd64 => "https://github.com/vyperlang/vyper/releases/download/v0.4.0rc6/vyper.0.4.0rc6+commit.33719560.linux", + svm::Platform::WindowsAmd64 => "https://github.com/vyperlang/vyper/releases/download/v0.4.0rc6/vyper.0.4.0rc6+commit.33719560.windows.exe", + _ => panic!("unsupported") + }; + + let res = reqwest::Client::builder().build().unwrap().get(url).send().await.unwrap(); + + assert!(res.status().is_success()); + + let bytes = res.bytes().await.unwrap(); + + std::fs::write(&*VYPER, bytes).unwrap(); + + #[cfg(target_family = "unix")] + std::fs::set_permissions(&*VYPER, Permissions::from_mode(0o755)).unwrap(); + + Vyper::new(&*VYPER).unwrap() + }) +} + +pub fn get_compiled(project: &mut Project) -> ProjectCompileOutput { let lock_file_path = project.sources_path().join(".lock"); // Compile only once per test run. // We need to use a file lock because `cargo-nextest` runs tests in different processes. @@ -268,21 +304,27 @@ pub fn get_compiled(project: &Project) -> ProjectCompileOutput { let mut lock = fd_lock::new_lock(&lock_file_path); let read = lock.read().unwrap(); let out; - if project.cache_path().exists() && std::fs::read(&lock_file_path).unwrap() == b"1" { - out = project.compile(); - drop(read); - } else { + + let mut write = None; + if !project.cache_path().exists() || std::fs::read(&lock_file_path).unwrap() != b"1" { drop(read); - let mut write = lock.write().unwrap(); - write.write_all(b"1").unwrap(); - out = project.compile(); - drop(write); + write = Some(lock.write().unwrap()); + } + + if project.compiler.vyper.is_none() { + project.compiler.vyper = Some(get_vyper()); } - let out = out.unwrap(); + out = project.compile().unwrap(); + if out.has_compiler_errors() { panic!("Compiled with errors:\n{out}"); } + + if let Some(ref mut write) = write { + write.write_all(b"1").unwrap(); + } + out } diff --git a/crates/forge/tests/it/vyper.rs b/crates/forge/tests/it/vyper.rs new file mode 100644 index 0000000000000..c40b87541bfb9 --- /dev/null +++ b/crates/forge/tests/it/vyper.rs @@ -0,0 +1,10 @@ +//! Integration tests for EVM specifications. + +use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; +use foundry_test_utils::Filter; + +#[tokio::test(flavor = "multi_thread")] +async fn test_basic_vyper_test() { + let filter = Filter::new("", "CounterTest", ".*vyper"); + TestConfig::with_filter(TEST_DATA_DEFAULT.runner(), filter).run().await; +} diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 17051a32b15d0..4a0ec81c60790 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -174,6 +174,7 @@ interface Vm { function deleteSnapshot(uint256 snapshotId) external returns (bool success); function deleteSnapshots() external; function deployCode(string calldata artifactPath) external returns (address deployedAddress); + function deployCode(string calldata artifactPath, bytes calldata constructorArgs) external returns (address deployedAddress); function deriveKey(string calldata mnemonic, uint32 index) external pure returns (uint256 privateKey); function deriveKey(string calldata mnemonic, string calldata derivationPath, uint32 index) external pure returns (uint256 privateKey); function deriveKey(string calldata mnemonic, uint32 index, string calldata language) external pure returns (uint256 privateKey); diff --git a/testdata/default/cheats/DeployCode.t.sol b/testdata/default/cheats/DeployCode.t.sol new file mode 100644 index 0000000000000..03ed28edac05c --- /dev/null +++ b/testdata/default/cheats/DeployCode.t.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract TestContract {} + +contract TestContractWithArgs { + uint256 public a; + uint256 public b; + + constructor(uint256 _a, uint256 _b) { + a = _a; + b = _b; + } +} + +contract DeployCodeTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + address public constant overrideAddress = 0x0000000000000000000000000000000000000064; + + event Payload(address sender, address target, bytes data); + + function testDeployCode() public { + address addrDefault = address(new TestContract()); + address addrDeployCode = vm.deployCode("cheats/DeployCode.t.sol:TestContract"); + + assertEq(addrDefault.code, addrDeployCode.code); + } + + function testDeployCodeWithArgs() public { + address withNew = address(new TestContractWithArgs(1, 2)); + TestContractWithArgs withDeployCode = TestContractWithArgs(vm.deployCode( + "cheats/DeployCode.t.sol:TestContractWithArgs", + abi.encode(3, 4) + )); + + assertEq(withNew.code, address(withDeployCode).code); + assertEq(withDeployCode.a(), 3); + assertEq(withDeployCode.b(), 4); + } +} diff --git a/testdata/default/vyper/Counter.vy b/testdata/default/vyper/Counter.vy new file mode 100644 index 0000000000000..772bddd11919c --- /dev/null +++ b/testdata/default/vyper/Counter.vy @@ -0,0 +1,12 @@ +from . import ICounter +implements: ICounter + +number: public(uint256) + +@external +def set_number(new_number: uint256): + self.number = new_number + +@external +def increment(): + self.number += 1 diff --git a/testdata/default/vyper/CounterTest.vy b/testdata/default/vyper/CounterTest.vy new file mode 100644 index 0000000000000..b6cc517d25dd6 --- /dev/null +++ b/testdata/default/vyper/CounterTest.vy @@ -0,0 +1,16 @@ +from . import ICounter + +interface Vm: + def deployCode(artifact_name: String[1024], args: Bytes[1024] = b"") -> address: nonpayable + +vm: constant(Vm) = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D) +counter: ICounter + +@external +def setUp(): + self.counter = ICounter(extcall vm.deployCode("vyper/Counter.vy")) + +@external +def test_increment(): + extcall self.counter.increment() + assert staticcall self.counter.number() == 1 diff --git a/testdata/default/vyper/ICounter.vyi b/testdata/default/vyper/ICounter.vyi new file mode 100644 index 0000000000000..e600c71c87e19 --- /dev/null +++ b/testdata/default/vyper/ICounter.vyi @@ -0,0 +1,12 @@ +@view +@external +def number() -> uint256: + ... + +@external +def set_number(new_number: uint256): + ... + +@external +def increment(): + ... \ No newline at end of file From ce9bc58b4d86a62d27c57db51f42de33fccac5d6 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Jun 2024 23:13:15 +0300 Subject: [PATCH 12/26] clippy --- crates/cheatcodes/src/fs.rs | 2 +- crates/forge/tests/it/test_helpers.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 7eb4729b924aa..8688485a6ca15 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -298,7 +298,7 @@ impl Cheatcode for deployCode_1Call { ) -> Result { let Self { artifactPath: path, constructorArgs } = self; let mut bytecode = get_artifact_code(ccx.state, path, false)?.to_vec(); - bytecode.extend_from_slice(&constructorArgs); + bytecode.extend_from_slice(constructorArgs); let output = executor .exec_create( CreateInputs { diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index f0af57e6eb082..725c87ab1e347 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -324,7 +324,7 @@ pub fn get_compiled(project: &mut Project) -> ProjectCompileOutput { if let Some(ref mut write) = write { write.write_all(b"1").unwrap(); } - + out } From 345c05e88a21ff75291851d2ebd9340238984125 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Jun 2024 23:23:06 +0300 Subject: [PATCH 13/26] cargo cheats --- crates/cheatcodes/assets/cheatcodes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 35dbfcb769b8d..2a125df98c5f7 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3574,7 +3574,7 @@ { "func": { "id": "deployCode_1", - "description": "Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.\n\nAdditionaly accepts abi-encoded constructor arguments.", + "description": "Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.\nAdditionaly accepts abi-encoded constructor arguments.", "declaration": "function deployCode(string calldata artifactPath, bytes calldata constructorArgs) external returns (address deployedAddress);", "visibility": "external", "mutability": "", From d8edd32c0a697f96c9f26bb71ab20dffbe8e0b5b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Jun 2024 23:23:45 +0300 Subject: [PATCH 14/26] const -> static --- crates/forge/tests/it/test_helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 725c87ab1e347..b7d5667428cc4 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -29,7 +29,7 @@ use std::{ pub const RE_PATH_SEPARATOR: &str = "/"; const TESTDATA: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata"); -const VYPER: Lazy = Lazy::new(|| std::env::temp_dir().join("vyper")); +static VYPER: Lazy = Lazy::new(|| std::env::temp_dir().join("vyper")); /// Profile for the tests group. Used to configure separate configurations for test runs. pub enum ForgeTestProfile { From 06cdaadcbd0aef74f34a58a5d539c8233b7054a7 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Jun 2024 23:25:12 +0300 Subject: [PATCH 15/26] fmt --- testdata/default/cheats/DeployCode.t.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/testdata/default/cheats/DeployCode.t.sol b/testdata/default/cheats/DeployCode.t.sol index 03ed28edac05c..330e826511da7 100644 --- a/testdata/default/cheats/DeployCode.t.sol +++ b/testdata/default/cheats/DeployCode.t.sol @@ -32,10 +32,8 @@ contract DeployCodeTest is DSTest { function testDeployCodeWithArgs() public { address withNew = address(new TestContractWithArgs(1, 2)); - TestContractWithArgs withDeployCode = TestContractWithArgs(vm.deployCode( - "cheats/DeployCode.t.sol:TestContractWithArgs", - abi.encode(3, 4) - )); + TestContractWithArgs withDeployCode = + TestContractWithArgs(vm.deployCode("cheats/DeployCode.t.sol:TestContractWithArgs", abi.encode(3, 4))); assertEq(withNew.code, address(withDeployCode).code); assertEq(withDeployCode.a(), 3); From fd558e3cc1539efddc9fd3bb4ccdff0408a5e20e Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 23 Jun 2024 08:53:11 +0300 Subject: [PATCH 16/26] clippy --- crates/cheatcodes/src/inspector.rs | 4 ++-- crates/evm/core/src/utils.rs | 4 ++-- crates/evm/evm/src/inspectors/stack.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 67ba6d2a940d1..6b8573aa3e445 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -33,8 +33,8 @@ use revm::{ opcode, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, - primitives::{BlockEnv, CreateScheme}, - EvmContext, InnerEvmContext, Inspector, + primitives::{BlockEnv, CreateScheme, EVMError}, + EvmContext, InnerEvmContext }; use rustc_hash::FxHashMap; use serde_json::Value; diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 101e78a16456a..2fc202c0dc189 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,7 +1,7 @@ pub use crate::ic::*; use crate::{constants::DEFAULT_CREATE2_DEPLOYER, InspectorExt}; use alloy_json_abi::{Function, JsonAbi}; -use alloy_primitives::{Address, Selector, U256}; +use alloy_primitives::{Address, Selector, TxKind, U256}; use alloy_rpc_types::{Block, Transaction}; use foundry_config::NamedChain; use revm::{ @@ -11,7 +11,7 @@ use revm::{ return_ok, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, Gas, InstructionResult, InterpreterResult, }, - primitives::{CreateScheme, EVMError, HandlerCfg, SpecId, TransactTo, KECCAK_EMPTY}, + primitives::{CreateScheme, EVMError, HandlerCfg, SpecId, KECCAK_EMPTY}, FrameOrResult, FrameResult, }; use std::{cell::RefCell, rc::Rc, sync::Arc}; diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 516534adc8afb..dad427b347829 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -2,7 +2,7 @@ use super::{ Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, Debugger, Fuzzer, LogCollector, StackSnapshotType, TracingInspector, TracingInspectorConfig, }; -use alloy_primitives::{Address, Bytes, Log, U256}; +use alloy_primitives::{Address, Bytes, Log, TxKind, U256}; use foundry_cheatcodes::CheatcodesExecutor; use foundry_evm_core::{ backend::{update_state, DatabaseExt}, @@ -21,7 +21,7 @@ use revm::{ BlockEnv, CreateScheme, Env, EnvWithHandlerCfg, ExecutionResult, Output, TransactTo, }, EvmContext, Inspector, - DatabaseCommit, EvmContext, Inspector, +}; use std::{ collections::HashMap, ops::{Deref, DerefMut}, From 36ad2b2513e11f3f3bb33ffb209b978050d185eb Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 23 Jun 2024 08:55:22 +0300 Subject: [PATCH 17/26] fix doc --- crates/evm/evm/src/inspectors/stack.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index dad427b347829..934d6f12f98ac 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -303,7 +303,7 @@ pub struct InspectorStackInner { /// Struct keeping mutable references to both parts of [InspectorStack] and implementing /// [revm::Inspector]. This struct can be obtained via [InspectorStack::as_mut] or via /// [CheatcodesExecutor::get_inspector] method implemented for [InspectorStackInner]. -struct InspectorStackRefMut<'a> { +pub struct InspectorStackRefMut<'a> { pub cheatcodes: Option<&'a mut Cheatcodes>, pub inner: &'a mut InspectorStackInner, } From ab0f3863a2e81ce681649fa9c583ee9c5a5a7c80 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 23 Jun 2024 09:05:44 +0300 Subject: [PATCH 18/26] chore: fmt --- crates/cheatcodes/src/inspector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 6b8573aa3e445..283a45654b69e 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -34,7 +34,7 @@ use revm::{ InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, primitives::{BlockEnv, CreateScheme, EVMError}, - EvmContext, InnerEvmContext + EvmContext, InnerEvmContext, }; use rustc_hash::FxHashMap; use serde_json::Value; From ad63dbc4ce211ddcfbeb87cce59ca6b1f5cc1495 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 23 Jun 2024 09:07:52 +0300 Subject: [PATCH 19/26] fix: doc --- crates/evm/evm/src/inspectors/stack.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 934d6f12f98ac..1b0b3e8ca9a89 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -272,9 +272,8 @@ pub struct InnerContextData { /// If a call to an inspector returns a value other than [InstructionResult::Continue] (or /// equivalent) the remaining inspectors are not called. /// -/// Stack is divided into [Cheatcodes] and [InspectorStackInner]. This is done to allow passing -/// mutable reference to [InspectorStackInner] into [Cheatcodes] functions to reassemble them into -/// [InspectorStackRefMut] and allow usage of it as [revm::Inspector]. +/// Stack is divided into [Cheatcodes] and `InspectorStackInner``. This is done to allow assembling +/// `InspectorStackRefMut` inside [Cheatcodes] to allow usage of it as [revm::Inspector]. #[derive(Clone, Debug, Default)] pub struct InspectorStack { pub cheatcodes: Option, From b78e28a008e2853af5362b4fcee9380f4c226a43 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 23 Jun 2024 09:12:57 +0300 Subject: [PATCH 20/26] fix: doc --- crates/evm/evm/src/inspectors/stack.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 1b0b3e8ca9a89..129cc5abbcf1e 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -272,7 +272,7 @@ pub struct InnerContextData { /// If a call to an inspector returns a value other than [InstructionResult::Continue] (or /// equivalent) the remaining inspectors are not called. /// -/// Stack is divided into [Cheatcodes] and `InspectorStackInner``. This is done to allow assembling +/// Stack is divided into [Cheatcodes] and `InspectorStackInner`. This is done to allow assembling /// `InspectorStackRefMut` inside [Cheatcodes] to allow usage of it as [revm::Inspector]. #[derive(Clone, Debug, Default)] pub struct InspectorStack { From d4c7270b76d3690a8dc654540bace2a40e3eac02 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 23 Jun 2024 23:52:38 +0300 Subject: [PATCH 21/26] increase depth for failing test --- crates/forge/tests/it/invariant.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 7712aa9a6f2a5..537569745aaf4 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -318,7 +318,7 @@ async fn test_shrink_big_sequence() { let mut runner = TEST_DATA_DEFAULT.runner(); runner.test_options.fuzz.seed = Some(U256::from(119u32)); runner.test_options.invariant.runs = 1; - runner.test_options.invariant.depth = 500; + runner.test_options.invariant.depth = 1000; let initial_counterexample = runner .test_collect(&filter) From dbbe05920e4f50497d481dcb59ec687157bbd287 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 26 Jun 2024 12:48:23 +0400 Subject: [PATCH 22/26] review fixes --- crates/cheatcodes/src/inspector.rs | 161 ++++++++++++++----------- crates/evm/evm/src/inspectors/stack.rs | 5 +- 2 files changed, 93 insertions(+), 73 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 7f8692d762226..0e3fddb722f53 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -34,7 +34,7 @@ use revm::{ InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, primitives::{BlockEnv, CreateScheme, EVMError}, - EvmContext, InnerEvmContext, + EvmContext, InnerEvmContext, Inspector, }; use rustc_hash::FxHashMap; use serde_json::Value; @@ -46,6 +46,7 @@ use std::{ path::PathBuf, sync::Arc, }; + /// Helper trait for obtaining complete [revm::Inspector] instance from mutable reference to /// [Cheatcodes]. /// @@ -111,6 +112,20 @@ pub trait CheatcodesExecutor { } } +/// Basic implementation of [CheatcodesExecutor] that simply returns the [Cheatcodes] instance as an +/// inspector. +#[derive(Debug, Default, Clone, Copy)] +struct TransparentCheatcodesExecutor; + +impl CheatcodesExecutor for TransparentCheatcodesExecutor { + fn get_inspector<'a, DB: DatabaseExt>( + &'a mut self, + cheats: &'a mut Cheatcodes, + ) -> impl InspectorExt + 'a { + cheats + } +} + macro_rules! try_or_return { ($e:expr) => { match $e { @@ -414,73 +429,6 @@ impl Cheatcodes { } } } -} - -impl Cheatcodes { - #[inline] - pub fn initialize_interp( - &mut self, - _: &mut Interpreter, - ecx: &mut EvmContext, - ) { - // When the first interpreter is initialized we've circumvented the balance and gas checks, - // so we apply our actual block data with the correct fees and all. - if let Some(block) = self.block.take() { - ecx.env.block = block; - } - if let Some(gas_price) = self.gas_price.take() { - ecx.env.tx.gas_price = gas_price; - } - } - - #[inline] - pub fn step( - &mut self, - interpreter: &mut Interpreter, - ecx: &mut EvmContext, - ) { - self.pc = interpreter.program_counter(); - - // `pauseGasMetering`: reset interpreter gas. - if self.gas_metering.is_some() { - self.meter_gas(interpreter); - } - - // `record`: record storage reads and writes. - if self.accesses.is_some() { - self.record_accesses(interpreter); - } - - // `startStateDiffRecording`: record granular ordered storage accesses. - if self.recorded_account_diffs_stack.is_some() { - self.record_state_diffs(interpreter, ecx); - } - - // `expectSafeMemory`: check if the current opcode is allowed to interact with memory. - if !self.allowed_mem_writes.is_empty() { - self.check_mem_opcodes(interpreter, ecx.journaled_state.depth()); - } - - // `startMappingRecording`: record SSTORE and KECCAK256. - if let Some(mapping_slots) = &mut self.mapping_slots { - mapping::step(mapping_slots, interpreter); - } - } - - pub fn log(&mut self, _context: &mut EvmContext, log: &Log) { - if !self.expected_emits.is_empty() { - expect::handle_expect_emit(self, log); - } - - // `recordLogs` - if let Some(storage_recorded_logs) = &mut self.recorded_logs { - storage_recorded_logs.push(Vm::Log { - topics: log.data.topics().to_vec(), - data: log.data.data.clone(), - emitter: log.address, - }); - } - } pub fn call( &mut self, @@ -739,8 +687,75 @@ impl Cheatcodes { None } +} + +impl Inspector for Cheatcodes { + #[inline] + fn initialize_interp(&mut self, _: &mut Interpreter, ecx: &mut EvmContext) { + // When the first interpreter is initialized we've circumvented the balance and gas checks, + // so we apply our actual block data with the correct fees and all. + if let Some(block) = self.block.take() { + ecx.env.block = block; + } + if let Some(gas_price) = self.gas_price.take() { + ecx.env.tx.gas_price = gas_price; + } + } - pub fn call_end( + #[inline] + fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + self.pc = interpreter.program_counter(); + + // `pauseGasMetering`: reset interpreter gas. + if self.gas_metering.is_some() { + self.meter_gas(interpreter); + } + + // `record`: record storage reads and writes. + if self.accesses.is_some() { + self.record_accesses(interpreter); + } + + // `startStateDiffRecording`: record granular ordered storage accesses. + if self.recorded_account_diffs_stack.is_some() { + self.record_state_diffs(interpreter, ecx); + } + + // `expectSafeMemory`: check if the current opcode is allowed to interact with memory. + if !self.allowed_mem_writes.is_empty() { + self.check_mem_opcodes(interpreter, ecx.journaled_state.depth()); + } + + // `startMappingRecording`: record SSTORE and KECCAK256. + if let Some(mapping_slots) = &mut self.mapping_slots { + mapping::step(mapping_slots, interpreter); + } + } + + fn log(&mut self, _context: &mut EvmContext, log: &Log) { + if !self.expected_emits.is_empty() { + expect::handle_expect_emit(self, log); + } + + // `recordLogs` + if let Some(storage_recorded_logs) = &mut self.recorded_logs { + storage_recorded_logs.push(Vm::Log { + topics: log.data.topics().to_vec(), + data: log.data.data.clone(), + emitter: log.address, + }); + } + } + + fn call( + &mut self, + context: &mut EvmContext, + inputs: &mut CallInputs, + ) -> Option { + Self::call(self, context, inputs, &mut TransparentCheatcodesExecutor) + } + + fn call_end( &mut self, ecx: &mut EvmContext, call: &CallInputs, @@ -1019,7 +1034,7 @@ impl Cheatcodes { outcome } - pub fn create( + fn create( &mut self, ecx: &mut EvmContext, call: &mut CreateInputs, @@ -1125,7 +1140,7 @@ impl Cheatcodes { None } - pub fn create_end( + fn create_end( &mut self, ecx: &mut EvmContext, _call: &CreateInputs, @@ -1235,8 +1250,10 @@ impl Cheatcodes { outcome } +} - pub fn should_use_create2_factory( +impl InspectorExt for Cheatcodes { + fn should_use_create2_factory( &mut self, ecx: &mut EvmContext, inputs: &mut CreateInputs, diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 129cc5abbcf1e..c5123b3993cae 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -273,7 +273,10 @@ pub struct InnerContextData { /// equivalent) the remaining inspectors are not called. /// /// Stack is divided into [Cheatcodes] and `InspectorStackInner`. This is done to allow assembling -/// `InspectorStackRefMut` inside [Cheatcodes] to allow usage of it as [revm::Inspector]. +/// `InspectorStackRefMut` inside [Cheatcodes] to allow usage of it as [revm::Inspector]. This gives +/// us ability to create and execute separate EVM frames from inside cheatcodes while still having +/// access to entire stack of inspectors and correctly handling traces, logs, debugging info +/// collection, etc. #[derive(Clone, Debug, Default)] pub struct InspectorStack { pub cheatcodes: Option, From f30eb018c8c86f4b7325f04515b8141558d30925 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 26 Jun 2024 12:50:54 +0400 Subject: [PATCH 23/26] reduce diff --- crates/cheatcodes/src/inspector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 0e3fddb722f53..185a9597dd02e 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -691,7 +691,7 @@ impl Cheatcodes { impl Inspector for Cheatcodes { #[inline] - fn initialize_interp(&mut self, _: &mut Interpreter, ecx: &mut EvmContext) { + fn initialize_interp(&mut self, _interpreter: &mut Interpreter, ecx: &mut EvmContext) { // When the first interpreter is initialized we've circumvented the balance and gas checks, // so we apply our actual block data with the correct fees and all. if let Some(block) = self.block.take() { From 5f31795bb757a060a75c1211a1449598e1125897 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 26 Jun 2024 14:33:47 +0400 Subject: [PATCH 24/26] rename --- crates/cheatcodes/src/evm.rs | 72 ++++++++++++++-------------- crates/cheatcodes/src/evm/fork.rs | 48 +++++++++---------- crates/cheatcodes/src/evm/mock.rs | 4 +- crates/cheatcodes/src/evm/prank.rs | 8 ++-- crates/cheatcodes/src/fs.rs | 4 +- crates/cheatcodes/src/inspector.rs | 2 +- crates/cheatcodes/src/lib.rs | 6 +-- crates/cheatcodes/src/script.rs | 14 +++--- crates/cheatcodes/src/test.rs | 6 +-- crates/cheatcodes/src/test/expect.rs | 26 +++++----- crates/cheatcodes/src/utils.rs | 6 +-- 11 files changed, 98 insertions(+), 98 deletions(-) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 50265b4b12e0d..4e559687dbc1e 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -67,14 +67,14 @@ impl Cheatcode for addrCall { } impl Cheatcode for getNonce_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; get_nonce(ccx, account) } } impl Cheatcode for loadCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target, slot } = *self; ensure_not_precompile!(&target, ccx); ccx.ecx.load_account(target)?; @@ -84,7 +84,7 @@ impl Cheatcode for loadCall { } impl Cheatcode for loadAllocsCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { pathToAllocsJson } = self; let path = Path::new(pathToAllocsJson); @@ -110,7 +110,7 @@ impl Cheatcode for loadAllocsCall { } impl Cheatcode for dumpStateCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { pathToStateJson } = self; let path = Path::new(pathToStateJson); @@ -156,28 +156,28 @@ impl Cheatcode for dumpStateCall { } impl Cheatcode for sign_0Call { - fn apply_full(&self, _: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, _: &mut CheatsCtxt) -> Result { let Self { privateKey, digest } = self; super::utils::sign(privateKey, digest) } } impl Cheatcode for sign_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { digest } = self; super::utils::sign_with_wallet(ccx, None, digest) } } impl Cheatcode for sign_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { signer, digest } = self; super::utils::sign_with_wallet(ccx, Some(*signer), digest) } } impl Cheatcode for signP256Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { privateKey, digest } = self; super::utils::sign_p256(privateKey, digest, ccx.state) } @@ -255,7 +255,7 @@ impl Cheatcode for lastCallGasCall { } impl Cheatcode for chainIdCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newChainId } = self; ensure!(*newChainId <= U256::from(u64::MAX), "chain ID must be less than 2^64 - 1"); ccx.ecx.env.cfg.chain_id = newChainId.to(); @@ -264,7 +264,7 @@ impl Cheatcode for chainIdCall { } impl Cheatcode for coinbaseCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newCoinbase } = self; ccx.ecx.env.block.coinbase = *newCoinbase; Ok(Default::default()) @@ -272,7 +272,7 @@ impl Cheatcode for coinbaseCall { } impl Cheatcode for difficultyCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newDifficulty } = self; ensure!( ccx.ecx.spec_id() < SpecId::MERGE, @@ -285,7 +285,7 @@ impl Cheatcode for difficultyCall { } impl Cheatcode for feeCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newBasefee } = self; ccx.ecx.env.block.basefee = *newBasefee; Ok(Default::default()) @@ -293,7 +293,7 @@ impl Cheatcode for feeCall { } impl Cheatcode for prevrandao_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newPrevrandao } = self; ensure!( ccx.ecx.spec_id() >= SpecId::MERGE, @@ -306,7 +306,7 @@ impl Cheatcode for prevrandao_0Call { } impl Cheatcode for prevrandao_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newPrevrandao } = self; ensure!( ccx.ecx.spec_id() >= SpecId::MERGE, @@ -319,7 +319,7 @@ impl Cheatcode for prevrandao_1Call { } impl Cheatcode for blobhashesCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { hashes } = self; ensure!( ccx.ecx.spec_id() >= SpecId::CANCUN, @@ -332,7 +332,7 @@ impl Cheatcode for blobhashesCall { } impl Cheatcode for getBlobhashesCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; ensure!( ccx.ecx.spec_id() >= SpecId::CANCUN, @@ -344,7 +344,7 @@ impl Cheatcode for getBlobhashesCall { } impl Cheatcode for rollCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newHeight } = self; ccx.ecx.env.block.number = *newHeight; Ok(Default::default()) @@ -352,14 +352,14 @@ impl Cheatcode for rollCall { } impl Cheatcode for getBlockNumberCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; Ok(ccx.ecx.env.block.number.abi_encode()) } } impl Cheatcode for txGasPriceCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newGasPrice } = self; ccx.ecx.env.tx.gas_price = *newGasPrice; Ok(Default::default()) @@ -367,7 +367,7 @@ impl Cheatcode for txGasPriceCall { } impl Cheatcode for warpCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newTimestamp } = self; ccx.ecx.env.block.timestamp = *newTimestamp; Ok(Default::default()) @@ -375,14 +375,14 @@ impl Cheatcode for warpCall { } impl Cheatcode for getBlockTimestampCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; Ok(ccx.ecx.env.block.timestamp.abi_encode()) } } impl Cheatcode for blobBaseFeeCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newBlobBaseFee } = self; ensure!( ccx.ecx.spec_id() >= SpecId::CANCUN, @@ -395,14 +395,14 @@ impl Cheatcode for blobBaseFeeCall { } impl Cheatcode for getBlobBaseFeeCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; Ok(ccx.ecx.env.block.get_blob_excess_gas().unwrap_or(0).abi_encode()) } } impl Cheatcode for dealCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account: address, newBalance: new_balance } = *self; let account = journaled_account(ccx.ecx, address)?; let old_balance = std::mem::replace(&mut account.info.balance, new_balance); @@ -413,7 +413,7 @@ impl Cheatcode for dealCall { } impl Cheatcode for etchCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target, newRuntimeBytecode } = self; ensure_not_precompile!(target, ccx); ccx.ecx.load_account(*target)?; @@ -424,7 +424,7 @@ impl Cheatcode for etchCall { } impl Cheatcode for resetNonceCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; let account = journaled_account(ccx.ecx, *account)?; // Per EIP-161, EOA nonces start at 0, but contract nonces @@ -439,7 +439,7 @@ impl Cheatcode for resetNonceCall { } impl Cheatcode for setNonceCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account, newNonce } = *self; let account = journaled_account(ccx.ecx, account)?; // nonce must increment only @@ -455,7 +455,7 @@ impl Cheatcode for setNonceCall { } impl Cheatcode for setNonceUnsafeCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account, newNonce } = *self; let account = journaled_account(ccx.ecx, account)?; account.info.nonce = newNonce; @@ -464,7 +464,7 @@ impl Cheatcode for setNonceUnsafeCall { } impl Cheatcode for storeCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target, slot, value } = *self; ensure_not_precompile!(&target, ccx); // ensure the account is touched @@ -475,7 +475,7 @@ impl Cheatcode for storeCall { } impl Cheatcode for coolCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target } = self; if let Some(account) = ccx.ecx.journaled_state.state.get_mut(target) { account.unmark_touch(); @@ -486,21 +486,21 @@ impl Cheatcode for coolCall { } impl Cheatcode for readCallersCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; read_callers(ccx.state, &ccx.ecx.env.tx.caller) } } impl Cheatcode for snapshotCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; Ok(ccx.ecx.db.snapshot(&ccx.ecx.journaled_state, &ccx.ecx.env).abi_encode()) } } impl Cheatcode for revertToCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; let result = if let Some(journaled_state) = ccx.ecx.db.revert( *snapshotId, @@ -519,7 +519,7 @@ impl Cheatcode for revertToCall { } impl Cheatcode for revertToAndDeleteCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; let result = if let Some(journaled_state) = ccx.ecx.db.revert( *snapshotId, @@ -538,14 +538,14 @@ impl Cheatcode for revertToAndDeleteCall { } impl Cheatcode for deleteSnapshotCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; let result = ccx.ecx.db.delete_snapshot(*snapshotId); Ok(result.abi_encode()) } } impl Cheatcode for deleteSnapshotsCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; ccx.ecx.db.delete_snapshots(); Ok(Default::default()) diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index 9be68d9939053..70b7591f8a82e 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -7,7 +7,7 @@ use foundry_common::provider::ProviderBuilder; use foundry_evm_core::fork::CreateFork; impl Cheatcode for activeForkCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; ccx.ecx .db @@ -18,49 +18,49 @@ impl Cheatcode for activeForkCall { } impl Cheatcode for createFork_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias } = self; create_fork(ccx, urlOrAlias, None) } } impl Cheatcode for createFork_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias, blockNumber } = self; create_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to())) } } impl Cheatcode for createFork_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias, txHash } = self; create_fork_at_transaction(ccx, urlOrAlias, txHash) } } impl Cheatcode for createSelectFork_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias } = self; create_select_fork(ccx, urlOrAlias, None) } } impl Cheatcode for createSelectFork_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias, blockNumber } = self; create_select_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to())) } } impl Cheatcode for createSelectFork_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias, txHash } = self; create_select_fork_at_transaction(ccx, urlOrAlias, txHash) } } impl Cheatcode for rollFork_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { blockNumber } = self; persist_caller(ccx); ccx.ecx.db.roll_fork( @@ -74,7 +74,7 @@ impl Cheatcode for rollFork_0Call { } impl Cheatcode for rollFork_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { txHash } = self; persist_caller(ccx); ccx.ecx.db.roll_fork_to_transaction( @@ -88,7 +88,7 @@ impl Cheatcode for rollFork_1Call { } impl Cheatcode for rollFork_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, blockNumber } = self; persist_caller(ccx); ccx.ecx.db.roll_fork( @@ -102,7 +102,7 @@ impl Cheatcode for rollFork_2Call { } impl Cheatcode for rollFork_3Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, txHash } = self; persist_caller(ccx); ccx.ecx.db.roll_fork_to_transaction( @@ -116,7 +116,7 @@ impl Cheatcode for rollFork_3Call { } impl Cheatcode for selectForkCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId } = self; persist_caller(ccx); check_broadcast(ccx.state)?; @@ -127,7 +127,7 @@ impl Cheatcode for selectForkCall { } impl Cheatcode for transact_0Call { - fn apply_full_with_executor( + fn apply_full( &self, ccx: &mut CheatsCtxt, executor: &mut E, @@ -145,7 +145,7 @@ impl Cheatcode for transact_0Call { } impl Cheatcode for transact_1Call { - fn apply_full_with_executor( + fn apply_full( &self, ccx: &mut CheatsCtxt, executor: &mut E, @@ -163,7 +163,7 @@ impl Cheatcode for transact_1Call { } impl Cheatcode for allowCheatcodesCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; ccx.ecx.db.allow_cheatcode_access(*account); Ok(Default::default()) @@ -171,7 +171,7 @@ impl Cheatcode for allowCheatcodesCall { } impl Cheatcode for makePersistent_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; ccx.ecx.db.add_persistent_account(*account); Ok(Default::default()) @@ -179,7 +179,7 @@ impl Cheatcode for makePersistent_0Call { } impl Cheatcode for makePersistent_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account0, account1 } = self; ccx.ecx.db.add_persistent_account(*account0); ccx.ecx.db.add_persistent_account(*account1); @@ -188,7 +188,7 @@ impl Cheatcode for makePersistent_1Call { } impl Cheatcode for makePersistent_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account0, account1, account2 } = self; ccx.ecx.db.add_persistent_account(*account0); ccx.ecx.db.add_persistent_account(*account1); @@ -198,7 +198,7 @@ impl Cheatcode for makePersistent_2Call { } impl Cheatcode for makePersistent_3Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { accounts } = self; for account in accounts { ccx.ecx.db.add_persistent_account(*account); @@ -208,7 +208,7 @@ impl Cheatcode for makePersistent_3Call { } impl Cheatcode for revokePersistent_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; ccx.ecx.db.remove_persistent_account(account); Ok(Default::default()) @@ -216,7 +216,7 @@ impl Cheatcode for revokePersistent_0Call { } impl Cheatcode for revokePersistent_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { accounts } = self; for account in accounts { ccx.ecx.db.remove_persistent_account(account); @@ -226,14 +226,14 @@ impl Cheatcode for revokePersistent_1Call { } impl Cheatcode for isPersistentCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; Ok(ccx.ecx.db.is_persistent(account).abi_encode()) } } impl Cheatcode for rpcCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { method, params } = self; let url = ccx.ecx.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; @@ -251,7 +251,7 @@ impl Cheatcode for rpcCall { } impl Cheatcode for eth_getLogsCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { fromBlock, toBlock, target, topics } = self; let (Ok(from_block), Ok(to_block)) = (u64::try_from(fromBlock), u64::try_from(toBlock)) else { diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index becf86f178f54..0949cbf4f973d 100644 --- a/crates/cheatcodes/src/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -47,7 +47,7 @@ impl Cheatcode for clearMockedCallsCall { } impl Cheatcode for mockCall_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, data, returnData } = self; let (acc, _) = ccx.ecx.load_account(*callee)?; @@ -65,7 +65,7 @@ impl Cheatcode for mockCall_0Call { } impl Cheatcode for mockCall_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, msgValue, data, returnData } = self; ccx.ecx.load_account(*callee)?; mock_call(ccx.state, callee, data, Some(msgValue), returnData, InstructionResult::Return); diff --git a/crates/cheatcodes/src/evm/prank.rs b/crates/cheatcodes/src/evm/prank.rs index 4e4ef81f759f3..fe5418b3157f8 100644 --- a/crates/cheatcodes/src/evm/prank.rs +++ b/crates/cheatcodes/src/evm/prank.rs @@ -45,28 +45,28 @@ impl Prank { } impl Cheatcode for prank_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender } = self; prank(ccx, msgSender, None, true) } } impl Cheatcode for startPrank_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender } = self; prank(ccx, msgSender, None, false) } } impl Cheatcode for prank_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender, txOrigin } = self; prank(ccx, msgSender, Some(txOrigin), true) } } impl Cheatcode for startPrank_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender, txOrigin } = self; prank(ccx, msgSender, Some(txOrigin), false) } diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 1f0db75262a1e..045ebea27e45f 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -265,7 +265,7 @@ impl Cheatcode for getDeployedCodeCall { } impl Cheatcode for deployCode_0Call { - fn apply_full_with_executor( + fn apply_full( &self, ccx: &mut CheatsCtxt, executor: &mut E, @@ -291,7 +291,7 @@ impl Cheatcode for deployCode_0Call { } impl Cheatcode for deployCode_1Call { - fn apply_full_with_executor( + fn apply_full( &self, ccx: &mut CheatsCtxt, executor: &mut E, diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 185a9597dd02e..ac32a410cceeb 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -1783,7 +1783,7 @@ fn apply_dispatch( macro_rules! dispatch { ($($variant:ident),*) => { match calls { - $(Vm::VmCalls::$variant(cheat) => crate::Cheatcode::apply_full_with_executor(cheat, ccx, executor),)* + $(Vm::VmCalls::$variant(cheat) => crate::Cheatcode::apply_full(cheat, ccx, executor),)* } }; } diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 40adf89736a98..85ce8d78c51d8 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -67,7 +67,7 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { /// /// Implement this function if you need access to the EVM data. #[inline(always)] - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { self.apply(ccx.state) } @@ -75,12 +75,12 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { /// /// Implement this function if you need access to the executor. #[inline(always)] - fn apply_full_with_executor( + fn apply_full( &self, ccx: &mut CheatsCtxt, _executor: &mut E, ) -> Result { - self.apply_full(ccx) + self.apply_stateful(ccx) } } diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 1f84e2475f815..af4457f8edb10 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -8,49 +8,49 @@ use parking_lot::Mutex; use std::sync::Arc; impl Cheatcode for broadcast_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; broadcast(ccx, None, true) } } impl Cheatcode for broadcast_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { signer } = self; broadcast(ccx, Some(signer), true) } } impl Cheatcode for broadcast_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { privateKey } = self; broadcast_key(ccx, privateKey, true) } } impl Cheatcode for startBroadcast_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; broadcast(ccx, None, false) } } impl Cheatcode for startBroadcast_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { signer } = self; broadcast(ccx, Some(signer), false) } } impl Cheatcode for startBroadcast_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { privateKey } = self; broadcast_key(ccx, privateKey, false) } } impl Cheatcode for stopBroadcastCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; let Some(broadcast) = ccx.state.broadcast.take() else { bail!("no broadcast in progress to stop"); diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index cab8b9f8b430d..4bccabda253e2 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -20,14 +20,14 @@ impl Cheatcode for assumeCall { } impl Cheatcode for breakpoint_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { char } = self; breakpoint(ccx.state, &ccx.caller, char, true) } } impl Cheatcode for breakpoint_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { char, value } = self; breakpoint(ccx.state, &ccx.caller, char, *value) } @@ -64,7 +64,7 @@ impl Cheatcode for sleepCall { } impl Cheatcode for skipCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { skipTest } = *self; if skipTest { // Skip should not work if called deeper than at test level. diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index eba665856b117..9c070a7ca8874 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -197,7 +197,7 @@ impl Cheatcode for expectCallMinGas_1Call { } impl Cheatcode for expectEmit_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self; expect_emit( ccx.state, @@ -209,7 +209,7 @@ impl Cheatcode for expectEmit_0Call { } impl Cheatcode for expectEmit_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self; expect_emit( ccx.state, @@ -221,69 +221,69 @@ impl Cheatcode for expectEmit_1Call { } impl Cheatcode for expectEmit_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 4], None) } } impl Cheatcode for expectEmit_3Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { emitter } = *self; expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 4], Some(emitter)) } } impl Cheatcode for expectRevert_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false) } } impl Cheatcode for expectRevert_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; expect_revert(ccx.state, Some(revertData.as_ref()), ccx.ecx.journaled_state.depth(), false) } } impl Cheatcode for expectRevert_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; expect_revert(ccx.state, Some(revertData), ccx.ecx.journaled_state.depth(), false) } } impl Cheatcode for _expectCheatcodeRevert_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true) } } impl Cheatcode for _expectCheatcodeRevert_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; expect_revert(ccx.state, Some(revertData.as_ref()), ccx.ecx.journaled_state.depth(), true) } } impl Cheatcode for _expectCheatcodeRevert_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; expect_revert(ccx.state, Some(revertData), ccx.ecx.journaled_state.depth(), true) } } impl Cheatcode for expectSafeMemoryCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { min, max } = *self; expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth()) } } impl Cheatcode for stopExpectSafeMemoryCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; ccx.state.allowed_mem_writes.remove(&ccx.ecx.journaled_state.depth()); Ok(Default::default()) @@ -291,7 +291,7 @@ impl Cheatcode for stopExpectSafeMemoryCall { } impl Cheatcode for expectSafeMemoryCallCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { min, max } = *self; expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth() + 1) } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 733ed36837279..8bea510eb9a7c 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -46,14 +46,14 @@ impl Cheatcode for createWallet_2Call { } impl Cheatcode for getNonce_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { wallet } = self; super::evm::get_nonce(ccx, &wallet.addr) } } impl Cheatcode for sign_3Call { - fn apply_full(&self, _: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, _: &mut CheatsCtxt) -> Result { let Self { wallet, digest } = self; sign(&wallet.privateKey, digest) } @@ -88,7 +88,7 @@ impl Cheatcode for deriveKey_3Call { } impl Cheatcode for rememberKeyCall { - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { privateKey } = self; let wallet = parse_wallet(privateKey)?; let address = wallet.address(); From efe3b71ecff365489d7e7df4a994e2a45f2dffd7 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 26 Jun 2024 15:03:32 +0400 Subject: [PATCH 25/26] call_with_executor --- crates/cheatcodes/src/inspector.rs | 4 ++-- crates/evm/evm/src/inspectors/stack.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index ac32a410cceeb..300ada83d7c10 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -430,7 +430,7 @@ impl Cheatcodes { } } - pub fn call( + pub fn call_with_executor( &mut self, ecx: &mut EvmContext, call: &mut CallInputs, @@ -752,7 +752,7 @@ impl Inspector for Cheatcodes { context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option { - Self::call(self, context, inputs, &mut TransparentCheatcodesExecutor) + Self::call_with_executor(self, context, inputs, &mut TransparentCheatcodesExecutor) } fn call_end( diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index c5123b3993cae..e290a55fc7c15 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -722,7 +722,7 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { ); if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() { - if let Some(output) = cheatcodes.call(ecx, call, self.inner) { + if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) { if output.result.result != InstructionResult::Continue { return Some(output) } From 84032392cac07813b89b4c48e4ac1b8f7cd2b762 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 26 Jun 2024 20:17:56 +0200 Subject: [PATCH 26/26] chore: keep dbext methods with auto_impl attribute --- crates/evm/core/src/backend/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index fd58e722a45a7..04ce60c027385 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -275,6 +275,28 @@ pub trait DatabaseExt: Database + DatabaseCommit { /// Marks the given account as persistent. fn add_persistent_account(&mut self, account: Address) -> bool; + /// Removes persistent status from all given accounts. + #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))] + fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator) + where + Self: Sized, + { + for acc in accounts { + self.remove_persistent_account(&acc); + } + } + + /// Extends the persistent accounts with the accounts the iterator yields. + #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))] + fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator) + where + Self: Sized, + { + for acc in accounts { + self.add_persistent_account(acc); + } + } + /// Grants cheatcode access for the given `account` /// /// Returns true if the `account` already has access