Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[do not merge] Experimenting with static strategy #789

Closed
wants to merge 27 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f1c871c
add strategy objects
nbaztec Dec 12, 2024
b38109d
remove unused parts
nbaztec Dec 12, 2024
57f32f2
fix test build
nbaztec Dec 12, 2024
06aedf4
remove use_zk completely, fix ExecutorStrategy trait
nbaztec Dec 13, 2024
5d77f8c
fix deadlock
nbaztec Dec 14, 2024
6c00749
clippy
nbaztec Dec 14, 2024
b5fd952
fix tests, startup bug
nbaztec Dec 15, 2024
9009698
switch to try_lock to prevent deadlocks
nbaztec Dec 15, 2024
bee659d
revert try_lock
nbaztec Dec 15, 2024
b5b7e5f
noop instead of panic on zksync methods for evm, clippy
nbaztec Dec 15, 2024
5ee99fc
fix warp and roll
nbaztec Dec 15, 2024
2151459
fix script
nbaztec Dec 15, 2024
e9fbf13
make call immutable
nbaztec Dec 15, 2024
7b23078
deep clone strategies on clone
nbaztec Dec 16, 2024
095903b
trigger ci
nbaztec Dec 16, 2024
da60354
revert unintended change
nbaztec Dec 16, 2024
f740454
Merge branch 'main' into nish-abstraction-strategy
nbaztec Dec 16, 2024
193f955
fix zk cheatcodes in evm context
nbaztec Dec 16, 2024
a506866
fix get_code, remove unintended sleep
nbaztec Dec 16, 2024
0e9c64f
fix script test, clippy
nbaztec Dec 16, 2024
589c8ba
Merge branch 'main' into nish-abstraction-strategy
nbaztec Dec 16, 2024
f0d0f9d
fix zk_env
nbaztec Dec 16, 2024
d80312f
guard zk provider
nbaztec Dec 16, 2024
bdba380
use blocking provider call
nbaztec Dec 16, 2024
53d5d9a
Merge branch 'main' into nish-abstraction-strategy
nbaztec Dec 16, 2024
1876c01
use Box instead of Arc<Mutex<T>>
nbaztec Dec 17, 2024
d92ad57
chore: snapshot
popzxc Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
chore: snapshot
popzxc committed Dec 18, 2024

Verified

This commit was signed with the committer’s verified signature.
popzxc Igor Aleksanov
commit d92ad5765eeba1cd9657a54e5bec99c98b6a69dc
52 changes: 24 additions & 28 deletions crates/evm/core/src/backend/cow.rs
Original file line number Diff line number Diff line change
@@ -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,
@@ -15,7 +15,7 @@ use foundry_fork_db::DatabaseError;
use revm::{
db::DatabaseRef,
primitives::{Account, AccountInfo, Bytecode, Env, EnvWithHandlerCfg, HashMap as Map, SpecId},
Database, DatabaseCommit, JournaledState,
Database, DatabaseCommit,
};
use std::{borrow::Cow, collections::BTreeMap};

@@ -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<S>>,
/// 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<S>) -> 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<S>) -> 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<S> {
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,34 +79,30 @@ 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<S>> {
if self.is_initialized {
return Some(self.backend.to_mut())
}
None
}
}

impl DatabaseExt for CowBackend<'_> {
impl<S: BackendStrategy> DatabaseExt<S> for CowBackend<'_, S> {
fn get_fork_info(&mut self, id: LocalForkId) -> eyre::Result<ForkInfo> {
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 {
fn snapshot_state(&mut self, journaled_state: &S::JournaledState, env: &Env) -> U256 {
self.backend_mut(env).snapshot_state(journaled_state, env)
}

fn revert_state(
&mut self,
id: U256,
journaled_state: &JournaledState,
journaled_state: &S::JournaledState,
current: &mut Env,
action: RevertStateSnapshotAction,
) -> Option<JournaledState> {
) -> Option<S::JournaledState> {
self.backend_mut(current).revert_state(id, journaled_state, current, action)
}

@@ -140,7 +136,7 @@ impl DatabaseExt for CowBackend<'_> {
&mut self,
id: LocalForkId,
env: &mut Env,
journaled_state: &mut JournaledState,
journaled_state: &mut S::JournaledState,
) -> eyre::Result<()> {
self.backend_mut(env).select_fork(id, env, journaled_state)
}
@@ -150,7 +146,7 @@ impl DatabaseExt for CowBackend<'_> {
id: Option<LocalForkId>,
block_number: u64,
env: &mut Env,
journaled_state: &mut JournaledState,
journaled_state: &mut S::JournaledState,
) -> eyre::Result<()> {
self.backend_mut(env).roll_fork(id, block_number, env, journaled_state)
}
@@ -160,7 +156,7 @@ impl DatabaseExt for CowBackend<'_> {
id: Option<LocalForkId>,
transaction: B256,
env: &mut Env,
journaled_state: &mut JournaledState,
journaled_state: &mut S::JournaledState,
) -> eyre::Result<()> {
self.backend_mut(env).roll_fork_to_transaction(id, transaction, env, journaled_state)
}
@@ -170,7 +166,7 @@ impl DatabaseExt for CowBackend<'_> {
id: Option<LocalForkId>,
transaction: B256,
env: Env,
journaled_state: &mut JournaledState,
journaled_state: &mut S::JournaledState,
inspector: &mut dyn InspectorExt,
) -> eyre::Result<()> {
self.backend_mut(&env).transact(id, transaction, env, journaled_state, inspector)
@@ -180,7 +176,7 @@ impl DatabaseExt for CowBackend<'_> {
&mut self,
transaction: &TransactionRequest,
env: Env,
journaled_state: &mut JournaledState,
journaled_state: &mut S::JournaledState,
inspector: &mut dyn InspectorExt,
) -> eyre::Result<()> {
self.backend_mut(&env).transact_from_tx(transaction, env, journaled_state, inspector)
@@ -205,15 +201,15 @@ impl DatabaseExt for CowBackend<'_> {
fn diagnose_revert(
&self,
callee: Address,
journaled_state: &JournaledState,
journaled_state: &S::JournaledState,
) -> Option<RevertDiagnostic> {
self.backend.diagnose_revert(callee, journaled_state)
}

fn load_allocs(
&mut self,
allocs: &BTreeMap<Address, GenesisAccount>,
journaled_state: &mut JournaledState,
journaled_state: &mut S::JournaledState,
) -> Result<(), BackendError> {
self.backend_mut(&Env::default()).load_allocs(allocs, journaled_state)
}
@@ -222,7 +218,7 @@ impl DatabaseExt for CowBackend<'_> {
&mut self,
source: &GenesisAccount,
target: &Address,
journaled_state: &mut JournaledState,
journaled_state: &mut S::JournaledState,
) -> Result<(), BackendError> {
self.backend_mut(&Env::default()).clone_account(source, target, journaled_state)
}
@@ -264,7 +260,7 @@ impl DatabaseExt for CowBackend<'_> {
}
}

impl DatabaseRef for CowBackend<'_> {
impl<S: BackendStrategy> DatabaseRef for CowBackend<'_, S> {
type Error = DatabaseError;

fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
@@ -284,7 +280,7 @@ impl DatabaseRef for CowBackend<'_> {
}
}

impl Database for CowBackend<'_> {
impl<S: BackendStrategy> Database for CowBackend<'_, S> {
type Error = DatabaseError;

fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
@@ -304,7 +300,7 @@ impl Database for CowBackend<'_> {
}
}

impl DatabaseCommit for CowBackend<'_> {
impl<S: BackendStrategy> DatabaseCommit for CowBackend<'_, S> {
fn commit(&mut self, changes: Map<Address, Account>) {
self.backend.to_mut().commit(changes)
}
270 changes: 129 additions & 141 deletions crates/evm/core/src/backend/mod.rs

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions crates/evm/core/src/backend/snapshot.rs
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@ use revm::{
};
use serde::{Deserialize, Serialize};

use super::strategy::{BackendStrategy, EvmBackendStrategy, JournaledState as _};

/// A minimal abstraction of a state at a certain point in time
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct StateSnapshot {
@@ -15,17 +17,17 @@ pub struct StateSnapshot {

/// Represents a state snapshot taken during evm execution
#[derive(Clone, Debug)]
pub struct BackendStateSnapshot<T> {
pub struct BackendStateSnapshot<T, S: BackendStrategy = EvmBackendStrategy> {
pub db: T,
/// The journaled_state state at a specific point
pub journaled_state: JournaledState,
pub journaled_state: S::JournaledState,
/// Contains the env at the time of the snapshot
pub env: Env,
}

impl<T> BackendStateSnapshot<T> {
impl<T, S: BackendStrategy> BackendStateSnapshot<T, S> {
/// Takes a new state snapshot.
pub fn new(db: T, journaled_state: JournaledState, env: Env) -> Self {
pub fn new(db: T, journaled_state: S::JournaledState, env: Env) -> Self {
Self { db, journaled_state, env }
}

@@ -36,8 +38,8 @@ impl<T> BackendStateSnapshot<T> {
/// those logs that are missing in the snapshot's journaled_state, since the current
/// journaled_state includes the same logs, we can simply replace use that See also
/// `DatabaseExt::revert`.
pub fn merge(&mut self, current: &JournaledState) {
self.journaled_state.logs.clone_from(&current.logs);
pub fn merge(&mut self, current: &S::JournaledState) {
*self.journaled_state.logs_mut() = current.logs().to_vec();
}
}

155 changes: 104 additions & 51 deletions crates/evm/core/src/backend/strategy.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,110 @@
use std::{
fmt::Debug,
sync::{Arc, Mutex},
};
use std::fmt::Debug;

use super::{BackendInner, Fork, ForkDB, ForkType, FoundryEvmInMemoryDB};
use alloy_primitives::{Address, U256};
use revm::{db::CacheDB, primitives::HashSet, DatabaseRef, JournaledState};
use serde::{Deserialize, Serialize};
use alloy_primitives::{Address, Log, U256};
use revm::{
db::CacheDB,
primitives::{EvmState, HashSet},
DatabaseRef,
};

/// A backend-agnostic trait that defines the behavior of a journaled state.
pub trait JournaledState: Sized + Clone + Debug {
// TODO: Probably should be VM-agnostic as well, e.g. we should not expose the state
// directly, but rather a set of methods to interact with it.
fn evm_state(&self) -> &EvmState;
fn evm_state_mut(&mut self) -> &mut EvmState;

fn logs(&self) -> &[Log];
fn logs_mut(&mut self) -> &mut Vec<Log>;

fn depth(&self) -> usize;
fn set_depth(&mut self, depth: usize);

fn load_account<DB: revm::Database>(
&mut self,
address: Address,
db: &mut DB,
) -> Result<
revm::interpreter::StateLoad<&mut revm::primitives::Account>,
revm::primitives::EVMError<DB::Error>,
>;

fn touch(&mut self, address: &Address);
}

impl JournaledState for revm::JournaledState {
fn evm_state(&self) -> &EvmState {
&self.state
}

fn evm_state_mut(&mut self) -> &mut EvmState {
&mut self.state
}

fn logs(&self) -> &[Log] {
&self.logs
}

fn logs_mut(&mut self) -> &mut Vec<Log> {
&mut self.logs
}

fn depth(&self) -> usize {
self.depth
}

pub struct BackendStrategyForkInfo<'a> {
pub active_fork: Option<&'a Fork>,
fn set_depth(&mut self, depth: usize) {
self.depth = depth;
}

fn load_account<DB: revm::Database>(
&mut self,
address: Address,
db: &mut DB,
) -> Result<
revm::interpreter::StateLoad<&mut revm::primitives::Account>,
revm::primitives::EVMError<DB::Error>,
> {
self.load_account(address, db)
}

fn touch(&mut self, address: &Address) {
self.touch(address)
}
}

pub struct BackendStrategyForkInfo<'a, S: BackendStrategy> {
pub active_fork: Option<&'a Fork<S>>,
pub active_type: ForkType,
pub target_type: ForkType,
}

pub trait BackendStrategy: Debug + Send + Sync {
fn name(&self) -> &'static str;

fn new_cloned(&self) -> Arc<Mutex<dyn BackendStrategy>>;
pub trait BackendStrategy: Debug + Send + Sync + Clone + Copy {
type JournaledState: JournaledState;

/// When creating or switching forks, we update the AccountInfo of the contract
fn update_fork_db(
&self,
fork_info: BackendStrategyForkInfo<'_>,
fork_info: BackendStrategyForkInfo<'_, Self>,
mem_db: &FoundryEvmInMemoryDB,
backend_inner: &BackendInner,
active_journaled_state: &mut JournaledState,
target_fork: &mut Fork,
backend_inner: &BackendInner<Self>,
active_journaled_state: &mut Self::JournaledState,
target_fork: &mut Fork<Self>,
);

/// 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,
active_journaled_state: &Self::JournaledState,
fork_journaled_state: &mut Self::JournaledState,
);

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);

fn new_journaled_state(backend_inner: &BackendInner<Self>) -> Self::JournaledState;
}

pub trait BackendStrategyExt: BackendStrategy {
fn new_cloned_ext(&self) -> Box<dyn BackendStrategyExt>;
/// Saves the storage keys for immutable variables per address.
///
/// These are required during fork to help merge the persisted addresses, as they are stored
@@ -51,29 +114,20 @@ pub trait BackendStrategyExt: BackendStrategy {
fn zksync_save_immutable_storage(&mut self, _addr: Address, _keys: HashSet<U256>) {}
}

struct _ObjectSafe(dyn BackendStrategy);

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[derive(Debug, Default, Clone, Copy)]
pub struct EvmBackendStrategy;

impl BackendStrategy for EvmBackendStrategy {
fn name(&self) -> &'static str {
"evm"
}

fn new_cloned(&self) -> Arc<Mutex<dyn BackendStrategy>> {
Arc::new(Mutex::new(self.clone()))
}
type JournaledState = revm::JournaledState;

fn update_fork_db(
&self,
fork_info: BackendStrategyForkInfo<'_>,
fork_info: BackendStrategyForkInfo<'_, Self>,
mem_db: &FoundryEvmInMemoryDB,
backend_inner: &BackendInner,
active_journaled_state: &mut JournaledState,
active_journaled_state: &mut Self::JournaledState,
target_fork: &mut Fork,
) {
self.update_fork_db_contracts(
Self::update_fork_db_contracts(
fork_info,
mem_db,
backend_inner,
@@ -83,10 +137,9 @@ impl BackendStrategy for EvmBackendStrategy {
}

fn merge_journaled_state_data(
&self,
addr: Address,
active_journaled_state: &JournaledState,
fork_journaled_state: &mut JournaledState,
active_journaled_state: &Self::JournaledState,
fork_journaled_state: &mut Self::JournaledState,
) {
EvmBackendMergeStrategy::merge_journaled_state_data(
addr,
@@ -95,25 +148,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) {
EvmBackendMergeStrategy::merge_db_account_data(addr, active, fork_db);
}
}

impl BackendStrategyExt for EvmBackendStrategy {
fn new_cloned_ext(&self) -> Box<dyn BackendStrategyExt> {
Box::new(self.clone())
fn new_journaled_state(backend_inner: &BackendInner) -> Self::JournaledState {
revm::JournaledState::new(
backend_inner.spec_id,
backend_inner.precompiles().addresses().copied().collect(),
)
}
}

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<'_>,
fork_info: BackendStrategyForkInfo<'_, Self>,
mem_db: &FoundryEvmInMemoryDB,
backend_inner: &BackendInner,
active_journaled_state: &mut JournaledState,
active_journaled_state: &mut revm::JournaledState,
target_fork: &mut Fork,
) {
let accounts = backend_inner.persistent_accounts.iter().copied();
@@ -141,7 +194,7 @@ impl EvmBackendMergeStrategy {
pub fn merge_account_data<ExtDB: DatabaseRef>(
accounts: impl IntoIterator<Item = Address>,
active: &CacheDB<ExtDB>,
active_journaled_state: &mut JournaledState,
active_journaled_state: &mut revm::JournaledState,
target_fork: &mut Fork,
) {
for addr in accounts.into_iter() {
@@ -165,8 +218,8 @@ impl EvmBackendMergeStrategy {
/// Clones the account data from the `active_journaled_state` into the `fork_journaled_state`
pub fn merge_journaled_state_data(
addr: Address,
active_journaled_state: &JournaledState,
fork_journaled_state: &mut JournaledState,
active_journaled_state: &revm::JournaledState,
fork_journaled_state: &mut revm::JournaledState,
) {
if let Some(mut acc) = active_journaled_state.state.get(&addr).cloned() {
trace!(?addr, "updating journaled_state account data");