diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index d2ad6e270..a0178a9d2 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -178,15 +178,8 @@ impl CallArgs { env.cfg.disable_block_gas_limit = true; env.block.gas_limit = U256::MAX; - let mut executor = TracingExecutor::new( - env, - fork, - evm_version, - debug, - decode_internal, - alphanet, - strategy, - ); + let mut executor = + TracingExecutor::new(env, fork, evm_version, debug, decode_internal, alphanet); let value = tx.value.unwrap_or_default(); let input = tx.inner.input.into_input().unwrap_or_default(); diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index ebb201282..d0d76d1ff 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -167,7 +167,6 @@ impl RunArgs { self.debug, self.decode_internal, alphanet, - strategy, ); let mut env = EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), executor.spec_id()); diff --git a/crates/cheatcodes/src/strategy.rs b/crates/cheatcodes/src/strategy.rs index 173457ca4..b35120566 100644 --- a/crates/cheatcodes/src/strategy.rs +++ b/crates/cheatcodes/src/strategy.rs @@ -247,7 +247,7 @@ pub trait CheatcodeInspectorStrategyExt: CheatcodeInspectorStrategy { } #[derive(Debug, Default, Clone)] -pub struct EvmCheatcodeInspectorStrategy {} +pub struct EvmCheatcodeInspectorStrategy; impl CheatcodeInspectorStrategy for EvmCheatcodeInspectorStrategy { fn name(&self) -> &'static str { diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 7a72c111e..1b8d89414 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -13,8 +13,11 @@ use eyre::{Result, WrapErr}; use foundry_cli::utils; use foundry_compilers::Artifact; use foundry_evm::{ - backend::Backend, decode::decode_console_logs, executors::ExecutorBuilder, - inspectors::CheatsConfig, traces::TraceMode, + backend::Backend, + decode::decode_console_logs, + executors::ExecutorBuilder, + inspectors::{cheatcodes::strategy::EvmCheatcodeInspectorStrategy, CheatsConfig}, + traces::TraceMode, }; use solang_parser::pt::{self, CodeLocation}; use std::str::FromStr; @@ -323,7 +326,7 @@ impl SessionSource { Some(backend) => backend, None => { let fork = self.config.evm_opts.get_fork(&self.config.foundry_config, env.clone()); - let backend = Backend::spawn(fork, strategy.new_backend_strategy()); + let backend = Backend::spawn(fork, ()); self.config.backend = Some(backend.clone()); backend } @@ -339,7 +342,7 @@ impl SessionSource { None, None, Some(self.solc.version.clone()), - strategy.new_cheatcode_inspector_strategy(), + Box::new(EvmCheatcodeInspectorStrategy), ) .into(), ) @@ -347,7 +350,7 @@ impl SessionSource { .gas_limit(self.config.evm_opts.gas_limit()) .spec(self.config.foundry_config.evm_spec_id()) .legacy_assertions(self.config.foundry_config.legacy_assertions) - .build(env, backend, strategy); + .build(env, backend, ()); // Create a [ChiselRunner] with a default balance of [U256::MAX] and // the sender [Address::zero]. diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 5e4fd633c..5b0781693 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -9,7 +9,7 @@ use foundry_common::{ shell, }; use foundry_config::{Chain, Config}; -use foundry_evm::executors::strategy::{EvmExecutorStrategy, ExecutorStrategyExt}; +use foundry_evm::executors::strategy::EvmExecutorStrategy; use foundry_strategy_zksync::ZksyncExecutorStrategy; use serde::de::DeserializeOwned; use std::{ @@ -93,13 +93,19 @@ pub fn get_provider(config: &Config) -> Result { get_provider_builder(config)?.build() } -pub fn get_executor_strategy(config: &Config) -> Box { +#[derive(Debug, Clone, Copy)] +pub enum ExecutorStrategy { + Evm, + Zksync, +} + +pub fn get_executor_strategy(config: &Config) -> ExecutorStrategy { if config.zksync.should_compile() { info!("using zksync strategy"); - Box::new(ZksyncExecutorStrategy::default()) + ExecutorStrategy::Zksync } else { info!("using evm strategy"); - Box::new(EvmExecutorStrategy::default()) + ExecutorStrategy::Evm } } diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index 2f7b7bd90..4be5b5e09 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -1,6 +1,6 @@ //! A wrapper around `Backend` that is clone-on-write used for fuzzing. -use super::{strategy::BackendStrategyExt, BackendError, ForkInfo}; +use super::{strategy::BackendStrategy, BackendError, ForkInfo}; use crate::{ backend::{ diagnostic::RevertDiagnostic, Backend, DatabaseExt, LocalForkId, RevertStateSnapshotAction, @@ -36,24 +36,24 @@ use std::{borrow::Cow, collections::BTreeMap}; /// which would add significant overhead for large fuzz sets even if the Database is not big after /// setup. #[derive(Clone, Debug)] -pub struct CowBackend<'a> { +pub struct CowBackend<'a, S: BackendStrategy> { /// The underlying `Backend`. /// /// No calls on the `CowBackend` will ever persistently modify the `backend`'s state. - pub backend: Cow<'a, Backend>, + pub backend: Cow<'a, Backend>, /// Keeps track of whether the backed is already initialized pub is_initialized: bool, /// The [SpecId] of the current backend. pub spec_id: SpecId, } -impl<'a> CowBackend<'a> { +impl<'a, S: BackendStrategy> CowBackend<'a, S> { /// Creates a new `CowBackend` with the given `Backend`. - pub fn new(backend: &'a Backend) -> Self { + pub fn new(backend: &'a Backend) -> Self { Self { backend: Cow::Borrowed(backend), is_initialized: false, spec_id: SpecId::LATEST } } - pub fn new_borrowed(backend: &'a Backend) -> Self { + pub fn new_borrowed(backend: &'a Backend) -> Self { Self { backend: Cow::Borrowed(backend), is_initialized: false, spec_id: SpecId::LATEST } } @@ -67,7 +67,7 @@ impl<'a> CowBackend<'a> { /// Returns a mutable instance of the Backend. /// /// If this is the first time this is called, the backed is cloned and initialized. - fn backend_mut(&mut self, env: &Env) -> &mut Backend { + fn backend_mut(&mut self, env: &Env) -> &mut Backend { if !self.is_initialized { let backend = self.backend.to_mut(); let env = EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), self.spec_id); @@ -79,7 +79,7 @@ impl<'a> CowBackend<'a> { } /// Returns a mutable instance of the Backend if it is initialized. - fn initialized_backend_mut(&mut self) -> Option<&mut Backend> { + fn initialized_backend_mut(&mut self) -> Option<&mut Backend> { if self.is_initialized { return Some(self.backend.to_mut()) } @@ -87,15 +87,11 @@ impl<'a> CowBackend<'a> { } } -impl DatabaseExt for CowBackend<'_> { +impl DatabaseExt for CowBackend<'_, S> { fn get_fork_info(&mut self, id: LocalForkId) -> eyre::Result { self.backend.to_mut().get_fork_info(id) } - fn get_strategy(&mut self) -> &mut dyn BackendStrategyExt { - self.backend.to_mut().strategy.as_mut() - } - fn snapshot_state(&mut self, journaled_state: &JournaledState, env: &Env) -> U256 { self.backend_mut(env).snapshot_state(journaled_state, env) } @@ -262,9 +258,30 @@ impl DatabaseExt for CowBackend<'_> { fn get_test_contract_address(&self) -> Option
{ self.backend.get_test_contract_address() } + + fn insert_account_info(&mut self, address: Address, account: AccountInfo) { + self.backend.to_mut().insert_account_info(address, account); + } + + fn insert_account_storage( + &mut self, + address: Address, + slot: U256, + value: U256, + ) -> Result<(), DatabaseError> { + self.backend.to_mut().insert_account_storage(address, slot, value) + } + + fn replace_account_storage( + &mut self, + address: Address, + storage: Map, + ) -> Result<(), DatabaseError> { + self.backend.to_mut().replace_account_storage(address, storage) + } } -impl DatabaseRef for CowBackend<'_> { +impl DatabaseRef for CowBackend<'_, S> { type Error = DatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { @@ -284,7 +301,7 @@ impl DatabaseRef for CowBackend<'_> { } } -impl Database for CowBackend<'_> { +impl Database for CowBackend<'_, S> { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { @@ -304,7 +321,7 @@ impl Database for CowBackend<'_> { } } -impl DatabaseCommit for CowBackend<'_> { +impl DatabaseCommit for CowBackend<'_, S> { fn commit(&mut self, changes: Map) { self.backend.to_mut().commit(changes) } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 9c4aba6f9..15a7ba901 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -30,7 +30,7 @@ use std::{ collections::{BTreeMap, HashSet}, time::Instant, }; -use strategy::{BackendStrategyExt, BackendStrategyForkInfo}; +use strategy::{BackendStrategy, BackendStrategyForkInfo, EvmBackendStrategy}; mod diagnostic; pub use diagnostic::RevertDiagnostic; @@ -86,7 +86,9 @@ pub struct ForkInfo { /// An extension trait that allows us to easily extend the `revm::Inspector` capabilities #[auto_impl::auto_impl(&mut)] -pub trait DatabaseExt: Database + DatabaseCommit { +pub trait DatabaseExt: + Database + DatabaseCommit + DatabaseRef +{ /// Creates a new state snapshot at the current point of execution. /// /// A state snapshot is associated with a new unique id that's created for the snapshot. @@ -101,9 +103,6 @@ pub trait DatabaseExt: Database + DatabaseCommit { /// and the the fork environment. fn get_fork_info(&mut self, id: LocalForkId) -> eyre::Result; - /// Retrieve the strategy. - fn get_strategy(&mut self) -> &mut dyn BackendStrategyExt; - /// Reverts the snapshot if it exists /// /// Returns `true` if the snapshot was successfully reverted, `false` if no snapshot for that id @@ -400,6 +399,26 @@ pub trait DatabaseExt: Database + DatabaseCommit { /// - Setting a blockhash for future blocks (number > block.number) has no effect /// - Setting a blockhash for blocks older than `block.number - 256` has no effect fn set_blockhash(&mut self, block_number: U256, block_hash: B256); + + fn insert_account_info(&mut self, address: Address, account: AccountInfo); + + /// Inserts a value on an account's storage without overriding account info + fn insert_account_storage( + &mut self, + address: Address, + slot: U256, + value: U256, + ) -> Result<(), DatabaseError>; + + /// Completely replace an account's storage without overriding account info. + /// + /// When forking, this causes the backend to assume a `0` value for all + /// unset storage slots instead of trying to fetch it. + fn replace_account_storage( + &mut self, + address: Address, + storage: Map, + ) -> Result<(), DatabaseError>; } struct _ObjectSafe(dyn DatabaseExt); @@ -458,10 +477,7 @@ struct _ObjectSafe(dyn DatabaseExt); /// after reverting the snapshot. #[derive(Debug)] #[must_use] -pub struct Backend { - /// The behavior strategy. - pub strategy: Box, - +pub struct Backend { /// The access point for managing forks forks: MultiFork, // The default in memory db @@ -491,29 +507,31 @@ pub struct Backend { inner: BackendInner, /// Keeps track of the fork type fork_url_type: CachedForkType, + + extra_ctx: S::BackendContext, } -impl Clone for Backend { +impl Clone for Backend { fn clone(&self) -> Self { Self { - strategy: self.strategy.new_cloned_ext(), forks: self.forks.clone(), mem_db: self.mem_db.clone(), fork_init_journaled_state: self.fork_init_journaled_state.clone(), active_fork_ids: self.active_fork_ids, inner: self.inner.clone(), fork_url_type: self.fork_url_type.clone(), + extra_ctx: self.extra_ctx.clone(), } } } -impl Backend { +impl Backend { /// Creates a new Backend with a spawned multi fork thread. /// /// If `fork` is `Some` this will use a `fork` database, otherwise with an in-memory /// database. - pub fn spawn(fork: Option, strategy: Box) -> Self { - Self::new(MultiFork::spawn(), fork, strategy) + pub fn spawn(fork: Option, extra_ctx: S::BackendContext) -> Self { + Self::new(MultiFork::spawn(), fork, extra_ctx) } /// Creates a new instance of `Backend` @@ -522,11 +540,7 @@ impl Backend { /// database. /// /// Prefer using [`spawn`](Self::spawn) instead. - pub fn new( - forks: MultiFork, - fork: Option, - strategy: Box, - ) -> Self { + pub fn new(forks: MultiFork, fork: Option, extra_ctx: S::BackendContext) -> Self { trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend"); // Note: this will take of registering the `fork` let inner = BackendInner { @@ -541,7 +555,7 @@ impl Backend { active_fork_ids: None, inner, fork_url_type: Default::default(), - strategy, + extra_ctx, }; if let Some(fork) = fork { @@ -568,9 +582,9 @@ impl Backend { id: &ForkId, fork: Fork, journaled_state: JournaledState, - strategy: Box, + extra_ctx: S::BackendContext, ) -> Self { - let mut backend = Self::spawn(None, strategy); + let mut backend = Self::spawn(None, extra_ctx); let fork_ids = backend.inner.insert_new_fork(id.clone(), fork.db, journaled_state); backend.inner.launched_with_fork = Some((id.clone(), fork_ids.0, fork_ids.1)); backend.active_fork_ids = Some(fork_ids); @@ -586,45 +600,7 @@ impl Backend { active_fork_ids: None, inner: Default::default(), fork_url_type: Default::default(), - strategy: self.strategy.new_cloned_ext(), - } - } - - pub fn insert_account_info(&mut self, address: Address, account: AccountInfo) { - if let Some(db) = self.active_fork_db_mut() { - db.insert_account_info(address, account) - } else { - self.mem_db.insert_account_info(address, account) - } - } - - /// Inserts a value on an account's storage without overriding account info - pub fn insert_account_storage( - &mut self, - address: Address, - slot: U256, - value: U256, - ) -> Result<(), DatabaseError> { - if let Some(db) = self.active_fork_db_mut() { - db.insert_account_storage(address, slot, value) - } else { - self.mem_db.insert_account_storage(address, slot, value) - } - } - - /// Completely replace an account's storage without overriding account info. - /// - /// When forking, this causes the backend to assume a `0` value for all - /// unset storage slots instead of trying to fetch it. - pub fn replace_account_storage( - &mut self, - address: Address, - storage: Map, - ) -> Result<(), DatabaseError> { - if let Some(db) = self.active_fork_db_mut() { - db.replace_account_storage(address, storage) - } else { - self.mem_db.replace_account_storage(address, storage) + extra_ctx: Default::default(), } } @@ -906,7 +882,7 @@ impl Backend { } trace!(tx=?tx.tx_hash(), "committing transaction"); - commit_transaction( + commit_transaction::( &tx.inner, env.clone(), journaled_state, @@ -914,7 +890,7 @@ impl Backend { &fork_id, &persistent_accounts, &mut NoOpInspector, - self.strategy.as_mut(), + &mut self.extra_ctx, )?; } @@ -922,7 +898,7 @@ impl Backend { } } -impl DatabaseExt for Backend { +impl DatabaseExt for Backend { fn get_fork_info(&mut self, id: LocalForkId) -> eyre::Result { let fork_id = self.ensure_fork_id(id).cloned()?; let fork_env = self @@ -938,10 +914,6 @@ impl DatabaseExt for Backend { Ok(ForkInfo { fork_type, fork_env }) } - fn get_strategy(&mut self) -> &mut dyn BackendStrategyExt { - self.strategy.as_mut() - } - fn snapshot_state(&mut self, journaled_state: &JournaledState, env: &Env) -> U256 { trace!("create snapshot"); let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new( @@ -1169,9 +1141,10 @@ impl DatabaseExt for Backend { caller_account.into() }); - self.strategy.update_fork_db( + let active_fork = self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx)); + S::update_fork_db( BackendStrategyForkInfo { - active_fork: self.active_fork(), + active_fork, active_type: current_fork_type, target_type: target_fork_type, }, @@ -1179,6 +1152,7 @@ impl DatabaseExt for Backend { &self.inner, active_journaled_state, &mut fork, + &mut self.extra_ctx, ); // insert the fork back @@ -1206,7 +1180,7 @@ impl DatabaseExt for Backend { let (fork_id, backend, fork_env) = self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?; // this will update the local mapping - self.inner.roll_fork(id, fork_id, backend, self.strategy.as_mut())?; + self.inner.roll_fork::(id, fork_id, backend, &mut self.extra_ctx)?; if let Some((active_id, active_idx)) = self.active_fork_ids { // the currently active fork is the targeted fork of this call @@ -1228,10 +1202,11 @@ impl DatabaseExt for Backend { active.journaled_state.depth = journaled_state.depth; for addr in persistent_addrs { - self.strategy.merge_journaled_state_data( + S::merge_journaled_state_data( addr, journaled_state, &mut active.journaled_state, + &mut self.extra_ctx, ); } @@ -1245,10 +1220,11 @@ impl DatabaseExt for Backend { for (addr, acc) in journaled_state.state.iter() { if acc.is_created() { if acc.is_touched() { - self.strategy.merge_journaled_state_data( + S::merge_journaled_state_data( *addr, journaled_state, &mut active.journaled_state, + &mut self.extra_ctx, ); } } else { @@ -1318,7 +1294,7 @@ impl DatabaseExt for Backend { let env = self.env_with_handler_cfg(env); let fork = self.inner.get_fork_by_id_mut(id)?; - commit_transaction( + commit_transaction::( &tx, env, journaled_state, @@ -1326,7 +1302,7 @@ impl DatabaseExt for Backend { &fork_id, &persistent_accounts, inspector, - self.strategy.as_mut(), + &mut self.extra_ctx, ) } @@ -1537,9 +1513,41 @@ impl DatabaseExt for Backend { self.mem_db.block_hashes.insert(block_number, block_hash); } } + + fn insert_account_info(&mut self, address: Address, account: AccountInfo) { + if let Some(db) = self.active_fork_db_mut() { + db.insert_account_info(address, account) + } else { + self.mem_db.insert_account_info(address, account) + } + } + fn insert_account_storage( + &mut self, + address: Address, + slot: U256, + value: U256, + ) -> Result<(), DatabaseError> { + if let Some(db) = self.active_fork_db_mut() { + db.insert_account_storage(address, slot, value) + } else { + self.mem_db.insert_account_storage(address, slot, value) + } + } + + fn replace_account_storage( + &mut self, + address: Address, + storage: Map, + ) -> Result<(), DatabaseError> { + if let Some(db) = self.active_fork_db_mut() { + db.replace_account_storage(address, storage) + } else { + self.mem_db.replace_account_storage(address, storage) + } + } } -impl DatabaseRef for Backend { +impl DatabaseRef for Backend { type Error = DatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { @@ -1575,7 +1583,7 @@ impl DatabaseRef for Backend { } } -impl DatabaseCommit for Backend { +impl DatabaseCommit for Backend { fn commit(&mut self, changes: Map) { if let Some(db) = self.active_fork_db_mut() { db.commit(changes) @@ -1585,7 +1593,7 @@ impl DatabaseCommit for Backend { } } -impl Database for Backend { +impl Database for Backend { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { if let Some(db) = self.active_fork_db_mut() { @@ -1803,12 +1811,12 @@ impl BackendInner { idx } - pub fn roll_fork( + pub fn roll_fork( &mut self, id: LocalForkId, new_fork_id: ForkId, backend: SharedBackend, - strategy: &mut dyn BackendStrategyExt, + extra_ctx: &mut S::BackendContext, ) -> eyre::Result { let fork_id = self.ensure_fork_id(id)?; let idx = self.ensure_fork_index(fork_id)?; @@ -1817,7 +1825,7 @@ impl BackendInner { // we initialize a _new_ `ForkDB` but keep the state of persistent accounts let mut new_db = ForkDB::new(backend); for addr in self.persistent_accounts.iter().copied() { - strategy.merge_db_account_data(addr, &active.db, &mut new_db); + S::merge_db_account_data(addr, &active.db, &mut new_db, extra_ctx); } active.db = new_db; } @@ -1929,7 +1937,7 @@ fn update_env_block(env: &mut Env, block: &AnyRpcBlock) { /// Executes the given transaction and commits state changes to the database _and_ the journaled /// state, with an inspector. #[allow(clippy::too_many_arguments)] -fn commit_transaction( +fn commit_transaction( tx: &Transaction, mut env: EnvWithHandlerCfg, journaled_state: &mut JournaledState, @@ -1937,7 +1945,7 @@ fn commit_transaction( fork_id: &ForkId, persistent_accounts: &HashSet
, inspector: &mut dyn InspectorExt, - strategy: &mut dyn BackendStrategyExt, + extra_ctx: &mut S::BackendContext, ) -> eyre::Result<()> { // TODO: Remove after https://github.com/foundry-rs/foundry/pull/9131 // if the tx has the blob_versioned_hashes field, we assume it's a Cancun block @@ -1952,8 +1960,7 @@ fn commit_transaction( let fork = fork.clone(); let journaled_state = journaled_state.clone(); let depth = journaled_state.depth; - let mut db = - Backend::new_with_fork(fork_id, fork, journaled_state, strategy.new_cloned_ext()); + let mut db = Backend::::new_with_fork(fork_id, fork, journaled_state, extra_ctx.clone()); let mut evm = crate::utils::new_evm_with_inspector(&mut db as _, env, inspector); // Adjust inner EVM depth to ensure that inspectors receive accurate data. @@ -2040,7 +2047,7 @@ mod tests { evm_opts, }; - let backend = Backend::spawn(Some(fork), Box::new(EvmBackendStrategy)); + let backend = Backend::::spawn(Some(fork), ()); // some rng contract from etherscan let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); diff --git a/crates/evm/core/src/backend/strategy.rs b/crates/evm/core/src/backend/strategy.rs index d755b7778..fa04902e1 100644 --- a/crates/evm/core/src/backend/strategy.rs +++ b/crates/evm/core/src/backend/strategy.rs @@ -12,59 +12,62 @@ pub struct BackendStrategyForkInfo<'a> { } pub trait BackendStrategy: Debug + Send + Sync { - fn name(&self) -> &'static str; + type BackendContext: Debug + Send + Sync + Clone + Default; /// When creating or switching forks, we update the AccountInfo of the contract fn update_fork_db( - &self, fork_info: BackendStrategyForkInfo<'_>, mem_db: &FoundryEvmInMemoryDB, backend_inner: &BackendInner, active_journaled_state: &mut JournaledState, target_fork: &mut Fork, + ctx: &mut Self::BackendContext, ); /// Clones the account data from the `active_journaled_state` into the `fork_journaled_state` fn merge_journaled_state_data( - &self, addr: Address, active_journaled_state: &JournaledState, fork_journaled_state: &mut JournaledState, + ctx: &mut Self::BackendContext, ); - fn merge_db_account_data(&self, addr: Address, active: &ForkDB, fork_db: &mut ForkDB); + fn merge_db_account_data( + addr: Address, + active: &ForkDB, + fork_db: &mut ForkDB, + ctx: &mut Self::BackendContext, + ); } -pub trait BackendStrategyExt: BackendStrategy { - fn new_cloned_ext(&self) -> Box; - /// Saves the storage keys for immutable variables per address. - /// - /// These are required during fork to help merge the persisted addresses, as they are stored - /// hashed so there is currently no way to retrieve all the address associated storage keys. - /// We store all the storage keys here, even if the addresses are not marked persistent as - /// they can be marked at a later stage as well. - fn zksync_save_immutable_storage(&mut self, _addr: Address, _keys: HashSet) {} -} +// pub trait BackendStrategyExt: BackendStrategy { +// fn new_cloned_ext(&self) -> Box; +// /// Saves the storage keys for immutable variables per address. +// /// +// /// These are required during fork to help merge the persisted addresses, as they are stored +// /// hashed so there is currently no way to retrieve all the address associated storage keys. +// /// We store all the storage keys here, even if the addresses are not marked persistent as +// /// they can be marked at a later stage as well. +// fn zksync_save_immutable_storage(&mut self, _addr: Address, _keys: HashSet) {} +// } -struct _ObjectSafe(dyn BackendStrategy); +// struct _ObjectSafe(dyn BackendStrategy); #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct EvmBackendStrategy; impl BackendStrategy for EvmBackendStrategy { - fn name(&self) -> &'static str { - "evm" - } + type BackendContext = (); fn update_fork_db( - &self, fork_info: BackendStrategyForkInfo<'_>, mem_db: &FoundryEvmInMemoryDB, backend_inner: &BackendInner, active_journaled_state: &mut JournaledState, target_fork: &mut Fork, + _ctx: &mut Self::BackendContext, ) { - self.update_fork_db_contracts( + Self::update_fork_db_contracts( fork_info, mem_db, backend_inner, @@ -74,10 +77,10 @@ impl BackendStrategy for EvmBackendStrategy { } fn merge_journaled_state_data( - &self, addr: Address, active_journaled_state: &JournaledState, fork_journaled_state: &mut JournaledState, + _ctx: &mut Self::BackendContext, ) { EvmBackendMergeStrategy::merge_journaled_state_data( addr, @@ -86,21 +89,25 @@ impl BackendStrategy for EvmBackendStrategy { ); } - fn merge_db_account_data(&self, addr: Address, active: &ForkDB, fork_db: &mut ForkDB) { + fn merge_db_account_data( + addr: Address, + active: &ForkDB, + fork_db: &mut ForkDB, + _ctx: &mut Self::BackendContext, + ) { EvmBackendMergeStrategy::merge_db_account_data(addr, active, fork_db); } } -impl BackendStrategyExt for EvmBackendStrategy { - fn new_cloned_ext(&self) -> Box { - Box::new(self.clone()) - } -} +// impl BackendStrategyExt for EvmBackendStrategy { +// fn new_cloned_ext(&self) -> Box { +// Box::new(self.clone()) +// } +// } impl EvmBackendStrategy { /// Merges the state of all `accounts` from the currently active db into the given `fork` pub(crate) fn update_fork_db_contracts( - &self, fork_info: BackendStrategyForkInfo<'_>, mem_db: &FoundryEvmInMemoryDB, backend_inner: &BackendInner, diff --git a/crates/evm/evm/src/executors/builder.rs b/crates/evm/evm/src/executors/builder.rs index b5e1d8f98..8aff399c3 100644 --- a/crates/evm/evm/src/executors/builder.rs +++ b/crates/evm/evm/src/executors/builder.rs @@ -2,7 +2,7 @@ use crate::{executors::Executor, inspectors::InspectorStackBuilder}; use foundry_evm_core::backend::Backend; use revm::primitives::{Env, EnvWithHandlerCfg, SpecId}; -use super::strategy::ExecutorStrategyExt; +use super::strategy::ExecutorStrategy; /// The builder that allows to configure an evm [`Executor`] which a stack of optional /// [`revm::Inspector`]s, such as [`Cheatcodes`]. @@ -13,7 +13,7 @@ use super::strategy::ExecutorStrategyExt; /// [`InspectorStack`]: super::InspectorStack #[derive(Clone, Debug)] #[must_use = "builders do nothing unless you call `build` on them"] -pub struct ExecutorBuilder { +pub struct ExecutorBuilder { /// The configuration used to build an `InspectorStack`. stack: InspectorStackBuilder, /// The gas limit. @@ -22,9 +22,11 @@ pub struct ExecutorBuilder { spec_id: SpecId, legacy_assertions: bool, + + _s: std::marker::PhantomData, } -impl Default for ExecutorBuilder { +impl Default for ExecutorBuilder { #[inline] fn default() -> Self { Self { @@ -32,11 +34,12 @@ impl Default for ExecutorBuilder { gas_limit: None, spec_id: SpecId::LATEST, legacy_assertions: false, + _s: Default::default(), } } } -impl ExecutorBuilder { +impl ExecutorBuilder { /// Create a new executor builder. #[inline] pub fn new() -> Self { @@ -76,8 +79,13 @@ impl ExecutorBuilder { /// Builds the executor as configured. #[inline] - pub fn build(self, env: Env, db: Backend, strategy: Box) -> Executor { - let Self { mut stack, gas_limit, spec_id, legacy_assertions } = self; + pub fn build( + self, + env: Env, + db: Backend, + ctx: S::ExecutorContext, + ) -> Executor { + let Self { mut stack, gas_limit, spec_id, legacy_assertions, _s } = self; if stack.block.is_none() { stack.block = Some(env.block.clone()); } @@ -86,6 +94,6 @@ impl ExecutorBuilder { } let gas_limit = gas_limit.unwrap_or_else(|| env.block.gas_limit.saturating_to()); let env = EnvWithHandlerCfg::new_with_spec_id(Box::new(env), spec_id); - Executor::new(db, env, stack.build(), gas_limit, legacy_assertions, strategy) + Executor::new(db, env, stack.build(), gas_limit, legacy_assertions, ctx) } } diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 8a479c019..4185c5343 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -22,6 +22,8 @@ use std::{cell::RefCell, collections::BTreeMap}; mod types; pub use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}; +use super::strategy::ExecutorStrategy; + /// Contains data collected during fuzz test runs. #[derive(Default)] pub struct FuzzTestData { @@ -50,9 +52,9 @@ pub struct FuzzTestData { /// After instantiation, calling `fuzz` will proceed to hammer the deployed smart contract with /// inputs, until it finds a counterexample. The provided [`TestRunner`] contains all the /// configuration which can be overridden via [environment variables](proptest::test_runner::Config) -pub struct FuzzedExecutor { +pub struct FuzzedExecutor { /// The EVM executor - executor: Executor, + executor: Executor, /// The fuzzer runner: TestRunner, /// The account that calls tests @@ -61,10 +63,10 @@ pub struct FuzzedExecutor { config: FuzzConfig, } -impl FuzzedExecutor { +impl FuzzedExecutor { /// Instantiates a fuzzed executor given a testrunner pub fn new( - executor: Executor, + executor: Executor, runner: TestRunner, sender: Address, config: FuzzConfig, diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 79a208821..f0ca1aa4c 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -37,7 +37,7 @@ use revm::{ }, }; use std::borrow::Cow; -use strategy::ExecutorStrategyExt; +use strategy::{EvmExecutorStrategy, ExecutorStrategy}; mod builder; pub use builder::ExecutorBuilder; @@ -75,13 +75,13 @@ sol! { /// deployment /// - `setup`: a special case of `transact`, used to set up the environment for a test #[derive(Debug)] -pub struct Executor { +pub struct Executor { /// The underlying `revm::Database` that contains the EVM storage. // Note: We do not store an EVM here, since we are really // only interested in the database. REVM's `EVM` is a thin // wrapper around spawning a new EVM on every call anyway, // so the performance difference should be negligible. - pub backend: Backend, + pub backend: Backend, /// The EVM environment. pub env: EnvWithHandlerCfg, /// The Revm inspector stack. @@ -93,10 +93,10 @@ pub struct Executor { /// Whether `failed()` should be called on the test contract to determine if the test failed. legacy_assertions: bool, - strategy: Option>, + extra_ctx: S::ExecutorContext, } -impl Clone for Executor { +impl Clone for Executor { fn clone(&self) -> Self { Self { backend: self.backend.clone(), @@ -104,27 +104,27 @@ impl Clone for Executor { inspector: self.inspector.clone(), gas_limit: self.gas_limit, legacy_assertions: self.legacy_assertions, - strategy: self.strategy.as_ref().map(|s| s.new_cloned_ext()), + extra_ctx: self.extra_ctx.clone(), } } } -impl Executor { +impl Executor { /// Creates a new `ExecutorBuilder`. #[inline] - pub fn builder() -> ExecutorBuilder { + pub fn builder() -> ExecutorBuilder { ExecutorBuilder::new() } /// Creates a new `Executor` with the given arguments. #[inline] pub fn new( - mut backend: Backend, + mut backend: Backend, env: EnvWithHandlerCfg, inspector: InspectorStack, gas_limit: u64, legacy_assertions: bool, - strategy: Box, + extra_ctx: S::ExecutorContext, ) -> Self { // Need to create a non-empty contract on the cheatcodes address so `extcodesize` checks // do not fail. @@ -139,10 +139,10 @@ impl Executor { }, ); - Self { backend, env, inspector, gas_limit, legacy_assertions, strategy: Some(strategy) } + Self { backend, env, inspector, gas_limit, legacy_assertions, extra_ctx } } - fn clone_with_backend(&self, backend: Backend) -> Self { + fn clone_with_backend(&self, backend: Backend) -> Self { let env = EnvWithHandlerCfg::new_with_spec_id(Box::new(self.env().clone()), self.spec_id()); Self::new( backend, @@ -150,17 +150,17 @@ impl Executor { self.inspector().clone(), self.gas_limit, self.legacy_assertions, - self.strategy.as_ref().map(|s| s.new_cloned_ext()).expect("failed acquiring strategy"), + self.extra_ctx.clone(), ) } /// Returns a reference to the EVM backend. - pub fn backend(&self) -> &Backend { + pub fn backend(&self) -> &Backend { &self.backend } /// Returns a mutable reference to the EVM backend. - pub fn backend_mut(&mut self) -> &mut Backend { + pub fn backend_mut(&mut self) -> &mut Backend { &mut self.backend } @@ -214,21 +214,10 @@ impl Executor { Ok(()) } - pub fn with_strategy(&mut self, mut f: F) -> R - where - F: FnMut(&mut dyn ExecutorStrategyExt, &mut Self) -> R, - { - let mut strategy = self.strategy.take(); - let result = f(strategy.as_mut().expect("failed acquiring strategy").as_mut(), self); - self.strategy = strategy; - - result - } - /// Set the balance of an account. pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> { trace!(?address, ?amount, "setting account balance"); - self.with_strategy(|strategy, executor| strategy.set_balance(executor, address, amount)) + S::set_balance(self.backend_mut(), address, amount) } /// Gets the balance of an account @@ -238,7 +227,7 @@ impl Executor { /// Set the nonce of an account. pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> { - self.with_strategy(|strategy, executor| strategy.set_nonce(executor, address, nonce)) + S::set_nonce(self.backend_mut(), address, nonce) } /// Returns the nonce of an account. @@ -269,14 +258,6 @@ impl Executor { self } - #[inline] - pub fn set_transaction_other_fields(&mut self, other_fields: OtherFields) { - self.strategy - .as_mut() - .expect("failed acquiring strategy") - .set_inspect_context(other_fields); - } - /// Deploys a contract and commits the new state to the underlying database. /// /// Executes a CREATE transaction with the contract `code` and persistent database state @@ -457,12 +438,8 @@ impl Executor { backend.is_initialized = false; backend.spec_id = env.spec_id(); - let result = self - .strategy - .as_ref() - .expect("failed acquiring strategy") - .new_cloned_ext() - .call_inspect(&mut backend, &mut env, &mut inspector)?; + let result = + S::call_inspect(&mut backend, &mut env, &mut inspector, &mut self.extra_ctx.clone())?; convert_executed_result( env.clone(), @@ -475,24 +452,22 @@ impl Executor { /// Execute the transaction configured in `env.tx`. #[instrument(name = "transact", level = "debug", skip_all)] pub fn transact_with_env(&mut self, mut env: EnvWithHandlerCfg) -> eyre::Result { - self.with_strategy(|strategy, executor| { - let mut inspector = executor.inspector.clone(); - let backend = &mut executor.backend; - backend.initialize(&env); - - let result_and_state = - strategy.transact_inspect(backend, &mut env, &executor.env, &mut inspector)?; - - let mut result = convert_executed_result( - env.clone(), - inspector, - result_and_state, - backend.has_state_snapshot_failure(), - )?; - - executor.commit(&mut result); - Ok(result) - }) + let mut inspector = self.inspector.clone(); + let backend = &mut self.backend; + backend.initialize(&env); + + let result_and_state = + S::transact_inspect(backend, &mut env, &self.env, &mut inspector, &mut self.extra_ctx)?; + + let mut result = convert_executed_result( + env.clone(), + inspector, + result_and_state, + backend.has_state_snapshot_failure(), + )?; + + self.commit(&mut result); + Ok(result) } /// Commit the changeset to the database and adjust `self.inspector_config` values according to diff --git a/crates/evm/evm/src/executors/strategy.rs b/crates/evm/evm/src/executors/strategy.rs index 0f4910791..414ced3a8 100644 --- a/crates/evm/evm/src/executors/strategy.rs +++ b/crates/evm/evm/src/executors/strategy.rs @@ -6,8 +6,8 @@ use eyre::{Context, Result}; use foundry_cheatcodes::strategy::{CheatcodeInspectorStrategyExt, EvmCheatcodeInspectorStrategy}; use foundry_evm_core::{ backend::{ - strategy::{BackendStrategyExt, EvmBackendStrategy}, - BackendResult, DatabaseExt, + strategy::{BackendStrategy, EvmBackendStrategy}, + Backend, BackendResult, DatabaseExt, }, InspectorExt, }; @@ -17,79 +17,72 @@ use revm::{ DatabaseRef, }; -use super::Executor; +pub trait ExecutorStrategy: Debug + Send + Sized + 'static { + type BackendStrategy: BackendStrategy; + type ExecutorContext: Debug + Clone + Default + Send + Sync; -pub trait ExecutorStrategy: Debug + Send { - fn name(&self) -> &'static str; + fn backend_ctx( + executor_ctx: &Self::ExecutorContext, + ) -> ::BackendContext; - fn new_cloned(&self) -> Box; + fn set_balance(db: &mut dyn DatabaseExt, address: Address, amount: U256) -> BackendResult<()>; - fn set_balance( - &self, - executor: &mut Executor, - address: Address, - amount: U256, - ) -> BackendResult<()>; - - fn set_nonce(&self, executor: &mut Executor, address: Address, nonce: u64) - -> BackendResult<()>; - - fn set_inspect_context(&self, other_fields: OtherFields); + fn set_nonce(db: &mut dyn DatabaseExt, address: Address, nonce: u64) -> BackendResult<()>; fn call_inspect( - &self, db: &mut dyn DatabaseExt, env: &mut EnvWithHandlerCfg, inspector: &mut dyn InspectorExt, + ctx: &mut Self::ExecutorContext, ) -> eyre::Result; fn transact_inspect( - &self, db: &mut dyn DatabaseExt, env: &mut EnvWithHandlerCfg, _executor_env: &EnvWithHandlerCfg, inspector: &mut dyn InspectorExt, + ctx: &mut Self::ExecutorContext, ) -> eyre::Result; - fn new_backend_strategy(&self) -> Box; - fn new_cheatcode_inspector_strategy(&self) -> Box; + fn new_cheatcode_inspector_strategy( + ctx: &Self::ExecutorContext, + ) -> Box; // TODO perhaps need to create fresh strategies as well } -pub trait ExecutorStrategyExt: ExecutorStrategy { - fn new_cloned_ext(&self) -> Box; +// pub trait ExecutorStrategyExt: ExecutorStrategy { +// fn new_cloned_ext(&self) -> Box; - fn zksync_set_dual_compiled_contracts(&self, _dual_compiled_contracts: DualCompiledContracts) {} +// fn zksync_set_dual_compiled_contracts(&self, _dual_compiled_contracts: DualCompiledContracts) +// {} - fn zksync_set_fork_env(&self, _fork_url: &str, _env: &Env) -> Result<()> { - Ok(()) - } -} +// fn zksync_set_fork_env(&self, _fork_url: &str, _env: &Env) -> Result<()> { +// Ok(()) +// } +// } #[derive(Debug, Default, Clone)] pub struct EvmExecutorStrategy {} impl ExecutorStrategy for EvmExecutorStrategy { - fn name(&self) -> &'static str { - "evm" - } + type BackendStrategy = EvmBackendStrategy; + type ExecutorContext = (); - fn new_cloned(&self) -> Box { - Box::new(self.clone()) + fn backend_ctx( + _executor_ctx: &Self::ExecutorContext, + ) -> ::BackendContext { } - fn set_inspect_context(&self, _other_fields: OtherFields) {} - /// Executes the configured test call of the `env` without committing state changes. /// /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. fn call_inspect( - &self, db: &mut dyn DatabaseExt, env: &mut EnvWithHandlerCfg, inspector: &mut dyn InspectorExt, + ctx: &mut Self::ExecutorContext, ) -> eyre::Result { let mut evm = crate::utils::new_evm_with_inspector(db, env.clone(), inspector); @@ -106,11 +99,11 @@ impl ExecutorStrategy for EvmExecutorStrategy { /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. fn transact_inspect( - &self, db: &mut dyn DatabaseExt, env: &mut EnvWithHandlerCfg, _executor_env: &EnvWithHandlerCfg, inspector: &mut dyn InspectorExt, + ctx: &mut Self::ExecutorContext, ) -> eyre::Result { let mut evm = crate::utils::new_evm_with_inspector(db, env.clone(), inspector); @@ -121,44 +114,26 @@ impl ExecutorStrategy for EvmExecutorStrategy { Ok(res) } - fn set_balance( - &self, - executor: &mut Executor, - address: Address, - amount: U256, - ) -> BackendResult<()> { + fn set_balance(db: &mut dyn DatabaseExt, address: Address, amount: U256) -> BackendResult<()> { trace!(?address, ?amount, "setting account balance"); - let mut account = executor.backend().basic_ref(address)?.unwrap_or_default(); + let mut account = db.basic_ref(address)?.unwrap_or_default(); account.balance = amount; - executor.backend_mut().insert_account_info(address, account); + db.insert_account_info(address, account); Ok(()) } - fn set_nonce( - &self, - executor: &mut Executor, - address: Address, - nonce: u64, - ) -> BackendResult<()> { - let mut account = executor.backend().basic_ref(address)?.unwrap_or_default(); + fn set_nonce(db: &mut dyn DatabaseExt, address: Address, nonce: u64) -> BackendResult<()> { + let mut account = db.basic_ref(address)?.unwrap_or_default(); account.nonce = nonce; - executor.backend_mut().insert_account_info(address, account); + db.insert_account_info(address, account); Ok(()) } - fn new_backend_strategy(&self) -> Box { - Box::new(EvmBackendStrategy) - } - - fn new_cheatcode_inspector_strategy(&self) -> Box { - Box::new(EvmCheatcodeInspectorStrategy::default()) - } -} - -impl ExecutorStrategyExt for EvmExecutorStrategy { - fn new_cloned_ext(&self) -> Box { - Box::new(self.clone()) + fn new_cheatcode_inspector_strategy( + _ctx: &Self::ExecutorContext, + ) -> Box { + Box::new(EvmCheatcodeInspectorStrategy) } } diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index 68a3c11f4..a22c05ea2 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -6,14 +6,14 @@ use foundry_evm_traces::{InternalTraceMode, TraceMode}; use revm::primitives::{Env, SpecId}; use std::ops::{Deref, DerefMut}; -use super::strategy::ExecutorStrategyExt; +use super::strategy::{EvmExecutorStrategy, ExecutorStrategy}; /// A default executor with tracing enabled -pub struct TracingExecutor { - executor: Executor, +pub struct TracingExecutor { + executor: Executor, } -impl TracingExecutor { +impl TracingExecutor { pub fn new( env: revm::primitives::Env, fork: Option, @@ -21,9 +21,22 @@ impl TracingExecutor { debug: bool, decode_internal: bool, alphanet: bool, - strategy: Box, ) -> Self { - let db = Backend::spawn(fork, strategy.new_backend_strategy()); + Self::new_with_context(env, fork, version, debug, decode_internal, alphanet, ()) + } +} + +impl TracingExecutor { + pub fn new_with_context( + env: revm::primitives::Env, + fork: Option, + version: Option, + debug: bool, + decode_internal: bool, + alphanet: bool, + extra_ctx: S::ExecutorContext, + ) -> Self { + let db = Backend::::spawn(fork, S::backend_ctx(&extra_ctx)); let trace_mode = TraceMode::Call.with_debug(debug).with_decode_internal(if decode_internal { InternalTraceMode::Full @@ -36,7 +49,7 @@ impl TracingExecutor { executor: ExecutorBuilder::new() .inspectors(|stack| stack.trace_mode(trace_mode).alphanet(alphanet)) .spec(evm_spec_id(&version.unwrap_or_default(), alphanet)) - .build(env, db, strategy), + .build(env, db, extra_ctx), } } @@ -44,7 +57,9 @@ impl TracingExecutor { pub fn spec_id(&self) -> SpecId { self.executor.spec_id() } +} +impl TracingExecutor { /// uses the fork block number from the config pub async fn get_fork_material( config: &Config, @@ -61,15 +76,15 @@ impl TracingExecutor { } } -impl Deref for TracingExecutor { - type Target = Executor; +impl Deref for TracingExecutor { + type Target = Executor; fn deref(&self) -> &Self::Target { &self.executor } } -impl DerefMut for TracingExecutor { +impl DerefMut for TracingExecutor { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.executor } diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 3171baaba..9439c9d98 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -29,6 +29,7 @@ use foundry_common::{ TransactionMaybeSigned, }; use foundry_config::Config; +use foundry_evm::executors::strategy::ExecutorStrategy; use foundry_zksync_core::convert::ConvertH160; use futures::{future::join_all, StreamExt}; use itertools::Itertools; @@ -208,15 +209,15 @@ impl SendTransactionsKind { /// State after we have bundled all /// [`TransactionWithMetadata`](forge_script_sequence::TransactionWithMetadata) objects into a /// single [`ScriptSequenceKind`] object containing one or more script sequences. -pub struct BundledState { +pub struct BundledState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, pub build_data: LinkedBuildData, pub sequence: ScriptSequenceKind, } -impl BundledState { +impl BundledState { pub async fn wait_for_pending(mut self) -> Result { let progress = ScriptProgress::default(); let progress_ref = &progress; @@ -251,7 +252,7 @@ impl BundledState { } /// Broadcasts transactions from all sequences. - pub async fn broadcast(mut self) -> Result { + pub async fn broadcast(mut self) -> Result> { let required_addresses = self .sequence .sequences() diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 3bb0da6a7..fe4900144 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -22,7 +22,10 @@ use foundry_compilers::{ zksync::compile::output::ProjectCompileOutput as ZkProjectCompileOutput, ArtifactId, ProjectCompileOutput, }; -use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::debug::ContractSources}; +use foundry_evm::{ + constants::DEFAULT_CREATE2_DEPLOYER, executors::strategy::ExecutorStrategy, + traces::debug::ContractSources, +}; use foundry_linking::Linker; use foundry_zksync_compiler::DualCompiledContracts; use std::{collections::BTreeMap, path::PathBuf, str::FromStr, sync::Arc}; @@ -48,7 +51,10 @@ impl BuildData { /// Links contracts. Uses CREATE2 linking when possible, otherwise falls back to /// default linking with sender nonce and address. - pub async fn link(self, script_config: &ScriptConfig) -> Result { + pub async fn link( + self, + script_config: &ScriptConfig, + ) -> Result { let can_use_create2 = if let Some(fork_url) = &script_config.evm_opts.fork_url { let provider = try_get_http_provider(fork_url)?; let deployer_code = provider.get_code_at(DEFAULT_CREATE2_DEPLOYER).await?; @@ -191,16 +197,16 @@ impl LinkedBuildData { } /// First state basically containing only inputs of the user. -pub struct PreprocessedState { +pub struct PreprocessedState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, } -impl PreprocessedState { +impl PreprocessedState { /// Parses user input and compiles the contracts depending on script target. /// After compilation, finds exact [ArtifactId] of the target contract. - pub fn compile(self) -> Result { + pub fn compile(self) -> Result> { let Self { args, script_config, script_wallets } = self; let project = script_config.config.project()?; @@ -305,16 +311,16 @@ impl PreprocessedState { } /// State after we have determined and compiled target contract to be executed. -pub struct CompiledState { +pub struct CompiledState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, pub build_data: BuildData, } -impl CompiledState { +impl CompiledState { /// Uses provided sender address to compute library addresses and link contracts with them. - pub async fn link(self) -> Result { + pub async fn link(self) -> Result> { let Self { args, script_config, script_wallets, build_data } = self; let build_data = build_data.link(&script_config).await?; @@ -323,7 +329,7 @@ impl CompiledState { } /// Tries loading the resumed state from the cache files, skipping simulation stage. - pub async fn resume(self) -> Result { + pub async fn resume(self) -> Result> { let chain = if self.args.multi { None } else { diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index c47c8ac4c..bc284619f 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -25,6 +25,7 @@ use foundry_config::{Config, NamedChain}; use foundry_debugger::Debugger; use foundry_evm::{ decode::decode_console_logs, + executors::strategy::ExecutorStrategy, inspectors::cheatcodes::BroadcastableTransactions, traces::{ decode_trace_arena, @@ -39,9 +40,9 @@ use yansi::Paint; /// State after linking, contains the linked build data along with library addresses and optional /// array of libraries that need to be predeployed. -pub struct LinkedState { +pub struct LinkedState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, pub build_data: LinkedBuildData, } @@ -59,10 +60,10 @@ pub struct ExecutionData { pub abi: JsonAbi, } -impl LinkedState { +impl LinkedState { /// Given linked and compiled artifacts, prepares data we need for execution. /// This includes the function to call and the calldata to pass to it. - pub async fn prepare_execution(self) -> Result { + pub async fn prepare_execution(self) -> Result> { let Self { args, script_config, script_wallets, build_data } = self; let target_contract = build_data.get_target_contract()?; @@ -91,19 +92,19 @@ impl LinkedState { /// Same as [LinkedState], but also contains [ExecutionData]. #[derive(Debug)] -pub struct PreExecutionState { +pub struct PreExecutionState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, } -impl PreExecutionState { +impl PreExecutionState { /// Executes the script and returns the state after execution. /// Might require executing script twice in cases when we determine sender from execution. #[async_recursion] - pub async fn execute(mut self) -> Result { + pub async fn execute(mut self) -> Result> { let mut runner = self .script_config .get_runner_with_cheatcodes( @@ -143,7 +144,7 @@ impl PreExecutionState { } /// Executes the script using the provided runner and returns the [ScriptResult]. - pub async fn execute_with_runner(&self, runner: &mut ScriptRunner) -> Result { + pub async fn execute_with_runner(&self, runner: &mut ScriptRunner) -> Result { let (address, mut setup_result) = runner.setup( &self.build_data.predeploy_libraries, self.execution_data.bytecode.clone(), @@ -274,18 +275,18 @@ pub struct ExecutionArtifacts { } /// State after the script has been executed. -pub struct ExecutedState { +pub struct ExecutedState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, pub execution_result: ScriptResult, } -impl ExecutedState { +impl ExecutedState { /// Collects the data we need for simulation and various post-execution tasks. - pub async fn prepare_simulation(self) -> Result { + pub async fn prepare_simulation(self) -> Result> { let returns = self.get_returns()?; let decoder = self.build_trace_decoder(&self.build_data.known_contracts).await?; @@ -389,7 +390,7 @@ impl ExecutedState { } } -impl PreSimulationState { +impl PreSimulationState { pub fn show_json(&self) -> Result<()> { let result = &self.execution_result; diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index c56058bc4..2d944ab52 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -47,7 +47,7 @@ use foundry_config::{ use foundry_evm::{ backend::Backend, constants::DEFAULT_CREATE2_DEPLOYER, - executors::ExecutorBuilder, + executors::{strategy::ExecutorStrategy, ExecutorBuilder}, inspectors::{ cheatcodes::{BroadcastableTransactions, Wallets}, CheatsConfig, @@ -535,23 +535,28 @@ struct JsonResult<'a> { } #[derive(Clone, Debug)] -pub struct ScriptConfig { +pub struct ScriptConfig { pub config: Config, pub evm_opts: EvmOpts, pub sender_nonce: u64, /// Maps a rpc url to a backend - pub backends: HashMap, + pub backends: HashMap>, + pub extra_ctx: S::ExecutorContext, } -impl ScriptConfig { - pub async fn new(config: Config, evm_opts: EvmOpts) -> Result { +impl ScriptConfig { + pub async fn new( + config: Config, + evm_opts: EvmOpts, + extra_ctx: S::ExecutorContext, + ) -> Result { let sender_nonce = if let Some(fork_url) = evm_opts.fork_url.as_ref() { next_nonce(evm_opts.sender, fork_url).await? } else { // dapptools compatibility 1 }; - Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::default() }) + Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::default(), extra_ctx }) } pub async fn update_sender(&mut self, sender: Address) -> Result<()> { @@ -565,7 +570,7 @@ impl ScriptConfig { Ok(()) } - async fn get_runner(&mut self) -> Result { + async fn get_runner(&mut self) -> Result> { self._get_runner(None, false).await } @@ -576,7 +581,7 @@ impl ScriptConfig { debug: bool, target: ArtifactId, dual_compiled_contracts: DualCompiledContracts, - ) -> Result { + ) -> Result> { self._get_runner( Some((known_contracts, script_wallets, target, dual_compiled_contracts)), debug, @@ -588,17 +593,18 @@ impl ScriptConfig { &mut self, cheats_data: Option<(ContractsByArtifact, Wallets, ArtifactId, DualCompiledContracts)>, debug: bool, - ) -> Result { + ) -> Result> { trace!("preparing script runner"); let env = self.evm_opts.evm_env().await?; let mut strategy = utils::get_executor_strategy(&self.config); + let backend_context = S::backend_ctx(&self.extra_ctx); let db = if let Some(fork_url) = self.evm_opts.fork_url.as_ref() { match self.backends.get(fork_url) { Some(db) => db.clone(), None => { let fork = self.evm_opts.get_fork(&self.config, env.clone()); - let backend = Backend::spawn(fork, strategy.new_backend_strategy()); + let backend = Backend::::spawn(fork, backend_context); self.backends.insert(fork_url.clone(), backend.clone()); backend } @@ -607,7 +613,7 @@ impl ScriptConfig { // It's only really `None`, when we don't pass any `--fork-url`. And if so, there is // no need to cache it, since there won't be any onchain simulation that we'd need // to cache the backend for. - Backend::spawn(None, strategy.new_backend_strategy()) + Backend::::spawn(None, backend_context) }; // We need to enable tracing to decode contract names: local or external. @@ -624,11 +630,11 @@ impl ScriptConfig { if let Some((known_contracts, script_wallets, target, dual_compiled_contracts)) = cheats_data { - strategy.zksync_set_dual_compiled_contracts(dual_compiled_contracts); + // strategy.zksync_set_dual_compiled_contracts(dual_compiled_contracts); - if let Some(fork_url) = &self.evm_opts.fork_url { - strategy.zksync_set_fork_env(fork_url, &env)?; - } + // if let Some(fork_url) = &self.evm_opts.fork_url { + // strategy.zksync_set_fork_env(fork_url, &env)?; + // } builder = builder.inspectors(|stack| { stack @@ -639,7 +645,7 @@ impl ScriptConfig { Some(known_contracts), Some(target.name), Some(target.version), - strategy.new_cheatcode_inspector_strategy(), + S::new_cheatcode_inspector_strategy(&self.extra_ctx), ) .into(), ) diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index 20ad42cf4..1f799c359 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -9,7 +9,9 @@ use foundry_cheatcodes::BroadcastableTransaction; use foundry_config::Config; use foundry_evm::{ constants::{CALLER, DEFAULT_CREATE2_DEPLOYER}, - executors::{DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, + executors::{ + strategy::ExecutorStrategy, DeployResult, EvmError, ExecutionErr, Executor, RawCallResult, + }, opts::EvmOpts, revm::interpreter::{return_ok, InstructionResult}, traces::{TraceKind, Traces}, @@ -18,13 +20,13 @@ use std::collections::VecDeque; /// Drives script execution #[derive(Debug)] -pub struct ScriptRunner { - pub executor: Executor, +pub struct ScriptRunner { + pub executor: Executor, pub evm_opts: EvmOpts, } -impl ScriptRunner { - pub fn new(executor: Executor, evm_opts: EvmOpts) -> Self { +impl ScriptRunner { + pub fn new(executor: Executor, evm_opts: EvmOpts) -> Self { Self { executor, evm_opts } } diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 4293c8929..256ae35c7 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -17,7 +17,10 @@ use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_different_gas_calc, now}; use foundry_common::{ContractData, TransactionMaybeSigned}; -use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; +use foundry_evm::{ + executors::strategy::ExecutorStrategy, + traces::{decode_trace_arena, render_trace_arena}, +}; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; use std::{ @@ -30,9 +33,9 @@ use std::{ /// /// Can be either converted directly to [BundledState] or driven to it through /// [FilledTransactionsState]. -pub struct PreSimulationState { +pub struct PreSimulationState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, @@ -40,13 +43,13 @@ pub struct PreSimulationState { pub execution_artifacts: ExecutionArtifacts, } -impl PreSimulationState { +impl PreSimulationState { /// If simulation is enabled, simulates transactions against fork and fills gas estimation and /// metadata. Otherwise, metadata (e.g. additional contracts, created contract names) is /// left empty. /// /// Both modes will panic if any of the transactions have None for the `rpc` field. - pub async fn fill_metadata(self) -> Result { + pub async fn fill_metadata(self) -> Result> { let address_to_abi = self.build_address_to_abi_map(); let mut transactions = self @@ -222,7 +225,7 @@ impl PreSimulationState { } /// Build [ScriptRunner] forking given RPC for each RPC used in the script. - async fn build_runners(&self) -> Result> { + async fn build_runners(&self) -> Result)>> { let rpcs = self.execution_artifacts.rpc_data.total_rpcs.clone(); let n = rpcs.len(); @@ -242,22 +245,22 @@ impl PreSimulationState { /// At this point we have converted transactions collected during script execution to /// [TransactionWithMetadata] objects which contain additional metadata needed for broadcasting and /// verification. -pub struct FilledTransactionsState { +pub struct FilledTransactionsState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, pub build_data: LinkedBuildData, pub execution_artifacts: ExecutionArtifacts, pub transactions: VecDeque, } -impl FilledTransactionsState { +impl FilledTransactionsState { /// Bundles all transactions of the [`TransactionWithMetadata`] type in a list of /// [`ScriptSequence`]. List length will be higher than 1, if we're dealing with a multi /// chain deployment. /// /// Each transaction will be added with the correct transaction type and gas estimation. - pub async fn bundle(self) -> Result { + pub async fn bundle(self) -> Result> { let is_multi_deployment = self.execution_artifacts.rpc_data.total_rpcs.len() > 1; if is_multi_deployment && !self.build_data.libraries.is_empty() { diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index a7e7fcdc7..4288942a7 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -11,20 +11,21 @@ use foundry_cli::opts::{EtherscanOpts, ProjectPathsArgs}; use foundry_common::ContractsByArtifact; use foundry_compilers::{info::ContractInfo, Project}; use foundry_config::{Chain, Config}; +use foundry_evm::executors::strategy::ExecutorStrategy; use foundry_zksync_compiler::ZKSYNC_ARTIFACTS_DIR; use semver::Version; /// State after we have broadcasted the script. /// It is assumed that at this point [BroadcastedState::sequence] contains receipts for all /// broadcasted transactions. -pub struct BroadcastedState { +pub struct BroadcastedState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub build_data: LinkedBuildData, pub sequence: ScriptSequenceKind, } -impl BroadcastedState { +impl BroadcastedState { pub async fn verify(self) -> Result<()> { let Self { args, script_config, build_data, mut sequence, .. } = self; diff --git a/crates/strategy/zksync/src/backend.rs b/crates/strategy/zksync/src/backend.rs index 6aa9316f7..1dc6bad49 100644 --- a/crates/strategy/zksync/src/backend.rs +++ b/crates/strategy/zksync/src/backend.rs @@ -1,7 +1,6 @@ use std::collections::hash_map::Entry; use alloy_primitives::{map::HashMap, Address, U256}; -use foundry_evm::backend::strategy::BackendStrategyExt; use foundry_evm_core::backend::{ strategy::{ BackendStrategy, BackendStrategyForkInfo, EvmBackendMergeStrategy, EvmBackendStrategy, @@ -18,8 +17,10 @@ use serde::{Deserialize, Serialize}; use tracing::trace; #[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct ZksyncBackendStrategy { - evm: EvmBackendStrategy, +pub struct ZksyncBackendStrategy; + +#[derive(Debug, Default, Clone)] +pub struct Context { /// Store storage keys per contract address for immutable variables. persistent_immutable_keys: HashMap>, } @@ -36,37 +37,41 @@ pub struct ZkBackendInspectData { } impl BackendStrategy for ZksyncBackendStrategy { - fn name(&self) -> &'static str { - "zk" - } + type BackendContext = Context; /// When creating or switching forks, we update the AccountInfo of the contract. fn update_fork_db( - &self, fork_info: BackendStrategyForkInfo<'_>, mem_db: &FoundryEvmInMemoryDB, backend_inner: &BackendInner, active_journaled_state: &mut JournaledState, target_fork: &mut Fork, + ctx: &mut Self::BackendContext, ) { - self.update_fork_db_contracts( + Self::update_fork_db_contracts( fork_info, mem_db, backend_inner, active_journaled_state, target_fork, + ctx, ) } fn merge_journaled_state_data( - &self, addr: Address, active_journaled_state: &JournaledState, fork_journaled_state: &mut JournaledState, + ctx: &mut Self::BackendContext, ) { - self.evm.merge_journaled_state_data(addr, active_journaled_state, fork_journaled_state); + EvmBackendStrategy::merge_journaled_state_data( + addr, + active_journaled_state, + fork_journaled_state, + &mut (), + ); let zk_state = - &ZksyncMergeState { persistent_immutable_keys: &self.persistent_immutable_keys }; + &ZksyncMergeState { persistent_immutable_keys: &ctx.persistent_immutable_keys }; ZksyncBackendMerge::merge_zk_journaled_state_data( addr, active_journaled_state, @@ -75,10 +80,15 @@ impl BackendStrategy for ZksyncBackendStrategy { ); } - fn merge_db_account_data(&self, addr: Address, active: &ForkDB, fork_db: &mut ForkDB) { - self.evm.merge_db_account_data(addr, active, fork_db); + fn merge_db_account_data( + addr: Address, + active: &ForkDB, + fork_db: &mut ForkDB, + ctx: &mut Self::BackendContext, + ) { + EvmBackendStrategy::merge_db_account_data(addr, active, fork_db, &mut ()); let zk_state = - &ZksyncMergeState { persistent_immutable_keys: &self.persistent_immutable_keys }; + &ZksyncMergeState { persistent_immutable_keys: &ctx.persistent_immutable_keys }; ZksyncBackendMerge::merge_zk_account_data(addr, active, fork_db, zk_state); } } @@ -86,12 +96,12 @@ impl BackendStrategy for ZksyncBackendStrategy { impl ZksyncBackendStrategy { /// Merges the state of all `accounts` from the currently active db into the given `fork` pub(crate) fn update_fork_db_contracts( - &self, fork_info: BackendStrategyForkInfo<'_>, mem_db: &FoundryEvmInMemoryDB, backend_inner: &BackendInner, active_journaled_state: &mut JournaledState, target_fork: &mut Fork, + ctx: &mut Context, ) { let _require_zk_storage_merge = fork_info.active_type.is_zk() && fork_info.target_type.is_zk(); @@ -103,7 +113,7 @@ impl ZksyncBackendStrategy { let accounts = backend_inner.persistent_accounts.iter().copied(); let zk_state = - &ZksyncMergeState { persistent_immutable_keys: &self.persistent_immutable_keys }; + &ZksyncMergeState { persistent_immutable_keys: &ctx.persistent_immutable_keys }; if let Some(db) = fork_info.active_fork.map(|f| &f.db) { ZksyncBackendMerge::merge_account_data( accounts, @@ -124,18 +134,18 @@ impl ZksyncBackendStrategy { } } -impl BackendStrategyExt for ZksyncBackendStrategy { - fn new_cloned_ext(&self) -> Box { - Box::new(self.clone()) - } +// impl BackendStrategyExt for ZksyncBackendStrategy { +// fn new_cloned_ext(&self) -> Box { +// Box::new(self.clone()) +// } - fn zksync_save_immutable_storage(&mut self, addr: Address, keys: HashSet) { - self.persistent_immutable_keys - .entry(addr) - .and_modify(|entry| entry.extend(&keys)) - .or_insert(keys); - } -} +// fn zksync_save_immutable_storage(&mut self, addr: Address, keys: HashSet) { +// self.persistent_immutable_keys +// .entry(addr) +// .and_modify(|entry| entry.extend(&keys)) +// .or_insert(keys); +// } +// } pub(crate) struct ZksyncBackendMerge; diff --git a/crates/strategy/zksync/src/cheatcode.rs b/crates/strategy/zksync/src/cheatcode.rs index 9e9c5f485..694aa0be5 100644 --- a/crates/strategy/zksync/src/cheatcode.rs +++ b/crates/strategy/zksync/src/cheatcode.rs @@ -937,7 +937,8 @@ impl CheatcodeInspectorStrategyExt for ZksyncCheatcodeInspectorStrategy { .to_ru256() }) .collect::>(); - ecx.db.get_strategy().zksync_save_immutable_storage(addr, keys); + // ecx.db.get_strategy().zksync_save_immutable_storage(addr, keys); + todo!() // TODO TODO TODO popzxc } } diff --git a/crates/strategy/zksync/src/executor.rs b/crates/strategy/zksync/src/executor.rs index ec4d21689..5fc761d05 100644 --- a/crates/strategy/zksync/src/executor.rs +++ b/crates/strategy/zksync/src/executor.rs @@ -7,9 +7,9 @@ use eyre::Result; use foundry_cheatcodes::strategy::CheatcodeInspectorStrategyExt; use foundry_evm::{ - backend::{strategy::BackendStrategyExt, BackendResult, DatabaseExt}, + backend::{Backend, BackendResult, DatabaseExt}, executors::{ - strategy::{EvmExecutorStrategy, ExecutorStrategy, ExecutorStrategyExt}, + strategy::{EvmExecutorStrategy, ExecutorStrategy}, Executor, }, InspectorExt, @@ -28,108 +28,96 @@ use crate::{ }; #[derive(Debug, Default, Clone)] -pub struct ZksyncExecutorStrategy { - evm: EvmExecutorStrategy, - inner: RefCell, -} +pub struct ZksyncExecutorStrategy; #[derive(Debug, Default, Clone)] -struct ZksyncExecutorStrategyInner { - inspect_context: Option, - persisted_factory_deps: HashMap>, - dual_compiled_contracts: DualCompiledContracts, - zk_env: ZkEnv, +pub struct Context { + pub inspect_context: Option, + pub persisted_factory_deps: HashMap>, + pub dual_compiled_contracts: DualCompiledContracts, + pub zk_env: ZkEnv, } impl ExecutorStrategy for ZksyncExecutorStrategy { - fn name(&self) -> &'static str { - "zk" - } + type BackendStrategy = ZksyncBackendStrategy; + type ExecutorContext = Context; - fn new_cloned(&self) -> Box { - Box::new(self.clone()) + fn backend_ctx(executor_ctx: &Self::ExecutorContext) -> super::backend::Context { + // TODO: is this right? + super::backend::Context::default() } - fn set_inspect_context(&self, other_fields: OtherFields) { - let maybe_context = get_zksync_transaction_metadata(&other_fields); - self.inner.borrow_mut().inspect_context = maybe_context; - } + // fn set_inspect_context(&self, other_fields: OtherFields) { + // let maybe_context = get_zksync_transaction_metadata(&other_fields); + // self.inner.borrow_mut().inspect_context = maybe_context; + // } - fn set_balance( - &self, - executor: &mut Executor, - address: Address, - amount: U256, - ) -> BackendResult<()> { - self.evm.set_balance(executor, address, amount)?; + fn set_balance(db: &mut dyn DatabaseExt, address: Address, amount: U256) -> BackendResult<()> { + EvmExecutorStrategy::set_balance(db, address, amount)?; let (address, slot) = foundry_zksync_core::state::get_balance_storage(address); - executor.backend.insert_account_storage(address, slot, amount)?; + db.insert_account_storage(address, slot, amount)?; Ok(()) } - fn set_nonce( - &self, - executor: &mut Executor, - address: Address, - nonce: u64, - ) -> BackendResult<()> { - self.evm.set_nonce(executor, address, nonce)?; + fn set_nonce(db: &mut dyn DatabaseExt, address: Address, nonce: u64) -> BackendResult<()> { + EvmExecutorStrategy::set_nonce(db, address, nonce)?; let (address, slot) = foundry_zksync_core::state::get_nonce_storage(address); // fetch the full nonce to preserve account's deployment nonce - let full_nonce = executor.backend.storage(address, slot)?; + let full_nonce = db.storage(address, slot)?; let full_nonce = foundry_zksync_core::state::parse_full_nonce(full_nonce); let new_full_nonce = foundry_zksync_core::state::new_full_nonce(nonce, full_nonce.deploy_nonce); - executor.backend.insert_account_storage(address, slot, new_full_nonce)?; + db.insert_account_storage(address, slot, new_full_nonce)?; Ok(()) } - fn new_backend_strategy(&self) -> Box { - Box::new(ZksyncBackendStrategy::default()) - } + // fn new_backend_strategy(&self) -> Box { + // Box::new(ZksyncBackendStrategy::default()) + // } - fn new_cheatcode_inspector_strategy(&self) -> Box { + fn new_cheatcode_inspector_strategy( + ctx: &Self::ExecutorContext, + ) -> Box { Box::new(ZksyncCheatcodeInspectorStrategy::new( - self.inner.borrow().dual_compiled_contracts.clone(), - self.inner.borrow().zk_env.clone(), + ctx.dual_compiled_contracts.clone(), + ctx.zk_env.clone(), )) } fn call_inspect( - &self, db: &mut dyn DatabaseExt, env: &mut EnvWithHandlerCfg, inspector: &mut dyn InspectorExt, + ctx: &mut Self::ExecutorContext, ) -> eyre::Result { - let self_ = self.inner.borrow(); - match self_.inspect_context.as_ref() { - None => self.evm.call_inspect(db, env, inspector), + match ctx.inspect_context.as_ref() { + None => EvmExecutorStrategy::call_inspect(db, env, inspector, &mut ()), Some(zk_tx) => foundry_zksync_core::vm::transact( - Some(&mut self_.persisted_factory_deps.clone()), + Some(&mut ctx.persisted_factory_deps.clone()), Some(zk_tx.factory_deps.clone()), zk_tx.paymaster_data.clone(), env, - &self_.zk_env, + &ctx.zk_env, db, ), } } fn transact_inspect( - &self, db: &mut dyn DatabaseExt, env: &mut EnvWithHandlerCfg, executor_env: &EnvWithHandlerCfg, inspector: &mut dyn InspectorExt, + ctx: &mut Self::ExecutorContext, ) -> eyre::Result { - let mut self_ = self.inner.borrow_mut(); - let zk_env = self_.zk_env.clone(); - match self_.inspect_context.take() { - None => self.evm.transact_inspect(db, env, executor_env, inspector), + match ctx.inspect_context.take() { + None => { + EvmExecutorStrategy::transact_inspect(db, env, executor_env, inspector, &mut ()) + } Some(zk_tx) => { // apply fork-related env instead of cheatcode handler // since it won't be set by zkEVM @@ -137,11 +125,11 @@ impl ExecutorStrategy for ZksyncExecutorStrategy { env.tx.gas_price = executor_env.tx.gas_price; foundry_zksync_core::vm::transact( - Some(&mut self_.persisted_factory_deps), + Some(&mut ctx.persisted_factory_deps), Some(zk_tx.factory_deps), zk_tx.paymaster_data, env, - &zk_env, + &ctx.zk_env, db, ) } @@ -149,49 +137,49 @@ impl ExecutorStrategy for ZksyncExecutorStrategy { } } -impl ExecutorStrategyExt for ZksyncExecutorStrategy { - fn new_cloned_ext(&self) -> Box { - Box::new(self.clone()) - } - - fn zksync_set_dual_compiled_contracts(&self, dual_compiled_contracts: DualCompiledContracts) { - self.inner.borrow_mut().dual_compiled_contracts = dual_compiled_contracts; - } - - fn zksync_set_fork_env(&self, fork_url: &str, env: &Env) -> Result<()> { - let provider = zksync_provider().with_recommended_fillers().on_http(fork_url.parse()?); - let block_number = env.block.number.try_into()?; - // TODO(zk): switch to getFeeParams call when it is implemented for anvil-zksync - let maybe_block_details = tokio::task::block_in_place(move || { - tokio::runtime::Handle::current().block_on(provider.get_block_details(block_number)) - }) - .ok() - .flatten(); - - if let Some(block_details) = maybe_block_details { - self.inner.borrow_mut().zk_env = ZkEnv { - l1_gas_price: block_details - .l1_gas_price - .try_into() - .expect("failed to convert l1_gas_price to u64"), - fair_l2_gas_price: block_details - .l2_fair_gas_price - .try_into() - .expect("failed to convert fair_l2_gas_price to u64"), - fair_pubdata_price: block_details - .fair_pubdata_price - // TODO(zk): None as a value might mean L1Pegged model - // we need to find out if it will ever be relevant to - // us - .unwrap_or_default() - .try_into() - .expect("failed to convert fair_pubdata_price to u64"), - }; - } - - Ok(()) - } -} +// impl ExecutorStrategyExt for ZksyncExecutorStrategy { +// fn new_cloned_ext(&self) -> Box { +// Box::new(self.clone()) +// } + +// fn zksync_set_dual_compiled_contracts(&self, dual_compiled_contracts: DualCompiledContracts) +// { self.inner.borrow_mut().dual_compiled_contracts = dual_compiled_contracts; +// } + +// fn zksync_set_fork_env(&self, fork_url: &str, env: &Env) -> Result<()> { +// let provider = zksync_provider().with_recommended_fillers().on_http(fork_url.parse()?); +// let block_number = env.block.number.try_into()?; +// // TODO(zk): switch to getFeeParams call when it is implemented for anvil-zksync +// let maybe_block_details = tokio::task::block_in_place(move || { +// tokio::runtime::Handle::current().block_on(provider.get_block_details(block_number)) +// }) +// .ok() +// .flatten(); + +// if let Some(block_details) = maybe_block_details { +// self.inner.borrow_mut().zk_env = ZkEnv { +// l1_gas_price: block_details +// .l1_gas_price +// .try_into() +// .expect("failed to convert l1_gas_price to u64"), +// fair_l2_gas_price: block_details +// .l2_fair_gas_price +// .try_into() +// .expect("failed to convert fair_l2_gas_price to u64"), +// fair_pubdata_price: block_details +// .fair_pubdata_price +// // TODO(zk): None as a value might mean L1Pegged model +// // we need to find out if it will ever be relevant to +// // us +// .unwrap_or_default() +// .try_into() +// .expect("failed to convert fair_pubdata_price to u64"), +// }; +// } + +// Ok(()) +// } +// } /// Retrieve metadata for zksync tx pub fn get_zksync_transaction_metadata( diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index e1299f49f..dbce6f1a8 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -22,7 +22,9 @@ use foundry_cli::{ use foundry_common::shell; use foundry_compilers::{artifacts::EvmVersion, info::ContractInfo}; use foundry_config::{figment, impl_figment_convert, Config}; -use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, utils::configure_tx_req_env}; +use foundry_evm::{ + backend::DatabaseExt as _, constants::DEFAULT_CREATE2_DEPLOYER, utils::configure_tx_req_env, +}; use revm_primitives::{AccountInfo, TxKind}; use std::path::PathBuf; @@ -240,7 +242,6 @@ impl VerifyBytecodeArgs { gen_blk_num, etherscan_metadata.evm_version()?.unwrap_or(EvmVersion::default()), evm_opts, - strategy.new_cloned_ext(), ) .await?; @@ -444,7 +445,6 @@ impl VerifyBytecodeArgs { simulation_block - 1, // env.fork_block_number etherscan_metadata.evm_version()?.unwrap_or(EvmVersion::default()), evm_opts, - strategy.new_cloned_ext(), ) .await?; env.block.number = U256::from(simulation_block); diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index ee562cea1..d1ff51cff 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -14,7 +14,7 @@ use foundry_compilers::artifacts::{BytecodeHash, CompactContractBytecode, EvmVer use foundry_config::Config; use foundry_evm::{ constants::DEFAULT_CREATE2_DEPLOYER, - executors::{strategy::ExecutorStrategyExt, TracingExecutor}, + executors::{strategy::ExecutorStrategy, TracingExecutor}, opts::EvmOpts, }; use reqwest::Url; @@ -325,7 +325,6 @@ pub async fn get_tracing_executor( fork_blk_num: u64, evm_version: EvmVersion, evm_opts: EvmOpts, - strategy: Box, ) -> Result<(Env, TracingExecutor)> { fork_config.fork_block_number = Some(fork_blk_num); fork_config.evm_version = evm_version; @@ -340,7 +339,6 @@ pub async fn get_tracing_executor( false, false, is_alphanet, - strategy, ); Ok((env, executor))