From 267005f7e35f7738be1970993b0d134e09f5d1bc Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 26 Aug 2022 19:34:53 +0200 Subject: [PATCH 01/13] fix: revm breaking changes --- evm/src/executor/backend/fuzz.rs | 20 +++---- evm/src/executor/backend/in_memory_db.rs | 24 +++++---- evm/src/executor/backend/mod.rs | 51 +++++++++++------- evm/src/executor/fork/backend.rs | 39 +++++++------- evm/src/executor/fork/database.rs | 67 +++++++++++++----------- 5 files changed, 111 insertions(+), 90 deletions(-) diff --git a/evm/src/executor/backend/fuzz.rs b/evm/src/executor/backend/fuzz.rs index 73bf2f2c82e2..b878d6147437 100644 --- a/evm/src/executor/backend/fuzz.rs +++ b/evm/src/executor/backend/fuzz.rs @@ -1,11 +1,11 @@ use crate::{ executor::{ backend::{diagnostic::RevertDiagnostic, Backend, DatabaseExt, LocalForkId}, - fork::{CreateFork, ForkId}, + fork::{database::DbResult, CreateFork, ForkId}, }, Address, }; -use ethers::prelude::{H160, H256, U256}; +use ethers::prelude::{H256, U256}; use hashbrown::HashMap as Map; use revm::{ db::DatabaseRef, Account, AccountInfo, Bytecode, Database, Env, ExecutionResult, Inspector, @@ -138,35 +138,35 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { } impl<'a> DatabaseRef for FuzzBackendWrapper<'a> { - fn basic(&self, address: H160) -> AccountInfo { + fn basic(& self, address: Address) -> DbResult> { DatabaseRef::basic(self.backend.as_ref(), address) } - fn code_by_hash(&self, code_hash: H256) -> Bytecode { + fn code_by_hash(& self, code_hash: H256) -> DbResult { DatabaseRef::code_by_hash(self.backend.as_ref(), code_hash) } - fn storage(&self, address: H160, index: U256) -> U256 { + fn storage(& self, address: Address, index: U256) -> DbResult { DatabaseRef::storage(self.backend.as_ref(), address, index) } - fn block_hash(&self, number: U256) -> H256 { + fn block_hash(& self, number: U256) -> DbResult { DatabaseRef::block_hash(self.backend.as_ref(), number) } } impl<'a> Database for FuzzBackendWrapper<'a> { - fn basic(&mut self, address: H160) -> AccountInfo { + fn basic(&mut self, address: Address) -> DbResult> { DatabaseRef::basic(self, address) } - fn code_by_hash(&mut self, code_hash: H256) -> Bytecode { + fn code_by_hash(&mut self, code_hash: H256) -> DbResult { DatabaseRef::code_by_hash(self, code_hash) } - fn storage(&mut self, address: H160, index: U256) -> U256 { + fn storage(&mut self, address: Address, index: U256) -> DbResult { DatabaseRef::storage(self, address, index) } - fn block_hash(&mut self, number: U256) -> H256 { + fn block_hash(&mut self, number: U256) -> DbResult { DatabaseRef::block_hash(self, number) } } diff --git a/evm/src/executor/backend/in_memory_db.rs b/evm/src/executor/backend/in_memory_db.rs index 2bf9406b0b53..04f11c45d22c 100644 --- a/evm/src/executor/backend/in_memory_db.rs +++ b/evm/src/executor/backend/in_memory_db.rs @@ -1,5 +1,9 @@ //! The in memory DB -use ethers::prelude::{H160, H256, U256}; +use crate::executor::fork::database::DbResult; +use ethers::{ + prelude::{H256, U256}, + types::Address, +}; use hashbrown::HashMap as Map; use revm::{db::DatabaseRef, Account, AccountInfo, Bytecode, Database, DatabaseCommit, InMemoryDB}; @@ -21,43 +25,43 @@ impl Default for MemDb { } impl DatabaseRef for MemDb { - fn basic(&self, address: H160) -> AccountInfo { + fn basic(& self, address: Address) -> DbResult> { DatabaseRef::basic(&self.inner, address) } - fn code_by_hash(&self, code_hash: H256) -> Bytecode { + fn code_by_hash(& self, code_hash: H256) -> DbResult { DatabaseRef::code_by_hash(&self.inner, code_hash) } - fn storage(&self, address: H160, index: U256) -> U256 { + fn storage(& self, address: Address, index: U256) -> DbResult { DatabaseRef::storage(&self.inner, address, index) } - fn block_hash(&self, number: U256) -> H256 { + fn block_hash(& self, number: U256) -> DbResult { DatabaseRef::block_hash(&self.inner, number) } } impl Database for MemDb { - fn basic(&mut self, address: H160) -> AccountInfo { + fn basic(&mut self, address: Address) -> DbResult> { Database::basic(&mut self.inner, address) } - fn code_by_hash(&mut self, code_hash: H256) -> Bytecode { + fn code_by_hash(&mut self, code_hash: H256) -> DbResult { Database::code_by_hash(&mut self.inner, code_hash) } - fn storage(&mut self, address: H160, index: U256) -> U256 { + fn storage(&mut self, address: Address, index: U256) -> DbResult { Database::storage(&mut self.inner, address, index) } - fn block_hash(&mut self, number: U256) -> H256 { + fn block_hash(&mut self, number: U256) -> DbResult { Database::block_hash(&mut self.inner, number) } } impl DatabaseCommit for MemDb { - fn commit(&mut self, changes: Map) { + fn commit(&mut self, changes: Map) { DatabaseCommit::commit(&mut self.inner, changes) } } diff --git a/evm/src/executor/backend/mod.rs b/evm/src/executor/backend/mod.rs index b805ec84e721..a259d91c7e63 100644 --- a/evm/src/executor/backend/mod.rs +++ b/evm/src/executor/backend/mod.rs @@ -25,7 +25,9 @@ mod fuzz; pub mod snapshot; pub use fuzz::FuzzBackendWrapper; mod diagnostic; +use crate::executor::fork::database::DbResult; pub use diagnostic::RevertDiagnostic; + mod in_memory_db; // A `revm::Database` that is used in forking mode @@ -300,7 +302,7 @@ impl Backend { let mut backend = Self { forks, mem_db: InMemoryDB::default(), - fork_init_journaled_state: Default::default(), + fork_init_journaled_state: new_journaled_state(), active_fork_ids: None, inner: BackendInner { persistent_accounts: HashSet::from(DEFAULT_PERSISTENT_ACCOUNTS), @@ -313,7 +315,7 @@ impl Backend { backend.forks.create_fork(fork).expect("Unable to create fork"); let fork_db = ForkDB::new(fork); backend.active_fork_ids = - Some(backend.inner.insert_new_fork(fork_id.clone(), fork_db, Default::default())); + Some(backend.inner.insert_new_fork(fork_id.clone(), fork_db, new_journaled_state())); backend.inner.launched_with_fork = Some(fork_id); } @@ -399,9 +401,9 @@ impl Backend { bool private _failed; } */ - let value = self.storage(address, U256::zero()); - - value.byte(1) != 0 + self.storage(address, U256::zero()).map(|value| { + value.byte(1) != 0 + }).unwrap_or_default() } /// In addition to the `_failed` variable, `DSTest::fail()` stores a failure @@ -409,8 +411,7 @@ impl Backend { /// See pub fn is_global_failure(&self) -> bool { let index = U256::from(&b"failed"[..]); - let value = self.storage(CHEATCODE_ADDRESS, index); - value == U256::one() + self.storage(CHEATCODE_ADDRESS, index).map(|value| value == U256::one()).unwrap_or_default() } /// when creating or switching forks, we update the AccountInfo of the contract @@ -747,7 +748,7 @@ impl DatabaseExt for Backend { } impl DatabaseRef for Backend { - fn basic(&self, address: H160) -> AccountInfo { + fn basic(& self, address: Address) -> DbResult> { if let Some(db) = self.active_fork_db() { db.basic(address) } else { @@ -755,7 +756,7 @@ impl DatabaseRef for Backend { } } - fn code_by_hash(&self, code_hash: H256) -> Bytecode { + fn code_by_hash(& self, code_hash: H256) -> DbResult { if let Some(db) = self.active_fork_db() { db.code_by_hash(code_hash) } else { @@ -763,7 +764,7 @@ impl DatabaseRef for Backend { } } - fn storage(&self, address: H160, index: U256) -> U256 { + fn storage(& self, address: Address, index: U256) -> DbResult { if let Some(db) = self.active_fork_db() { DatabaseRef::storage(db, address, index) } else { @@ -771,7 +772,7 @@ impl DatabaseRef for Backend { } } - fn block_hash(&self, number: U256) -> H256 { + fn block_hash(& self, number: U256) -> DbResult { if let Some(db) = self.active_fork_db() { db.block_hash(number) } else { @@ -781,7 +782,7 @@ impl DatabaseRef for Backend { } impl<'a> DatabaseRef for &'a mut Backend { - fn basic(&self, address: H160) -> AccountInfo { + fn basic(& self, address: Address) -> DbResult> { if let Some(db) = self.active_fork_db() { DatabaseRef::basic(db, address) } else { @@ -789,7 +790,7 @@ impl<'a> DatabaseRef for &'a mut Backend { } } - fn code_by_hash(&self, code_hash: H256) -> Bytecode { + fn code_by_hash(& self, code_hash: H256) -> DbResult { if let Some(db) = self.active_fork_db() { DatabaseRef::code_by_hash(db, code_hash) } else { @@ -797,7 +798,7 @@ impl<'a> DatabaseRef for &'a mut Backend { } } - fn storage(&self, address: H160, index: U256) -> U256 { + fn storage(& self, address: Address, index: U256) -> DbResult { if let Some(db) = self.active_fork_db() { DatabaseRef::storage(db, address, index) } else { @@ -805,7 +806,7 @@ impl<'a> DatabaseRef for &'a mut Backend { } } - fn block_hash(&self, number: U256) -> H256 { + fn block_hash(& self, number: U256) -> DbResult { if let Some(db) = self.active_fork_db() { DatabaseRef::block_hash(db, number) } else { @@ -825,7 +826,7 @@ impl DatabaseCommit for Backend { } impl Database for Backend { - fn basic(&mut self, address: H160) -> AccountInfo { + fn basic(&mut self, address: Address) -> DbResult> { if let Some(db) = self.active_fork_db_mut() { db.basic(address) } else { @@ -833,7 +834,7 @@ impl Database for Backend { } } - fn code_by_hash(&mut self, code_hash: H256) -> Bytecode { + fn code_by_hash(&mut self, code_hash: H256) -> DbResult { if let Some(db) = self.active_fork_db_mut() { db.code_by_hash(code_hash) } else { @@ -841,7 +842,7 @@ impl Database for Backend { } } - fn storage(&mut self, address: H160, index: U256) -> U256 { + fn storage(&mut self, address: Address, index: U256) -> DbResult { if let Some(db) = self.active_fork_db_mut() { Database::storage(db, address, index) } else { @@ -849,7 +850,7 @@ impl Database for Backend { } } - fn block_hash(&mut self, number: U256) -> H256 { + fn block_hash(&mut self, number: U256) -> DbResult { if let Some(db) = self.active_fork_db_mut() { db.block_hash(number) } else { @@ -1130,3 +1131,15 @@ fn clone_db_account_data( } fork_db.accounts.insert(addr, acc); } + + +fn new_journaled_state () -> JournaledState { + JournaledState { + state: Default::default(), + logs: vec![], + depth: 0, + journal: vec![], + is_before_spurious_dragon: false, + num_of_precompiles: 0 + } +} \ No newline at end of file diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index 4945f7fa2e94..75bd7810c4d2 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -1,5 +1,5 @@ //! Smart caching and deduplication of requests when using a forking provider -use crate::executor::fork::{cache::FlushJsonBlockCacheDB, BlockchainDb}; +use crate::executor::fork::{cache::FlushJsonBlockCacheDB, database::DbResult, BlockchainDb}; use ethers::{ core::abi::ethereum_types::BigEndianHash, providers::Middleware, @@ -491,55 +491,54 @@ impl SharedBackend { } impl DatabaseRef for SharedBackend { - fn basic(&self, address: H160) -> AccountInfo { + fn basic(&self, address: H160) -> DbResult> { trace!( target: "sharedbackend", "request basic {:?}", address); - self.do_get_basic(address).unwrap_or_else(|_| { - panic!("Failed to send/recv `basic` for {address}"); + self.do_get_basic(address).map(Some).map_err(|err| { + error!(target: "sharedbackend", ?err, ?address, "Failed to send/recv `basic`"); + "Failed to retrieve basic account data from the fork" }) } - fn code_by_hash(&self, _address: H256) -> Bytecode { - panic!("Should not be called. Code is already loaded.") + fn code_by_hash(&self, _address: H256) -> DbResult { + Err("Should not be called. Code is already loaded.") } - fn storage(&self, address: H160, index: U256) -> U256 { + fn storage(&self, address: H160, index: U256) -> DbResult { trace!( target: "sharedbackend", "request storage {:?} at {:?}", address, index); - self.do_get_storage(address, index).unwrap_or_else(|_| { - panic!("Failed to send/recv `storage` for {address} at {index}"); + self.do_get_storage(address, index).map_err(|err| { + error!( target: "sharedbackend", ?err, ?address, ?index, "Failed to send/recv `storage`"); + "Failed to retrieve storage from the fork" }) } - fn block_hash(&self, number: U256) -> H256 { + fn block_hash(&self, number: U256) -> DbResult { if number > U256::from(u64::MAX) { - return KECCAK_EMPTY + return Ok(KECCAK_EMPTY) } let number = number.as_u64(); trace!( target: "sharedbackend", "request block hash for number {:?}", number); - self.do_get_block_hash(number).unwrap_or_else(|_| { - panic!("Failed to send/recv `block_hash` for {number}"); + self.do_get_block_hash(number).map_err(|err| { + error!(target: "sharedbackend",?err, ?number, "Failed to send/recv `block_hash`"); + "Failed to retrieve `block_hash` from the fork" }) } } #[cfg(test)] mod tests { + use super::*; use crate::executor::{ - fork::{BlockchainDbMeta, JsonBlockCacheDB}, + fork::{BlockchainDbMeta, CreateFork, JsonBlockCacheDB}, opts::EvmOpts, Backend, }; use ethers::{ providers::{Http, Provider}, solc::utils::RuntimeOrHandle, - types::Address, + types::{Address, Chain}, }; - - use crate::executor::fork::CreateFork; - use ethers::types::Chain; use foundry_config::Config; use std::{collections::BTreeSet, convert::TryFrom, path::PathBuf, sync::Arc}; - - use super::*; const ENDPOINT: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; #[test] diff --git a/evm/src/executor/fork/database.rs b/evm/src/executor/fork/database.rs index c38a5f753a24..45328a443432 100644 --- a/evm/src/executor/fork/database.rs +++ b/evm/src/executor/fork/database.rs @@ -18,6 +18,8 @@ use revm::{db::DatabaseRef, Account, AccountInfo, Bytecode, Database, DatabaseCo use std::sync::Arc; use tracing::{trace, warn}; +pub type DbResult = Result; + /// a [revm::Database] that's forked off another client /// /// The `backend` is used to retrieve (missing) data, which is then fetched from the remote @@ -149,37 +151,37 @@ impl ForkedDatabase { } impl Database for ForkedDatabase { - fn basic(&mut self, address: Address) -> AccountInfo { + fn basic(&mut self, address: Address) -> DbResult> { Database::basic(&mut self.cache_db, address) } - fn code_by_hash(&mut self, code_hash: H256) -> Bytecode { + fn code_by_hash(&mut self, code_hash: H256) -> DbResult { Database::code_by_hash(&mut self.cache_db, code_hash) } - fn storage(&mut self, address: Address, index: U256) -> U256 { + fn storage(&mut self, address: Address, index: U256) -> DbResult { Database::storage(&mut self.cache_db, address, index) } - fn block_hash(&mut self, number: U256) -> H256 { + fn block_hash(&mut self, number: U256) -> DbResult { Database::block_hash(&mut self.cache_db, number) } } impl DatabaseRef for ForkedDatabase { - fn basic(&self, address: Address) -> AccountInfo { + fn basic(&self, address: Address) -> DbResult> { self.cache_db.basic(address) } - fn code_by_hash(&self, code_hash: H256) -> Bytecode { + fn code_by_hash(&self, code_hash: H256) -> DbResult { self.cache_db.code_by_hash(code_hash) } - fn storage(&self, address: Address, index: U256) -> U256 { + fn storage(&self, address: Address, index: U256) -> DbResult { DatabaseRef::storage(&self.cache_db, address, index) } - fn block_hash(&self, number: U256) -> H256 { + fn block_hash(&self, number: U256) -> DbResult { self.cache_db.block_hash(number) } } @@ -211,41 +213,44 @@ impl ForkDbSnapshot { // and uses another db as fallback // We prioritize stored changed accounts/storage impl DatabaseRef for ForkDbSnapshot { - fn basic(&self, address: Address) -> AccountInfo { + fn basic(&self, address: Address) -> DbResult> { match self.local.accounts.get(&address) { - Some(account) => account.info.clone(), - None => self - .snapshot - .accounts - .get(&address) - .cloned() - .unwrap_or_else(|| self.local.basic(address)), + Some(account) => Ok(Some(account.info.clone())), + None => { + let mut acc = self.snapshot.accounts.get(&address).cloned(); + + if acc.is_none() { + acc = self.local.basic(address)?; + } + Ok(acc) + } } } - fn code_by_hash(&self, code_hash: H256) -> Bytecode { + fn code_by_hash(&self, code_hash: H256) -> DbResult { self.local.code_by_hash(code_hash) } - fn storage(&self, address: Address, index: U256) -> U256 { + fn storage(&self, address: Address, index: U256) -> DbResult { match self.local.accounts.get(&address) { Some(account) => match account.storage.get(&index) { - Some(entry) => *entry, - None => self - .get_storage(address, index) - .unwrap_or_else(|| DatabaseRef::storage(&self.local, address, index)), + Some(entry) => Ok(*entry), + None => match self.get_storage(address, index) { + None => DatabaseRef::storage(&self.local, address, index), + Some(storage) => Ok(storage), + }, + }, + None => match self.get_storage(address, index) { + None => DatabaseRef::storage(&self.local, address, index), + Some(storage) => Ok(storage), }, - None => self - .get_storage(address, index) - .unwrap_or_else(|| DatabaseRef::storage(&self.local, address, index)), } } - fn block_hash(&self, number: U256) -> H256 { - self.snapshot - .block_hashes - .get(&number) - .copied() - .unwrap_or_else(|| self.local.block_hash(number)) + fn block_hash(&self, number: U256) -> DbResult { + match self.snapshot.block_hashes.get(&number).copied() { + None => self.local.block_hash(number), + Some(block_hash) => Ok(block_hash), + } } } From 46a93f85b92dfad68e4c53e3e71efe758d8dfb11 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 1 Sep 2022 15:45:54 +0200 Subject: [PATCH 02/13] more refactoring --- evm/src/executor/backend/error.rs | 8 ++ evm/src/executor/backend/fuzz.rs | 24 +++-- evm/src/executor/backend/in_memory_db.rs | 36 +++---- evm/src/executor/backend/mod.rs | 114 ++++++++++++++--------- evm/src/executor/fork/backend.rs | 15 ++- evm/src/executor/fork/database.rs | 32 ++++--- 6 files changed, 143 insertions(+), 86 deletions(-) create mode 100644 evm/src/executor/backend/error.rs diff --git a/evm/src/executor/backend/error.rs b/evm/src/executor/backend/error.rs new file mode 100644 index 000000000000..7bb6f0de40a2 --- /dev/null +++ b/evm/src/executor/backend/error.rs @@ -0,0 +1,8 @@ +use ethers::types::Address; + +/// Errors that can happen when working with [`revm::Database`] +#[derive(Debug, thiserror::Error)] +pub enum DatabaseError { + #[error("Failed to fetch AccountInfo {0:?}")] + MissingAccount(Address), +} diff --git a/evm/src/executor/backend/fuzz.rs b/evm/src/executor/backend/fuzz.rs index b878d6147437..afabd4fb2670 100644 --- a/evm/src/executor/backend/fuzz.rs +++ b/evm/src/executor/backend/fuzz.rs @@ -1,6 +1,8 @@ use crate::{ executor::{ - backend::{diagnostic::RevertDiagnostic, Backend, DatabaseExt, LocalForkId}, + backend::{ + diagnostic::RevertDiagnostic, error::DatabaseError, Backend, DatabaseExt, LocalForkId, + }, fork::{database::DbResult, CreateFork, ForkId}, }, Address, @@ -138,35 +140,39 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { } impl<'a> DatabaseRef for FuzzBackendWrapper<'a> { - fn basic(& self, address: Address) -> DbResult> { + type Error = DatabaseError; + + fn basic(&self, address: Address) -> Result, Self::Error> { DatabaseRef::basic(self.backend.as_ref(), address) } - fn code_by_hash(& self, code_hash: H256) -> DbResult { + fn code_by_hash(&self, code_hash: H256) -> Result { DatabaseRef::code_by_hash(self.backend.as_ref(), code_hash) } - fn storage(& self, address: Address, index: U256) -> DbResult { + fn storage(&self, address: Address, index: U256) -> Result { DatabaseRef::storage(self.backend.as_ref(), address, index) } - fn block_hash(& self, number: U256) -> DbResult { + fn block_hash(&self, number: U256) -> Result { DatabaseRef::block_hash(self.backend.as_ref(), number) } } impl<'a> Database for FuzzBackendWrapper<'a> { - fn basic(&mut self, address: Address) -> DbResult> { + type Error = DatabaseError; + + fn basic(&mut self, address: Address) -> Result, Self::Error> { DatabaseRef::basic(self, address) } - fn code_by_hash(&mut self, code_hash: H256) -> DbResult { + fn code_by_hash(&mut self, code_hash: H256) -> Result { DatabaseRef::code_by_hash(self, code_hash) } - fn storage(&mut self, address: Address, index: U256) -> DbResult { + fn storage(&mut self, address: Address, index: U256) -> Result { DatabaseRef::storage(self, address, index) } - fn block_hash(&mut self, number: U256) -> DbResult { + fn block_hash(&mut self, number: U256) -> Result { DatabaseRef::block_hash(self, number) } } diff --git a/evm/src/executor/backend/in_memory_db.rs b/evm/src/executor/backend/in_memory_db.rs index 04f11c45d22c..4b7031f12fbb 100644 --- a/evm/src/executor/backend/in_memory_db.rs +++ b/evm/src/executor/backend/in_memory_db.rs @@ -1,5 +1,5 @@ //! The in memory DB -use crate::executor::fork::database::DbResult; +use crate::executor::{backend::error::DatabaseError, fork::database::DbResult}; use ethers::{ prelude::{H256, U256}, types::Address, @@ -25,38 +25,40 @@ impl Default for MemDb { } impl DatabaseRef for MemDb { - fn basic(& self, address: Address) -> DbResult> { - DatabaseRef::basic(&self.inner, address) + type Error = DatabaseError; + fn basic(&self, address: Address) -> Result, Self::Error> { + Ok(DatabaseRef::basic(&self.inner, address).unwrap()) } - fn code_by_hash(& self, code_hash: H256) -> DbResult { - DatabaseRef::code_by_hash(&self.inner, code_hash) + fn code_by_hash(&self, code_hash: H256) -> Result { + Ok(DatabaseRef::code_by_hash(&self.inner, code_hash).unwrap()) } - fn storage(& self, address: Address, index: U256) -> DbResult { - DatabaseRef::storage(&self.inner, address, index) + fn storage(&self, address: Address, index: U256) -> Result { + Ok(DatabaseRef::storage(&self.inner, address, index).unwrap()) } - fn block_hash(& self, number: U256) -> DbResult { - DatabaseRef::block_hash(&self.inner, number) + fn block_hash(&self, number: U256) -> Result { + Ok(DatabaseRef::block_hash(&self.inner, number).unwrap()) } } impl Database for MemDb { - fn basic(&mut self, address: Address) -> DbResult> { - Database::basic(&mut self.inner, address) + type Error = DatabaseError; + fn basic(&mut self, address: Address) -> Result, Self::Error> { + Ok(Database::basic(&mut self.inner, address).unwrap()) } - fn code_by_hash(&mut self, code_hash: H256) -> DbResult { - Database::code_by_hash(&mut self.inner, code_hash) + fn code_by_hash(&mut self, code_hash: H256) -> Result { + Ok(Database::code_by_hash(&mut self.inner, code_hash).unwrap()) } - fn storage(&mut self, address: Address, index: U256) -> DbResult { - Database::storage(&mut self.inner, address, index) + fn storage(&mut self, address: Address, index: U256) -> Result { + Ok(Database::storage(&mut self.inner, address, index).unwrap()) } - fn block_hash(&mut self, number: U256) -> DbResult { - Database::block_hash(&mut self.inner, number) + fn block_hash(&mut self, number: U256) -> Result { + Ok(Database::block_hash(&mut self.inner, number).unwrap()) } } diff --git a/evm/src/executor/backend/mod.rs b/evm/src/executor/backend/mod.rs index d184b2eb338d..6a150aa8c029 100644 --- a/evm/src/executor/backend/mod.rs +++ b/evm/src/executor/backend/mod.rs @@ -15,6 +15,7 @@ use hashbrown::HashMap as Map; pub use in_memory_db::MemDb; use revm::{ db::{CacheDB, DatabaseRef}, + precompiles::Precompiles, Account, AccountInfo, Bytecode, Database, DatabaseCommit, Env, ExecutionResult, InMemoryDB, Inspector, JournaledState, Log, TransactTo, KECCAK_EMPTY, }; @@ -25,10 +26,13 @@ mod fuzz; pub mod snapshot; pub use fuzz::FuzzBackendWrapper; mod diagnostic; -use crate::executor::inspector::cheatcodes::util::with_journaled_account; -use crate::executor::fork::database::DbResult; +use crate::executor::{ + backend::error::DatabaseError, fork::database::DbResult, + inspector::cheatcodes::util::with_journaled_account, +}; pub use diagnostic::RevertDiagnostic; +pub mod error; mod in_memory_db; // A `revm::Database` that is used in forking mode @@ -300,23 +304,28 @@ impl Backend { /// database pub fn new(forks: MultiFork, fork: Option) -> Self { // Note: this will take of registering the `fork` + let inner = BackendInner { + persistent_accounts: HashSet::from(DEFAULT_PERSISTENT_ACCOUNTS), + ..Default::default() + }; + let mut backend = Self { forks, mem_db: InMemoryDB::default(), - fork_init_journaled_state: new_journaled_state(), + fork_init_journaled_state: inner.new_journaled_state(), active_fork_ids: None, - inner: BackendInner { - persistent_accounts: HashSet::from(DEFAULT_PERSISTENT_ACCOUNTS), - ..Default::default() - }, + inner, }; if let Some(fork) = fork { let (fork_id, fork, _) = backend.forks.create_fork(fork).expect("Unable to create fork"); let fork_db = ForkDB::new(fork); - let fork_ids = - backend.inner.insert_new_fork(fork_id.clone(), fork_db, Default::default()); + let fork_ids = backend.inner.insert_new_fork( + fork_id.clone(), + fork_db, + backend.inner.new_journaled_state(), + ); backend.inner.launched_with_fork = Some((fork_id, fork_ids.0, fork_ids.1)); backend.active_fork_ids = Some(fork_ids); } @@ -329,7 +338,7 @@ impl Backend { Self { forks: self.forks.clone(), mem_db: InMemoryDB::default(), - fork_init_journaled_state: Default::default(), + fork_init_journaled_state: self.inner.new_journaled_state(), active_fork_ids: None, inner: Default::default(), } @@ -371,6 +380,13 @@ impl Backend { self } + /// Sets the caller address + pub fn set_precompiles(&mut self, precompiles: Precompiles) -> &mut Self { + trace!("setting precompiles"); + self.inner.precompiles = precompiles; + self + } + /// Returns the address of the set `DSTest` contract pub fn test_contract_address(&self) -> Option
{ self.inner.test_contract_address @@ -403,9 +419,7 @@ impl Backend { bool private _failed; } */ - self.storage(address, U256::zero()).map(|value| { - value.byte(1) != 0 - }).unwrap_or_default() + self.storage(address, U256::zero()).map(|value| value.byte(1) != 0).unwrap_or_default() } /// In addition to the `_failed` variable, `DSTest::fail()` stores a failure @@ -413,7 +427,7 @@ impl Backend { /// See pub fn is_global_failure(&self) -> bool { let index = U256::from(&b"failed"[..]); - self.storage(CHEATCODE_ADDRESS, index).map(|value| value == U256::one()).unwrap_or_default() + self.storage(CHEATCODE_ADDRESS, index).map(|value| value == U256::one()).unwrap_or_default() } /// when creating or switching forks, we update the AccountInfo of the contract @@ -524,12 +538,18 @@ impl Backend { INSP: Inspector, { self.set_caller(env.tx.caller); + self.set_precompiles(Precompiles::new(env.cfg.spec_id.to_precompile_id()).clone()); if let TransactTo::Call(to) = env.tx.transact_to { self.set_test_contract(to); } revm::evm_inner::(&mut env, self, &mut inspector).transact() } + /// Returns true if the address is a precompile + pub fn is_existing_precompile(&self, addr: &Address) -> bool { + self.inner.precompiles.contains(addr) + } + /// Ths will clean up already loaded accounts that would be initialized without the correct data /// from the fork /// @@ -539,13 +559,13 @@ impl Backend { /// This account data then would not match the account data of a fork if it exists. /// So when the first fork is initialized we replace these accounts with the actual account as /// it exists on the fork. - fn prepare_init_journal_state(&mut self) { + fn prepare_init_journal_state(&mut self) -> Result<(), DatabaseError> { let loaded_accounts = self .fork_init_journaled_state .state .iter() .filter(|(addr, acc)| { - !acc.is_existing_precompile && acc.is_touched && !self.is_persistent(addr) + !self.is_existing_precompile(*addr) && acc.is_touched && !self.is_persistent(addr) }) .map(|(addr, _)| addr) .copied() @@ -555,13 +575,17 @@ impl Backend { let mut journaled_state = self.fork_init_journaled_state.clone(); for loaded_account in loaded_accounts.iter().copied() { trace!(?loaded_account, "replacing account on init"); - let fork_account = fork.db.basic(loaded_account); + let fork_account = fork + .db + .basic(loaded_account)? + .ok_or_else(|| DatabaseError: MissingAccount(loaded_account))?; let init_account = journaled_state.state.get_mut(&loaded_account).expect("exists; qed"); init_account.info = fork_account; } fork.journaled_state = journaled_state; } + Ok(()) } } @@ -725,7 +749,7 @@ impl DatabaseExt for Backend { // for all future forks trace!("recording fork init journaled_state"); self.fork_init_journaled_state = journaled_state.clone(); - self.prepare_init_journal_state(); + self.prepare_init_journal_state()?; } // update the shared state and track @@ -846,7 +870,9 @@ impl DatabaseExt for Backend { } impl DatabaseRef for Backend { - fn basic(& self, address: Address) -> DbResult> { + type Error = DatabaseError; + + fn basic(&self, address: Address) -> Result, Self::Error> { if let Some(db) = self.active_fork_db() { db.basic(address) } else { @@ -854,7 +880,7 @@ impl DatabaseRef for Backend { } } - fn code_by_hash(& self, code_hash: H256) -> DbResult { + fn code_by_hash(&self, code_hash: H256) -> Result { if let Some(db) = self.active_fork_db() { db.code_by_hash(code_hash) } else { @@ -862,7 +888,7 @@ impl DatabaseRef for Backend { } } - fn storage(& self, address: Address, index: U256) -> DbResult { + fn storage(&self, address: Address, index: U256) -> Result { if let Some(db) = self.active_fork_db() { DatabaseRef::storage(db, address, index) } else { @@ -870,7 +896,7 @@ impl DatabaseRef for Backend { } } - fn block_hash(& self, number: U256) -> DbResult { + fn block_hash(&self, number: U256) -> Result { if let Some(db) = self.active_fork_db() { db.block_hash(number) } else { @@ -880,7 +906,8 @@ impl DatabaseRef for Backend { } impl<'a> DatabaseRef for &'a mut Backend { - fn basic(& self, address: Address) -> DbResult> { + type Error = DatabaseError; + fn basic(&self, address: Address) -> Result, Self::Error> { if let Some(db) = self.active_fork_db() { DatabaseRef::basic(db, address) } else { @@ -888,7 +915,7 @@ impl<'a> DatabaseRef for &'a mut Backend { } } - fn code_by_hash(& self, code_hash: H256) -> DbResult { + fn code_by_hash(&self, code_hash: H256) -> Result { if let Some(db) = self.active_fork_db() { DatabaseRef::code_by_hash(db, code_hash) } else { @@ -896,7 +923,7 @@ impl<'a> DatabaseRef for &'a mut Backend { } } - fn storage(& self, address: Address, index: U256) -> DbResult { + fn storage(&self, address: Address, index: U256) -> Result { if let Some(db) = self.active_fork_db() { DatabaseRef::storage(db, address, index) } else { @@ -904,7 +931,7 @@ impl<'a> DatabaseRef for &'a mut Backend { } } - fn block_hash(& self, number: U256) -> DbResult { + fn block_hash(&self, number: U256) -> Result { if let Some(db) = self.active_fork_db() { DatabaseRef::block_hash(db, number) } else { @@ -924,7 +951,8 @@ impl DatabaseCommit for Backend { } impl Database for Backend { - fn basic(&mut self, address: Address) -> DbResult> { + type Error = DatabaseError; + fn basic(&mut self, address: Address) -> Result, Self::Error> { if let Some(db) = self.active_fork_db_mut() { db.basic(address) } else { @@ -932,7 +960,7 @@ impl Database for Backend { } } - fn code_by_hash(&mut self, code_hash: H256) -> DbResult { + fn code_by_hash(&mut self, code_hash: H256) -> Result { if let Some(db) = self.active_fork_db_mut() { db.code_by_hash(code_hash) } else { @@ -940,7 +968,7 @@ impl Database for Backend { } } - fn storage(&mut self, address: Address, index: U256) -> DbResult { + fn storage(&mut self, address: Address, index: U256) -> Result { if let Some(db) = self.active_fork_db_mut() { Database::storage(db, address, index) } else { @@ -948,7 +976,7 @@ impl Database for Backend { } } - fn block_hash(&mut self, number: U256) -> DbResult { + fn block_hash(&mut self, number: U256) -> Result { if let Some(db) = self.active_fork_db_mut() { db.block_hash(number) } else { @@ -1040,6 +1068,8 @@ pub struct BackendInner { /// /// See also [`clone_data()`] pub persistent_accounts: HashSet
, + /// The configured precompiles + pub precompiles: Precompiles, } // === impl BackendInner === @@ -1180,6 +1210,18 @@ impl BackendInner { pub fn is_empty(&self) -> bool { self.issued_local_fork_ids.is_empty() } + + /// Returns a new, empty, `JournaledState` with set precompiles + pub fn new_journaled_state(&self) -> JournaledState { + JournaledState { + state: Default::default(), + logs: vec![], + depth: 0, + journal: vec![], + is_before_spurious_dragon: false, + num_of_precompiles: self.precompiles.len(), + } + } } /// This updates the currently used env with the fork's environment @@ -1229,15 +1271,3 @@ fn clone_db_account_data( } fork_db.accounts.insert(addr, acc); } - - -fn new_journaled_state () -> JournaledState { - JournaledState { - state: Default::default(), - logs: vec![], - depth: 0, - journal: vec![], - is_before_spurious_dragon: false, - num_of_precompiles: 0 - } -} \ No newline at end of file diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index 75bd7810c4d2..bbbccebfd7d4 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -1,5 +1,8 @@ //! Smart caching and deduplication of requests when using a forking provider -use crate::executor::fork::{cache::FlushJsonBlockCacheDB, database::DbResult, BlockchainDb}; +use crate::executor::{ + backend::error::DatabaseError, + fork::{cache::FlushJsonBlockCacheDB, database::DbResult, BlockchainDb}, +}; use ethers::{ core::abi::ethereum_types::BigEndianHash, providers::Middleware, @@ -491,7 +494,9 @@ impl SharedBackend { } impl DatabaseRef for SharedBackend { - fn basic(&self, address: H160) -> DbResult> { + type Error = DatabaseError; + + fn basic(&self, address: H160) -> Result, Self::Error> { trace!( target: "sharedbackend", "request basic {:?}", address); self.do_get_basic(address).map(Some).map_err(|err| { error!(target: "sharedbackend", ?err, ?address, "Failed to send/recv `basic`"); @@ -499,11 +504,11 @@ impl DatabaseRef for SharedBackend { }) } - fn code_by_hash(&self, _address: H256) -> DbResult { + fn code_by_hash(&self, _address: H256) -> Result { Err("Should not be called. Code is already loaded.") } - fn storage(&self, address: H160, index: U256) -> DbResult { + fn storage(&self, address: H160, index: U256) -> Result { trace!( target: "sharedbackend", "request storage {:?} at {:?}", address, index); self.do_get_storage(address, index).map_err(|err| { error!( target: "sharedbackend", ?err, ?address, ?index, "Failed to send/recv `storage`"); @@ -511,7 +516,7 @@ impl DatabaseRef for SharedBackend { }) } - fn block_hash(&self, number: U256) -> DbResult { + fn block_hash(&self, number: U256) -> Result { if number > U256::from(u64::MAX) { return Ok(KECCAK_EMPTY) } diff --git a/evm/src/executor/fork/database.rs b/evm/src/executor/fork/database.rs index 45328a443432..087a1b0d449c 100644 --- a/evm/src/executor/fork/database.rs +++ b/evm/src/executor/fork/database.rs @@ -2,7 +2,7 @@ use crate::{ executor::{ - backend::snapshot::StateSnapshot, + backend::{error::DatabaseError, snapshot::StateSnapshot}, fork::{BlockchainDb, SharedBackend}, snapshot::Snapshots, }, @@ -151,37 +151,41 @@ impl ForkedDatabase { } impl Database for ForkedDatabase { - fn basic(&mut self, address: Address) -> DbResult> { + type Error = DatabaseError; + + fn basic(&mut self, address: Address) -> Result, Self::Error> { Database::basic(&mut self.cache_db, address) } - fn code_by_hash(&mut self, code_hash: H256) -> DbResult { + fn code_by_hash(&mut self, code_hash: H256) -> Result { Database::code_by_hash(&mut self.cache_db, code_hash) } - fn storage(&mut self, address: Address, index: U256) -> DbResult { + fn storage(&mut self, address: Address, index: U256) -> Result { Database::storage(&mut self.cache_db, address, index) } - fn block_hash(&mut self, number: U256) -> DbResult { + fn block_hash(&mut self, number: U256) -> Result { Database::block_hash(&mut self.cache_db, number) } } impl DatabaseRef for ForkedDatabase { - fn basic(&self, address: Address) -> DbResult> { + type Error = DatabaseError; + + fn basic(&self, address: Address) -> Result, Self::Error> { self.cache_db.basic(address) } - fn code_by_hash(&self, code_hash: H256) -> DbResult { + fn code_by_hash(&self, code_hash: H256) -> Result { self.cache_db.code_by_hash(code_hash) } - fn storage(&self, address: Address, index: U256) -> DbResult { + fn storage(&self, address: Address, index: U256) -> Result { DatabaseRef::storage(&self.cache_db, address, index) } - fn block_hash(&self, number: U256) -> DbResult { + fn block_hash(&self, number: U256) -> Result { self.cache_db.block_hash(number) } } @@ -213,7 +217,9 @@ impl ForkDbSnapshot { // and uses another db as fallback // We prioritize stored changed accounts/storage impl DatabaseRef for ForkDbSnapshot { - fn basic(&self, address: Address) -> DbResult> { + type Error = DatabaseError; + + fn basic(&self, address: Address) -> Result, Self::Error> { match self.local.accounts.get(&address) { Some(account) => Ok(Some(account.info.clone())), None => { @@ -227,11 +233,11 @@ impl DatabaseRef for ForkDbSnapshot { } } - fn code_by_hash(&self, code_hash: H256) -> DbResult { + fn code_by_hash(&self, code_hash: H256) -> Result { self.local.code_by_hash(code_hash) } - fn storage(&self, address: Address, index: U256) -> DbResult { + fn storage(&self, address: Address, index: U256) -> Result { match self.local.accounts.get(&address) { Some(account) => match account.storage.get(&index) { Some(entry) => Ok(*entry), @@ -247,7 +253,7 @@ impl DatabaseRef for ForkDbSnapshot { } } - fn block_hash(&self, number: U256) -> DbResult { + fn block_hash(&self, number: U256) -> Result { match self.snapshot.block_hashes.get(&number).copied() { None => self.local.block_hash(number), Some(block_hash) => Ok(block_hash), From 6e6e0b557c13ad4f1f44beb7475bd67056a55d90 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 1 Sep 2022 19:38:06 +0200 Subject: [PATCH 03/13] migrate revm --- evm/src/executor/backend/error.rs | 62 ++++++- evm/src/executor/backend/fuzz.rs | 2 +- evm/src/executor/backend/in_memory_db.rs | 18 +- evm/src/executor/backend/mod.rs | 102 +++++++----- evm/src/executor/fork/backend.rs | 154 +++++++++++------- evm/src/executor/fork/database.rs | 2 - evm/src/executor/inspector/cheatcodes/env.rs | 127 +++++++++------ .../executor/inspector/cheatcodes/expect.rs | 13 +- evm/src/executor/inspector/cheatcodes/mod.rs | 24 ++- evm/src/executor/inspector/cheatcodes/util.rs | 36 ++-- evm/src/executor/inspector/debugger.rs | 60 +++---- evm/src/executor/inspector/tracer.rs | 2 +- evm/src/executor/mod.rs | 51 ++++-- 13 files changed, 424 insertions(+), 229 deletions(-) diff --git a/evm/src/executor/backend/error.rs b/evm/src/executor/backend/error.rs index 7bb6f0de40a2..ee4e2e9df390 100644 --- a/evm/src/executor/backend/error.rs +++ b/evm/src/executor/backend/error.rs @@ -1,8 +1,68 @@ -use ethers::types::Address; +use crate::executor::inspector::cheatcodes::util; +use bytes::Bytes; +use ethers::{ + abi::AbiEncode, + types::{Address, H256, U256}, +}; +use futures::channel::mpsc::{SendError, TrySendError}; +use std::{ + convert::Infallible, + sync::{mpsc::RecvError, Arc}, +}; + +/// Result alias with `DatabaseError` as error +pub type DatabaseResult = Result; /// Errors that can happen when working with [`revm::Database`] #[derive(Debug, thiserror::Error)] pub enum DatabaseError { #[error("Failed to fetch AccountInfo {0:?}")] MissingAccount(Address), + #[error("Could should already be loaded: {0:?}")] + MissingCode(H256), + #[error(transparent)] + Recv(#[from] RecvError), + #[error(transparent)] + Send(#[from] SendError), + #[error("{0}")] + Message(String), + #[error("Failed to get account for {0:?}: {0:?}")] + GetAccount(Address, Arc), + #[error("Failed to get storage for {0:?} at {1:?}: {2:?}")] + GetStorage(Address, U256, Arc), + #[error("Failed to get block hash for {0:?}: {1:?}")] + GetBlockHash(u64, Arc), + #[error( + "CREATE2 Deployer not present on this chain. [0x4e59b44847b379578588920ca78fbf26c0b4956c]" + )] + MissingCreate2Deployer, +} + +impl DatabaseError { + /// Create a new error with a message + pub fn msg(msg: impl Into) -> Self { + DatabaseError::Message(msg.into()) + } + + /// Returns the abi encoded error + pub fn err_encoded(&self) -> Bytes { + util::encode_error(self) + } + + /// Returns the error as abi encoded String + pub fn string_encoded(&self) -> Bytes { + self.to_string().encode().into() + } +} + +impl From> for DatabaseError { + fn from(err: TrySendError) -> Self { + err.into_send_error().into() + } +} + +impl From for DatabaseError { + fn from(never: Infallible) -> Self { + match never {} + } } diff --git a/evm/src/executor/backend/fuzz.rs b/evm/src/executor/backend/fuzz.rs index afabd4fb2670..ea1f3a8facad 100644 --- a/evm/src/executor/backend/fuzz.rs +++ b/evm/src/executor/backend/fuzz.rs @@ -3,7 +3,7 @@ use crate::{ backend::{ diagnostic::RevertDiagnostic, error::DatabaseError, Backend, DatabaseExt, LocalForkId, }, - fork::{database::DbResult, CreateFork, ForkId}, + fork::{CreateFork, ForkId}, }, Address, }; diff --git a/evm/src/executor/backend/in_memory_db.rs b/evm/src/executor/backend/in_memory_db.rs index 4b7031f12fbb..48ebcef9f07d 100644 --- a/evm/src/executor/backend/in_memory_db.rs +++ b/evm/src/executor/backend/in_memory_db.rs @@ -1,5 +1,5 @@ //! The in memory DB -use crate::executor::{backend::error::DatabaseError, fork::database::DbResult}; +use crate::executor::backend::error::DatabaseError; use ethers::{ prelude::{H256, U256}, types::Address, @@ -27,38 +27,38 @@ impl Default for MemDb { impl DatabaseRef for MemDb { type Error = DatabaseError; fn basic(&self, address: Address) -> Result, Self::Error> { - Ok(DatabaseRef::basic(&self.inner, address).unwrap()) + Ok(DatabaseRef::basic(&self.inner, address)?) } fn code_by_hash(&self, code_hash: H256) -> Result { - Ok(DatabaseRef::code_by_hash(&self.inner, code_hash).unwrap()) + Ok(DatabaseRef::code_by_hash(&self.inner, code_hash)?) } fn storage(&self, address: Address, index: U256) -> Result { - Ok(DatabaseRef::storage(&self.inner, address, index).unwrap()) + Ok(DatabaseRef::storage(&self.inner, address, index)?) } fn block_hash(&self, number: U256) -> Result { - Ok(DatabaseRef::block_hash(&self.inner, number).unwrap()) + Ok(DatabaseRef::block_hash(&self.inner, number)?) } } impl Database for MemDb { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { - Ok(Database::basic(&mut self.inner, address).unwrap()) + Ok(Database::basic(&mut self.inner, address)?) } fn code_by_hash(&mut self, code_hash: H256) -> Result { - Ok(Database::code_by_hash(&mut self.inner, code_hash).unwrap()) + Ok(Database::code_by_hash(&mut self.inner, code_hash)?) } fn storage(&mut self, address: Address, index: U256) -> Result { - Ok(Database::storage(&mut self.inner, address, index).unwrap()) + Ok(Database::storage(&mut self.inner, address, index)?) } fn block_hash(&mut self, number: U256) -> Result { - Ok(Database::block_hash(&mut self.inner, number).unwrap()) + Ok(Database::block_hash(&mut self.inner, number)?) } } diff --git a/evm/src/executor/backend/mod.rs b/evm/src/executor/backend/mod.rs index 6a150aa8c029..b0dd445248e3 100644 --- a/evm/src/executor/backend/mod.rs +++ b/evm/src/executor/backend/mod.rs @@ -17,7 +17,7 @@ use revm::{ db::{CacheDB, DatabaseRef}, precompiles::Precompiles, Account, AccountInfo, Bytecode, Database, DatabaseCommit, Env, ExecutionResult, InMemoryDB, - Inspector, JournaledState, Log, TransactTo, KECCAK_EMPTY, + Inspector, JournaledState, Log, SpecId, TransactTo, KECCAK_EMPTY, }; use std::collections::{HashMap, HashSet}; use tracing::{trace, warn}; @@ -27,8 +27,7 @@ pub mod snapshot; pub use fuzz::FuzzBackendWrapper; mod diagnostic; use crate::executor::{ - backend::error::DatabaseError, fork::database::DbResult, - inspector::cheatcodes::util::with_journaled_account, + backend::error::DatabaseError, inspector::cheatcodes::util::with_journaled_account, }; pub use diagnostic::RevertDiagnostic; @@ -52,7 +51,7 @@ const DEFAULT_PERSISTENT_ACCOUNTS: [H160; 2] = [CHEATCODE_ADDRESS, DEFAULT_CREAT /// An extension trait that allows us to easily extend the `revm::Inspector` capabilities #[auto_impl::auto_impl(&mut, Box)] -pub trait DatabaseExt: Database { +pub trait DatabaseExt: Database { /// Creates a new snapshot at the current point of execution. /// /// A snapshot is associated with a new unique id that's created for the snapshot. @@ -380,10 +379,10 @@ impl Backend { self } - /// Sets the caller address - pub fn set_precompiles(&mut self, precompiles: Precompiles) -> &mut Self { - trace!("setting precompiles"); - self.inner.precompiles = precompiles; + /// Sets the current spec id + pub fn set_spec_id(&mut self, spec_id: SpecId) -> &mut Self { + trace!("setting precompile id"); + self.inner.precompile_id = spec_id.to_precompile_id(); self } @@ -538,7 +537,7 @@ impl Backend { INSP: Inspector, { self.set_caller(env.tx.caller); - self.set_precompiles(Precompiles::new(env.cfg.spec_id.to_precompile_id()).clone()); + self.set_spec_id(env.cfg.spec_id); if let TransactTo::Call(to) = env.tx.transact_to { self.set_test_contract(to); } @@ -547,7 +546,7 @@ impl Backend { /// Returns true if the address is a precompile pub fn is_existing_precompile(&self, addr: &Address) -> bool { - self.inner.precompiles.contains(addr) + self.inner.precompiles().contains(addr) } /// Ths will clean up already loaded accounts that would be initialized without the correct data @@ -565,7 +564,7 @@ impl Backend { .state .iter() .filter(|(addr, acc)| { - !self.is_existing_precompile(*addr) && acc.is_touched && !self.is_persistent(addr) + !self.is_existing_precompile(addr) && acc.is_touched && !self.is_persistent(addr) }) .map(|(addr, _)| addr) .copied() @@ -578,7 +577,7 @@ impl Backend { let fork_account = fork .db .basic(loaded_account)? - .ok_or_else(|| DatabaseError: MissingAccount(loaded_account))?; + .ok_or(DatabaseError::MissingAccount(loaded_account))?; let init_account = journaled_state.state.get_mut(&loaded_account).expect("exists; qed"); init_account.info = fork_account; @@ -639,7 +638,7 @@ impl DatabaseExt for Backend { if !fork.db.accounts.contains_key(&caller) { // update the caller account which is required by the evm fork.db.insert_account_info(caller, caller_account.clone()); - with_journaled_account( + let _ = with_journaled_account( &mut fork.journaled_state, &mut fork.db, caller, @@ -732,7 +731,7 @@ impl DatabaseExt for Backend { // we also need to initialize and touch the caller if let Some(acc) = caller_account { target_fork.db.insert_account_info(caller, acc); - with_journaled_account( + let _ = with_journaled_account( &mut target_fork.journaled_state, &mut target_fork.db, caller, @@ -876,7 +875,7 @@ impl DatabaseRef for Backend { if let Some(db) = self.active_fork_db() { db.basic(address) } else { - self.mem_db.basic(address) + Ok(self.mem_db.basic(address)?) } } @@ -884,7 +883,7 @@ impl DatabaseRef for Backend { if let Some(db) = self.active_fork_db() { db.code_by_hash(code_hash) } else { - self.mem_db.code_by_hash(code_hash) + Ok(self.mem_db.code_by_hash(code_hash)?) } } @@ -892,7 +891,7 @@ impl DatabaseRef for Backend { if let Some(db) = self.active_fork_db() { DatabaseRef::storage(db, address, index) } else { - DatabaseRef::storage(&self.mem_db, address, index) + Ok(DatabaseRef::storage(&self.mem_db, address, index)?) } } @@ -900,7 +899,7 @@ impl DatabaseRef for Backend { if let Some(db) = self.active_fork_db() { db.block_hash(number) } else { - self.mem_db.block_hash(number) + Ok(self.mem_db.block_hash(number)?) } } } @@ -911,7 +910,7 @@ impl<'a> DatabaseRef for &'a mut Backend { if let Some(db) = self.active_fork_db() { DatabaseRef::basic(db, address) } else { - DatabaseRef::basic(&self.mem_db, address) + Ok(DatabaseRef::basic(&self.mem_db, address)?) } } @@ -919,7 +918,7 @@ impl<'a> DatabaseRef for &'a mut Backend { if let Some(db) = self.active_fork_db() { DatabaseRef::code_by_hash(db, code_hash) } else { - DatabaseRef::code_by_hash(&self.mem_db, code_hash) + Ok(DatabaseRef::code_by_hash(&self.mem_db, code_hash)?) } } @@ -927,7 +926,7 @@ impl<'a> DatabaseRef for &'a mut Backend { if let Some(db) = self.active_fork_db() { DatabaseRef::storage(db, address, index) } else { - DatabaseRef::storage(&self.mem_db, address, index) + Ok(DatabaseRef::storage(&self.mem_db, address, index)?) } } @@ -935,7 +934,7 @@ impl<'a> DatabaseRef for &'a mut Backend { if let Some(db) = self.active_fork_db() { DatabaseRef::block_hash(db, number) } else { - DatabaseRef::block_hash(&self.mem_db, number) + Ok(DatabaseRef::block_hash(&self.mem_db, number)?) } } } @@ -956,7 +955,7 @@ impl Database for Backend { if let Some(db) = self.active_fork_db_mut() { db.basic(address) } else { - self.mem_db.basic(address) + Ok(self.mem_db.basic(address)?) } } @@ -964,7 +963,7 @@ impl Database for Backend { if let Some(db) = self.active_fork_db_mut() { db.code_by_hash(code_hash) } else { - self.mem_db.code_by_hash(code_hash) + Ok(self.mem_db.code_by_hash(code_hash)?) } } @@ -972,7 +971,7 @@ impl Database for Backend { if let Some(db) = self.active_fork_db_mut() { Database::storage(db, address, index) } else { - Database::storage(&mut self.mem_db, address, index) + Ok(Database::storage(&mut self.mem_db, address, index)?) } } @@ -980,7 +979,7 @@ impl Database for Backend { if let Some(db) = self.active_fork_db_mut() { db.block_hash(number) } else { - self.mem_db.block_hash(number) + Ok(self.mem_db.block_hash(number)?) } } } @@ -1006,17 +1005,21 @@ pub struct Fork { impl Fork { /// Returns true if the account is a contract pub fn is_contract(&self, acc: Address) -> bool { - self.db.basic(acc).code_hash != KECCAK_EMPTY || - self.journaled_state - .state - .get(&acc) - .map(|acc| acc.info.code_hash != KECCAK_EMPTY) - .unwrap_or_default() + if let Ok(Some(acc)) = self.db.basic(acc) { + if acc.code_hash != KECCAK_EMPTY { + return true + } + } + self.journaled_state + .state + .get(&acc) + .map(|acc| acc.info.code_hash != KECCAK_EMPTY) + .unwrap_or_default() } } /// Container type for various Backend related data -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct BackendInner { /// Stores the `ForkId` of the fork the `Backend` launched with from the start. /// @@ -1068,8 +1071,8 @@ pub struct BackendInner { /// /// See also [`clone_data()`] pub persistent_accounts: HashSet
, - /// The configured precompiles - pub precompiles: Precompiles, + /// The configured precompile spec id + pub precompile_id: revm::precompiles::SpecId, } // === impl BackendInner === @@ -1211,15 +1214,30 @@ impl BackendInner { self.issued_local_fork_ids.is_empty() } + pub fn precompiles(&self) -> &'static Precompiles { + Precompiles::new(self.precompile_id) + } + /// Returns a new, empty, `JournaledState` with set precompiles pub fn new_journaled_state(&self) -> JournaledState { - JournaledState { - state: Default::default(), - logs: vec![], - depth: 0, - journal: vec![], - is_before_spurious_dragon: false, - num_of_precompiles: self.precompiles.len(), + JournaledState::new(self.precompiles().len()) + } +} + +impl Default for BackendInner { + fn default() -> Self { + Self { + launched_with_fork: None, + issued_local_fork_ids: Default::default(), + created_forks: Default::default(), + forks: vec![], + snapshots: Default::default(), + has_failure_snapshot: false, + test_contract_address: None, + caller: None, + next_fork_id: Default::default(), + persistent_accounts: Default::default(), + precompile_id: revm::precompiles::SpecId::LATEST, } } } diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index bbbccebfd7d4..d41392281b90 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -1,7 +1,7 @@ //! Smart caching and deduplication of requests when using a forking provider use crate::executor::{ - backend::error::DatabaseError, - fork::{cache::FlushJsonBlockCacheDB, database::DbResult, BlockchainDb}, + backend::error::{DatabaseError, DatabaseResult}, + fork::{cache::FlushJsonBlockCacheDB, BlockchainDb}, }; use ethers::{ core::abi::ethereum_types::BigEndianHash, @@ -9,6 +9,7 @@ use ethers::{ types::{Address, BlockId, Bytes, H160, H256, U256}, utils::keccak256, }; + use futures::{ channel::mpsc::{channel, Receiver, Sender}, stream::Stream, @@ -31,6 +32,10 @@ type AccountFuture = type StorageFuture = Pin, Address, U256)> + Send>>; type BlockHashFuture = Pin, u64)> + Send>>; +type AccountInfoSender = OneshotSender>; +type StorageSender = OneshotSender>; +type BlockHashSender = OneshotSender>; + /// Request variants that are executed by the provider enum ProviderRequest { Account(AccountFuture), @@ -42,11 +47,11 @@ enum ProviderRequest { #[derive(Debug)] enum BackendRequest { /// Fetch the account info - Basic(Address, OneshotSender), + Basic(Address, AccountInfoSender), /// Fetch a storage slot - Storage(Address, U256, OneshotSender), + Storage(Address, U256, StorageSender), /// Fetch a block hash - BlockHash(u64, OneshotSender), + BlockHash(u64, BlockHashSender), /// Sets the pinned block to fetch data from SetPinnedBlock(BlockId), } @@ -61,13 +66,13 @@ pub struct BackendHandler { /// Stores all the data. db: BlockchainDb, /// Requests currently in progress - pending_requests: Vec>, + pending_requests: Vec>, /// Listeners that wait for a `get_account` related response - account_requests: HashMap>>, + account_requests: HashMap>, /// Listeners that wait for a `get_storage_at` response - storage_requests: HashMap<(Address, U256), Vec>>, + storage_requests: HashMap<(Address, U256), Vec>, /// Listeners that wait for a `get_block` response - block_requests: HashMap>>, + block_requests: HashMap>, /// Incoming commands. incoming: Receiver, /// unprocessed queued requests @@ -112,7 +117,7 @@ where trace!(target: "backendhandler", "received request basic address={:?}", addr); let acc = self.db.accounts().read().get(&addr).cloned(); if let Some(basic) = acc { - let _ = sender.send(basic); + let _ = sender.send(Ok(basic)); } else { self.request_account(addr, sender); } @@ -120,7 +125,7 @@ where BackendRequest::BlockHash(number, sender) => { let hash = self.db.block_hashes().read().get(&U256::from(number)).cloned(); if let Some(hash) = hash { - let _ = sender.send(hash); + let _ = sender.send(Ok(hash)); } else { self.request_hash(number, sender); } @@ -130,7 +135,7 @@ where let value = self.db.storage().read().get(&addr).and_then(|acc| acc.get(&idx).copied()); if let Some(value) = value { - let _ = sender.send(value); + let _ = sender.send(Ok(value)); } else { // account present but not storage -> fetch storage self.request_account_storage(addr, idx, sender); @@ -143,12 +148,7 @@ where } /// process a request for account's storage - fn request_account_storage( - &mut self, - address: Address, - idx: U256, - listener: OneshotSender, - ) { + fn request_account_storage(&mut self, address: Address, idx: U256, listener: StorageSender) { match self.storage_requests.entry((address, idx)) { Entry::Occupied(mut entry) => { entry.get_mut().push(listener); @@ -162,8 +162,7 @@ where // serialize & deserialize back to U256 let idx_req = H256::from_uint(&idx); let storage = provider.get_storage_at(address, idx_req, block_id).await; - let storage = - storage.map(|storage| storage.into_uint()).map_err(|err| eyre::eyre!(err)); + let storage = storage.map(|storage| storage.into_uint()); (storage, address, idx) }); self.pending_requests.push(ProviderRequest::Storage(fut)); @@ -172,7 +171,7 @@ where } /// returns the future that fetches the account data - fn get_account_req(&self, address: Address) -> ProviderRequest { + fn get_account_req(&self, address: Address) -> ProviderRequest { trace!(target: "backendhandler", "preparing account request, address={:?}", address); let provider = self.provider.clone(); let block_id = self.block_id; @@ -180,14 +179,14 @@ where let balance = provider.get_balance(address, block_id); let nonce = provider.get_transaction_count(address, block_id); let code = provider.get_code(address, block_id); - let resp = tokio::try_join!(balance, nonce, code).map_err(|err| eyre::eyre!(err)); + let resp = tokio::try_join!(balance, nonce, code); (resp, address) }); ProviderRequest::Account(fut) } /// process a request for an account - fn request_account(&mut self, address: Address, listener: OneshotSender) { + fn request_account(&mut self, address: Address, listener: AccountInfoSender) { match self.account_requests.entry(address) { Entry::Occupied(mut entry) => { entry.get_mut().push(listener); @@ -200,7 +199,7 @@ where } /// process a request for a block hash - fn request_hash(&mut self, number: u64, listener: OneshotSender) { + fn request_hash(&mut self, number: u64, listener: BlockHashSender) { match self.block_requests.entry(number) { Entry::Occupied(mut entry) => { entry.get_mut().push(listener); @@ -222,9 +221,9 @@ where // we return empty hash Ok(KECCAK_EMPTY) } - Err(_) => { - error!(target: "backendhandler", ?number, "failed to get block"); - Err(eyre::eyre!("block {number} not found")) + Err(err) => { + error!(target: "backendhandler", ?err, ?number, "failed to get block"); + Err(err) } }; (block_hash, number) @@ -270,9 +269,21 @@ where ProviderRequest::Account(fut) => { if let Poll::Ready((resp, addr)) = fut.poll_unpin(cx) { // get the response - let (balance, nonce, code) = resp.unwrap_or_else(|report| { - panic!("Failed to get account for {}\n{}", addr, report); - }); + let (balance, nonce, code) = match resp { + Ok(res) => res, + Err(err) => { + let err = Arc::new(eyre::Error::new(err)); + if let Some(listeners) = pin.account_requests.remove(&addr) { + listeners.into_iter().for_each(|l| { + let _ = l.send(Err(DatabaseError::GetAccount( + addr, + Arc::clone(&err), + ))); + }) + } + continue + } + }; // convert it to revm-style types let (code, code_hash) = if !code.0.is_empty() { @@ -293,7 +304,7 @@ where // notify all listeners if let Some(listeners) = pin.account_requests.remove(&addr) { listeners.into_iter().for_each(|l| { - let _ = l.send(acc.clone()); + let _ = l.send(Ok(acc.clone())); }) } continue @@ -301,9 +312,25 @@ where } ProviderRequest::Storage(fut) => { if let Poll::Ready((resp, addr, idx)) = fut.poll_unpin(cx) { - let value = resp.unwrap_or_else(|report| { - panic!("Failed to get storage for {} at {}\n{}", addr, idx, report); - }); + let value = match resp { + Ok(value) => value, + Err(err) => { + // notify all listeners + let err = Arc::new(eyre::Error::new(err)); + if let Some(listeners) = + pin.storage_requests.remove(&(addr, idx)) + { + listeners.into_iter().for_each(|l| { + let _ = l.send(Err(DatabaseError::GetStorage( + addr, + idx, + Arc::clone(&err), + ))); + }) + } + continue + } + }; // update the cache pin.db.storage().write().entry(addr).or_default().insert(idx, value); @@ -311,7 +338,7 @@ where // notify all listeners if let Some(listeners) = pin.storage_requests.remove(&(addr, idx)) { listeners.into_iter().for_each(|l| { - let _ = l.send(value); + let _ = l.send(Ok(value)); }) } continue @@ -319,9 +346,22 @@ where } ProviderRequest::BlockHash(fut) => { if let Poll::Ready((block_hash, number)) = fut.poll_unpin(cx) { - let value = block_hash.unwrap_or_else(|report| { - panic!("Failed to get block hash for {}\n{}", number, report); - }); + let value = match block_hash { + Ok(value) => value, + Err(err) => { + let err = Arc::new(eyre::Error::new(err)); + // notify all listeners + if let Some(listeners) = pin.block_requests.remove(&number) { + listeners.into_iter().for_each(|l| { + let _ = l.send(Err(DatabaseError::GetBlockHash( + number, + Arc::clone(&err), + ))); + }) + } + continue + } + }; // update the cache pin.db.block_hashes().write().insert(number.into(), value); @@ -329,7 +369,7 @@ where // notify all listeners if let Some(listeners) = pin.block_requests.remove(&number) { listeners.into_iter().for_each(|l| { - let _ = l.send(value); + let _ = l.send(Ok(value)); }) } continue @@ -460,30 +500,30 @@ impl SharedBackend { }) } - fn do_get_basic(&self, address: Address) -> eyre::Result { + fn do_get_basic(&self, address: Address) -> DatabaseResult> { tokio::task::block_in_place(|| { let (sender, rx) = oneshot_channel(); let req = BackendRequest::Basic(address, sender); - self.backend.clone().try_send(req).map_err(|e| eyre::eyre!("{:?}", e))?; - Ok(rx.recv()?) + self.backend.clone().try_send(req)?; + rx.recv()?.map(Some) }) } - fn do_get_storage(&self, address: Address, index: U256) -> eyre::Result { + fn do_get_storage(&self, address: Address, index: U256) -> DatabaseResult { tokio::task::block_in_place(|| { let (sender, rx) = oneshot_channel(); let req = BackendRequest::Storage(address, index, sender); - self.backend.clone().try_send(req).map_err(|e| eyre::eyre!("{:?}", e))?; - Ok(rx.recv()?) + self.backend.clone().try_send(req)?; + rx.recv()? }) } - fn do_get_block_hash(&self, number: u64) -> eyre::Result { + fn do_get_block_hash(&self, number: u64) -> DatabaseResult { tokio::task::block_in_place(|| { let (sender, rx) = oneshot_channel(); let req = BackendRequest::BlockHash(number, sender); - self.backend.clone().try_send(req).map_err(|e| eyre::eyre!("{:?}", e))?; - Ok(rx.recv()?) + self.backend.clone().try_send(req)?; + rx.recv()? }) } @@ -498,21 +538,21 @@ impl DatabaseRef for SharedBackend { fn basic(&self, address: H160) -> Result, Self::Error> { trace!( target: "sharedbackend", "request basic {:?}", address); - self.do_get_basic(address).map(Some).map_err(|err| { + self.do_get_basic(address).map_err(|err| { error!(target: "sharedbackend", ?err, ?address, "Failed to send/recv `basic`"); - "Failed to retrieve basic account data from the fork" + err }) } - fn code_by_hash(&self, _address: H256) -> Result { - Err("Should not be called. Code is already loaded.") + fn code_by_hash(&self, hash: H256) -> Result { + Err(DatabaseError::MissingCode(hash)) } fn storage(&self, address: H160, index: U256) -> Result { trace!( target: "sharedbackend", "request storage {:?} at {:?}", address, index); self.do_get_storage(address, index).map_err(|err| { error!( target: "sharedbackend", ?err, ?address, ?index, "Failed to send/recv `storage`"); - "Failed to retrieve storage from the fork" + err }) } @@ -524,7 +564,7 @@ impl DatabaseRef for SharedBackend { trace!( target: "sharedbackend", "request block hash for number {:?}", number); self.do_get_block_hash(number).map_err(|err| { error!(target: "sharedbackend",?err, ?number, "Failed to send/recv `block_hash`"); - "Failed to retrieve `block_hash` from the fork" + err }) } } @@ -564,8 +604,8 @@ mod tests { let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); let idx = U256::from(0u64); - let value = backend.storage(address, idx); - let account = backend.basic(address); + let value = backend.storage(address, idx).unwrap(); + let account = backend.basic(address).unwrap().unwrap(); let mem_acc = db.accounts().read().get(&address).unwrap().clone(); assert_eq!(account.balance, mem_acc.balance); @@ -575,7 +615,7 @@ mod tests { assert_eq!(slots.get(&idx).copied().unwrap(), value); let num = U256::from(10u64); - let hash = backend.block_hash(num); + let hash = backend.block_hash(num).unwrap(); let mem_hash = *db.block_hashes().read().get(&num).unwrap(); assert_eq!(hash, mem_hash); diff --git a/evm/src/executor/fork/database.rs b/evm/src/executor/fork/database.rs index 087a1b0d449c..bf28f9a3ad91 100644 --- a/evm/src/executor/fork/database.rs +++ b/evm/src/executor/fork/database.rs @@ -18,8 +18,6 @@ use revm::{db::DatabaseRef, Account, AccountInfo, Bytecode, Database, DatabaseCo use std::sync::Arc; use tracing::{trace, warn}; -pub type DbResult = Result; - /// a [revm::Database] that's forked off another client /// /// The `backend` is used to retrieve (missing) data, which is then fetched from the remote diff --git a/evm/src/executor/inspector/cheatcodes/env.rs b/evm/src/executor/inspector/cheatcodes/env.rs index df90a87ccb7a..c14fa5125089 100644 --- a/evm/src/executor/inspector/cheatcodes/env.rs +++ b/evm/src/executor/inspector/cheatcodes/env.rs @@ -1,7 +1,10 @@ use std::collections::BTreeMap; use super::Cheatcodes; -use crate::{abi::HEVMCalls, executor::inspector::cheatcodes::util::with_journaled_account}; +use crate::{ + abi::HEVMCalls, + executor::{backend::DatabaseExt, inspector::cheatcodes::util::with_journaled_account}, +}; use bytes::Bytes; use ethers::{ abi::{self, AbiEncode, RawLog, Token, Tokenizable, Tokenize}, @@ -18,7 +21,7 @@ pub struct Broadcast { pub original_caller: Address, /// Depth of the broadcast pub depth: u64, - /// Whether or not the prank stops by itself after the next call + /// Whether the prank stops by itself after the next call pub single_call: bool, } @@ -34,7 +37,7 @@ pub struct Prank { pub new_origin: Option
, /// The depth at which the prank was called pub depth: u64, - /// Whether or not the prank stops by itself after the next call + /// Whether the prank stops by itself after the next call pub single_call: bool, } @@ -71,11 +74,11 @@ fn prank( let prank = Prank { prank_caller, prank_origin, new_caller, new_origin, depth, single_call }; if state.prank.is_some() { - return Err("You have an active prank already.".to_string().encode().into()) + return Err("You have an active prank already.".encode().into()) } if state.broadcast.is_some() { - return Err("You cannot `prank` for a broadcasted transaction. Pass the desired tx.origin into the broadcast cheatcode call".to_string().encode().into()); + return Err("You cannot `prank` for a broadcasted transaction. Pass the desired tx.origin into the broadcast cheatcode call".encode().into()); } state.prank = Some(prank); @@ -134,52 +137,63 @@ fn get_recorded_logs(state: &mut Cheatcodes) -> Bytes { } } -pub fn apply( +pub fn apply( state: &mut Cheatcodes, data: &mut EVMData<'_, DB>, caller: Address, call: &HEVMCalls, -) -> Option> { - Some(match call { +) -> Result, Bytes> { + let res = match call { HEVMCalls::Warp(inner) => { data.env.block.timestamp = inner.0; - Ok(Bytes::new()) + Bytes::new() } HEVMCalls::Difficulty(inner) => { data.env.block.difficulty = inner.0; - Ok(Bytes::new()) + Bytes::new() } HEVMCalls::Roll(inner) => { data.env.block.number = inner.0; - Ok(Bytes::new()) + Bytes::new() } HEVMCalls::Fee(inner) => { data.env.block.basefee = inner.0; - Ok(Bytes::new()) + Bytes::new() } HEVMCalls::Coinbase(inner) => { data.env.block.coinbase = inner.0; - Ok(Bytes::new()) + Bytes::new() } HEVMCalls::Store(inner) => { // TODO: Does this increase gas usage? - data.journaled_state.load_account(inner.0, data.db); - data.journaled_state.sstore(inner.0, inner.1.into(), inner.2.into(), data.db); - Ok(Bytes::new()) + data.journaled_state + .load_account(inner.0, data.db) + .map_err(|err| err.string_encoded())?; + data.journaled_state + .sstore(inner.0, inner.1.into(), inner.2.into(), data.db) + .map_err(|err| err.string_encoded())?; + Bytes::new() } HEVMCalls::Load(inner) => { // TODO: Does this increase gas usage? - data.journaled_state.load_account(inner.0, data.db); - let (val, _) = data.journaled_state.sload(inner.0, inner.1.into(), data.db); - Ok(val.encode().into()) + data.journaled_state + .load_account(inner.0, data.db) + .map_err(|err| err.string_encoded())?; + let (val, _) = data + .journaled_state + .sload(inner.0, inner.1.into(), data.db) + .map_err(|err| err.string_encoded())?; + val.encode().into() } HEVMCalls::Etch(inner) => { let code = inner.1.clone(); // TODO: Does this increase gas usage? - data.journaled_state.load_account(inner.0, data.db); + data.journaled_state + .load_account(inner.0, data.db) + .map_err(|err| err.string_encoded())?; data.journaled_state.set_code(inner.0, Bytecode::new_raw(code.0).to_checked()); - Ok(Bytes::new()) + Bytes::new() } HEVMCalls::Deal(inner) => { let who = inner.0; @@ -188,8 +202,9 @@ pub fn apply( with_journaled_account(&mut data.journaled_state, data.db, who, |account| { account.info.balance = value; - }); - Ok(Bytes::new()) + }) + .map_err(|err| err.string_encoded())?; + Bytes::new() } HEVMCalls::Prank0(inner) => prank( state, @@ -199,7 +214,7 @@ pub fn apply( None, data.journaled_state.depth(), true, - ), + )?, HEVMCalls::Prank1(inner) => prank( state, caller, @@ -208,7 +223,7 @@ pub fn apply( Some(inner.1), data.journaled_state.depth(), true, - ), + )?, HEVMCalls::StartPrank0(inner) => prank( state, caller, @@ -217,7 +232,7 @@ pub fn apply( None, data.journaled_state.depth(), false, - ), + )?, HEVMCalls::StartPrank1(inner) => prank( state, caller, @@ -226,23 +241,23 @@ pub fn apply( Some(inner.1), data.journaled_state.depth(), false, - ), + )?, HEVMCalls::StopPrank(_) => { state.prank = None; - Ok(Bytes::new()) + Bytes::new() } HEVMCalls::Record(_) => { start_record(state); - Ok(Bytes::new()) + Bytes::new() } - HEVMCalls::Accesses(inner) => Ok(accesses(state, inner.0)), + HEVMCalls::Accesses(inner) => accesses(state, inner.0), HEVMCalls::RecordLogs(_) => { start_record_logs(state); - Ok(Bytes::new()) + Bytes::new() } - HEVMCalls::GetRecordedLogs(_) => Ok(get_recorded_logs(state)), + HEVMCalls::GetRecordedLogs(_) => get_recorded_logs(state), HEVMCalls::SetNonce(inner) => { - with_journaled_account(&mut data.journaled_state, data.db, inner.0, |account| { + with_journaled_account(&mut data.journaled_state, data.db, inner.0, |account| -> Result{ // nonce must increment only if account.info.nonce < inner.1 { account.info.nonce = inner.1; @@ -250,7 +265,7 @@ pub fn apply( } else { Err(format!("Nonce lower than account's current nonce. Please provide a higher nonce than {}", account.info.nonce).encode().into()) } - }) + }).map_err(|err| err.string_encoded())?? } HEVMCalls::GetNonce(inner) => { correct_sender_nonce( @@ -258,19 +273,22 @@ pub fn apply( &mut data.journaled_state, &mut data.db, state, - ); + ) + .map_err(|err| err.string_encoded())?; // TODO: this is probably not a good long-term solution since it might mess up the gas // calculations - data.journaled_state.load_account(inner.0, data.db); + data.journaled_state + .load_account(inner.0, data.db) + .map_err(|err| err.string_encoded())?; // we can safely unwrap because `load_account` insert inner.0 to DB. let account = data.journaled_state.state().get(&inner.0).unwrap(); - Ok(abi::encode(&[Token::Uint(account.info.nonce.into())]).into()) + abi::encode(&[Token::Uint(account.info.nonce.into())]).into() } HEVMCalls::ChainId(inner) => { data.env.cfg.chain_id = inner.0; - Ok(Bytes::new()) + Bytes::new() } HEVMCalls::Broadcast0(_) => { correct_sender_nonce( @@ -278,8 +296,9 @@ pub fn apply( &mut data.journaled_state, &mut data.db, state, - ); - broadcast(state, data.env.tx.caller, caller, data.journaled_state.depth(), true) + ) + .map_err(|err| err.string_encoded())?; + broadcast(state, data.env.tx.caller, caller, data.journaled_state.depth(), true)? } HEVMCalls::Broadcast1(inner) => { correct_sender_nonce( @@ -287,8 +306,9 @@ pub fn apply( &mut data.journaled_state, &mut data.db, state, - ); - broadcast(state, inner.0, caller, data.journaled_state.depth(), true) + ) + .map_err(|err| err.string_encoded())?; + broadcast(state, inner.0, caller, data.journaled_state.depth(), true)? } HEVMCalls::StartBroadcast0(_) => { correct_sender_nonce( @@ -296,8 +316,9 @@ pub fn apply( &mut data.journaled_state, &mut data.db, state, - ); - broadcast(state, data.env.tx.caller, caller, data.journaled_state.depth(), false) + ) + .map_err(|err| err.string_encoded())?; + broadcast(state, data.env.tx.caller, caller, data.journaled_state.depth(), false)? } HEVMCalls::StartBroadcast1(inner) => { correct_sender_nonce( @@ -305,15 +326,18 @@ pub fn apply( &mut data.journaled_state, &mut data.db, state, - ); - broadcast(state, inner.0, caller, data.journaled_state.depth(), false) + ) + .map_err(|err| err.string_encoded())?; + broadcast(state, inner.0, caller, data.journaled_state.depth(), false)? } HEVMCalls::StopBroadcast(_) => { state.broadcast = None; - Ok(Bytes::new()) + Bytes::new() } - _ => return None, - }) + _ => return Ok(None), + }; + + Ok(Some(res)) } /// When using `forge script`, the script method is called using the address from `--sender`. @@ -324,11 +348,12 @@ fn correct_sender_nonce( journaled_state: &mut revm::JournaledState, db: &mut DB, state: &mut Cheatcodes, -) { +) -> Result<(), DB::Error> { if !state.corrected_nonce { with_journaled_account(journaled_state, db, sender, |account| { account.info.nonce = account.info.nonce.saturating_sub(1); state.corrected_nonce = true; - }); + })?; } + Ok(()) } diff --git a/evm/src/executor/inspector/cheatcodes/expect.rs b/evm/src/executor/inspector/cheatcodes/expect.rs index dc70a31049b3..6fca09bb1d5b 100644 --- a/evm/src/executor/inspector/cheatcodes/expect.rs +++ b/evm/src/executor/inspector/cheatcodes/expect.rs @@ -1,14 +1,17 @@ use super::Cheatcodes; use crate::{ abi::HEVMCalls, - executor::inspector::cheatcodes::util::{ERROR_PREFIX, REVERT_PREFIX}, + executor::{ + backend::DatabaseExt, + inspector::cheatcodes::util::{ERROR_PREFIX, REVERT_PREFIX}, + }, }; use bytes::Bytes; use ethers::{ abi::{AbiDecode, AbiEncode, RawLog}, types::{Address, H160, U256}, }; -use revm::{return_ok, Bytecode, Database, EVMData, Return}; +use revm::{return_ok, Bytecode, EVMData, Return}; use std::cmp::Ordering; /// For some cheatcodes we may internally change the status of the call, i.e. in `expectRevert`. @@ -212,7 +215,7 @@ impl PartialOrd for MockCallDataContext { } } -pub fn apply( +pub fn apply( state: &mut Cheatcodes, data: &mut EVMData<'_, DB>, call: &HEVMCalls, @@ -262,7 +265,9 @@ pub fn apply( } HEVMCalls::MockCall0(inner) => { // TODO: Does this increase gas usage? - data.journaled_state.load_account(inner.0, data.db); + if let Err(err) = data.journaled_state.load_account(inner.0, data.db) { + return Some(Err(err.string_encoded())) + } // Etches a single byte onto the account if it is empty to circumvent the `extcodesize` // check Solidity might perform. diff --git a/evm/src/executor/inspector/cheatcodes/mod.rs b/evm/src/executor/inspector/cheatcodes/mod.rs index b1395eab2476..d25a4249c6e7 100644 --- a/evm/src/executor/inspector/cheatcodes/mod.rs +++ b/evm/src/executor/inspector/cheatcodes/mod.rs @@ -150,6 +150,7 @@ impl Cheatcodes { // TODO: Log the opcode for the debugger env::apply(self, data, caller, &decoded) + .transpose() .or_else(|| util::apply(self, data, &decoded)) .or_else(|| expect::apply(self, data, &decoded)) .or_else(|| fuzz::apply(data, &decoded)) @@ -311,7 +312,12 @@ where // into 1559, in the cli package, relatively easily once we // know the target chain supports EIP-1559. if !is_static { - data.journaled_state.load_account(broadcast.origin, data.db); + if let Err(err) = + data.journaled_state.load_account(broadcast.origin, data.db) + { + return (Return::Revert, Gas::new(call.gas_limit), err.string_encoded()) + } + let account = data.journaled_state.state().get_mut(&broadcast.origin).unwrap(); @@ -490,10 +496,22 @@ where if data.journaled_state.depth() == broadcast.depth && call.caller == broadcast.original_caller { - data.journaled_state.load_account(broadcast.origin, data.db); + if let Err(err) = data.journaled_state.load_account(broadcast.origin, data.db) { + return (Return::Revert, None, Gas::new(call.gas_limit), err.string_encoded()) + } let (bytecode, to, nonce) = - process_create(broadcast.origin, call.init_code.clone(), data, call); + match process_create(broadcast.origin, call.init_code.clone(), data, call) { + Ok(val) => val, + Err(err) => { + return ( + Return::Revert, + None, + Gas::new(call.gas_limit), + err.string_encoded(), + ) + } + }; self.broadcastable_transactions.push_back(TypedTransaction::Legacy( TransactionRequest { diff --git a/evm/src/executor/inspector/cheatcodes/util.rs b/evm/src/executor/inspector/cheatcodes/util.rs index a3cb754b5b0c..552ccfcded8a 100644 --- a/evm/src/executor/inspector/cheatcodes/util.rs +++ b/evm/src/executor/inspector/cheatcodes/util.rs @@ -1,5 +1,8 @@ use super::Cheatcodes; -use crate::abi::HEVMCalls; +use crate::{ + abi::HEVMCalls, + executor::backend::error::{DatabaseError, DatabaseResult}, +}; use bytes::{BufMut, Bytes, BytesMut}; use ethers::{ abi::{AbiEncode, Address, ParamType, Token}, @@ -16,7 +19,7 @@ use ethers::{ use foundry_common::fmt::*; use hex::FromHex; use revm::{Account, CreateInputs, Database, EVMData, JournaledState}; -use std::str::FromStr; +use std::{fmt::Display, str::FromStr}; const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/"; @@ -38,14 +41,14 @@ pub fn with_journaled_account( db: &mut DB, addr: Address, mut f: F, -) -> R +) -> Result where F: FnMut(&mut Account) -> R, { - journaled_state.load_account(addr, db); + journaled_state.load_account(addr, db)?; journaled_state.touch(&addr); let account = journaled_state.state.get_mut(&addr).expect("account loaded;"); - f(account) + Ok(f(account)) } fn addr(private_key: U256) -> Result { @@ -155,33 +158,36 @@ pub fn apply( }) } -pub fn process_create( +pub fn process_create( broadcast_sender: Address, bytecode: Bytes, data: &mut EVMData<'_, DB>, call: &mut CreateInputs, -) -> (Bytes, Option, u64) { +) -> DatabaseResult<(Bytes, Option, u64)> +where + DB: Database, +{ match call.scheme { revm::CreateScheme::Create => { call.caller = broadcast_sender; - (bytecode, None, data.journaled_state.account(broadcast_sender).info.nonce) + Ok((bytecode, None, data.journaled_state.account(broadcast_sender).info.nonce)) } revm::CreateScheme::Create2 { salt } => { // Sanity checks for our CREATE2 deployer - data.journaled_state.load_account(DEFAULT_CREATE2_DEPLOYER, data.db); + data.journaled_state.load_account(DEFAULT_CREATE2_DEPLOYER, data.db)?; let info = &data.journaled_state.account(DEFAULT_CREATE2_DEPLOYER).info; match &info.code { Some(code) => { if code.is_empty() { - panic!("{MISSING_CREATE2_DEPLOYER}") + return Err(DatabaseError::MissingCreate2Deployer) } } None => { - // SharedBacked - if data.db.code_by_hash(info.code_hash).is_empty() { - panic!("{MISSING_CREATE2_DEPLOYER}") + // forked db + if data.db.code_by_hash(info.code_hash)?.is_empty() { + return Err(DatabaseError::MissingCreate2Deployer) } } } @@ -201,12 +207,12 @@ pub fn process_create( calldata.put_slice(&salt_bytes); calldata.put(bytecode); - (calldata.freeze(), Some(NameOrAddress::Address(DEFAULT_CREATE2_DEPLOYER)), nonce) + Ok((calldata.freeze(), Some(NameOrAddress::Address(DEFAULT_CREATE2_DEPLOYER)), nonce)) } } } -pub fn encode_error(reason: impl ToString) -> Bytes { +pub fn encode_error(reason: impl Display) -> Bytes { [ERROR_PREFIX.as_slice(), reason.to_string().encode().as_slice()].concat().into() } diff --git a/evm/src/executor/inspector/debugger.rs b/evm/src/executor/inspector/debugger.rs index f79ae3a4bde9..7aadce5c2eeb 100644 --- a/evm/src/executor/inspector/debugger.rs +++ b/evm/src/executor/inspector/debugger.rs @@ -1,6 +1,7 @@ use crate::{ debug::{DebugArena, DebugNode, DebugStep, Instruction}, executor::{ + backend::DatabaseExt, inspector::utils::{gas_used, get_create_address}, CHEATCODE_ADDRESS, }, @@ -9,8 +10,8 @@ use crate::{ use bytes::Bytes; use ethers::types::Address; use revm::{ - opcode, spec_opcode_gas, CallInputs, CreateInputs, Database, EVMData, Gas, Inspector, - Interpreter, Memory, Return, + opcode, spec_opcode_gas, CallInputs, CreateInputs, EVMData, Gas, Inspector, Interpreter, + Memory, Return, }; /// An inspector that collects debug nodes on every step of the interpreter. @@ -59,32 +60,8 @@ impl Debugger { impl Inspector for Debugger where - DB: Database, + DB: DatabaseExt, { - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CallInputs, - _: bool, - ) -> (Return, Gas, Bytes) { - self.enter( - data.journaled_state.depth() as usize, - call.context.code_address, - call.context.scheme.into(), - ); - if call.contract == CHEATCODE_ADDRESS { - self.arena.arena[self.head].steps.push(DebugStep { - memory: Memory::new(), - instruction: Instruction::Cheatcode( - call.input[0..4].try_into().expect("malformed cheatcode call"), - ), - ..Default::default() - }); - } - - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) - } - fn initialize_interp( &mut self, interp: &mut Interpreter, @@ -144,6 +121,30 @@ where Return::Continue } + fn call( + &mut self, + data: &mut EVMData<'_, DB>, + call: &mut CallInputs, + _: bool, + ) -> (Return, Gas, Bytes) { + self.enter( + data.journaled_state.depth() as usize, + call.context.code_address, + call.context.scheme.into(), + ); + if call.contract == CHEATCODE_ADDRESS { + self.arena.arena[self.head].steps.push(DebugStep { + memory: Memory::new(), + instruction: Instruction::Cheatcode( + call.input[0..4].try_into().expect("malformed cheatcode call"), + ), + ..Default::default() + }); + } + + (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) + } + fn call_end( &mut self, _: &mut EVMData<'_, DB>, @@ -164,7 +165,10 @@ where call: &mut CreateInputs, ) -> (Return, Option
, Gas, Bytes) { // TODO: Does this increase gas cost? - data.journaled_state.load_account(call.caller, data.db); + if let Err(err) = data.journaled_state.load_account(call.caller, data.db) { + return (Return::Revert, None, Gas::new(call.gas_limit), err.string_encoded()) + } + let nonce = data.journaled_state.account(call.caller).info.nonce; self.enter( data.journaled_state.depth() as usize, diff --git a/evm/src/executor/inspector/tracer.rs b/evm/src/executor/inspector/tracer.rs index 8836eeafd6fe..a77f23b42abd 100644 --- a/evm/src/executor/inspector/tracer.rs +++ b/evm/src/executor/inspector/tracer.rs @@ -130,7 +130,7 @@ where call: &mut CreateInputs, ) -> (Return, Option
, Gas, Bytes) { // TODO: Does this increase gas cost? - data.journaled_state.load_account(call.caller, data.db); + let _ = data.journaled_state.load_account(call.caller, data.db); let nonce = data.journaled_state.account(call.caller).info.nonce; self.start_trace( data.journaled_state.depth() as usize, diff --git a/evm/src/executor/mod.rs b/evm/src/executor/mod.rs index 83e2a16a1a4b..ec033a4c0597 100644 --- a/evm/src/executor/mod.rs +++ b/evm/src/executor/mod.rs @@ -40,7 +40,10 @@ pub mod snapshot; use crate::{ coverage::HitMaps, executor::{ - backend::DatabaseExt, + backend::{ + error::{DatabaseError, DatabaseResult}, + DatabaseExt, + }, inspector::{InspectorStack, DEFAULT_CREATE2_DEPLOYER}, }, }; @@ -126,7 +129,10 @@ impl Executor { /// Creates the default CREATE2 Contract Deployer for local tests and scripts. pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> { - let create2_deployer_account = self.backend_mut().basic(DEFAULT_CREATE2_DEPLOYER); + let create2_deployer_account = self + .backend_mut() + .basic(DEFAULT_CREATE2_DEPLOYER)? + .ok_or(DatabaseError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?; if create2_deployer_account.code.is_none() || create2_deployer_account.code.as_ref().unwrap().is_empty() @@ -134,42 +140,44 @@ impl Executor { let creator = "0x3fAB184622Dc19b6109349B94811493BF2a45362".parse().unwrap(); // Probably 0, but just in case. - let initial_balance = self.get_balance(creator); + let initial_balance = self.get_balance(creator)?; - self.set_balance(creator, U256::MAX); + self.set_balance(creator, U256::MAX)?; self.deploy( creator, hex::decode("604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3").expect("Could not decode create2 deployer init_code").into(), U256::zero(), None )?; - self.set_balance(creator, initial_balance); + self.set_balance(creator, initial_balance)?; } Ok(()) } /// Set the balance of an account. - pub fn set_balance(&mut self, address: Address, amount: U256) -> &mut Self { + pub fn set_balance(&mut self, address: Address, amount: U256) -> DatabaseResult<&mut Self> { trace!(?address, ?amount, "setting account balance"); - let mut account = self.backend_mut().basic(address); + let mut account = + self.backend_mut().basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; account.balance = amount; self.backend_mut().insert_account_info(address, account); - self + Ok(self) } /// Gets the balance of an account - pub fn get_balance(&self, address: Address) -> U256 { - self.backend().basic(address).balance + pub fn get_balance(&self, address: Address) -> DatabaseResult { + Ok(self.backend().basic(address)?.ok_or(DatabaseError::MissingAccount(address))?.balance) } /// Set the nonce of an account. - pub fn set_nonce(&mut self, address: Address, nonce: u64) -> &mut Self { - let mut account = self.backend_mut().basic(address); + pub fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<&mut Self> { + let mut account = + self.backend_mut().basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; account.nonce = nonce; self.backend_mut().insert_account_info(address, account); - self + Ok(self) } pub fn set_tracing(&mut self, tracing: bool) -> &mut Self { @@ -541,9 +549,22 @@ impl Executor { state_changeset: StateChangeset, should_fail: bool, ) -> bool { + self.ensure_success(address, reverted, state_changeset, should_fail).unwrap_or_default() + } + + fn ensure_success( + &self, + address: Address, + reverted: bool, + state_changeset: StateChangeset, + should_fail: bool, + ) -> Result { // Construct a new VM with the state changeset let mut backend = self.backend().clone_empty(); - backend.insert_account_info(address, self.backend().basic(address)); + backend.insert_account_info( + address, + self.backend().basic(address)?.ok_or(DatabaseError::MissingAccount(address))?, + ); backend.commit(state_changeset); let executor = Executor::new(backend, self.env.clone(), self.inspector_config.clone(), self.gas_limit); @@ -559,7 +580,7 @@ impl Executor { } } - should_fail ^ success + Ok(should_fail ^ success) } /// Creates the environment to use when executing a transaction in a test context From 9df448cd285a50fb320a3906eacfb18c985ed8a8 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 1 Sep 2022 19:59:04 +0200 Subject: [PATCH 04/13] update map types --- evm/src/executor/backend/mod.rs | 6 +++--- evm/src/executor/backend/snapshot.rs | 5 ++--- evm/src/executor/fork/cache.rs | 22 ++++++++-------------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/evm/src/executor/backend/mod.rs b/evm/src/executor/backend/mod.rs index b0dd445248e3..85d16e3cc61b 100644 --- a/evm/src/executor/backend/mod.rs +++ b/evm/src/executor/backend/mod.rs @@ -26,12 +26,12 @@ mod fuzz; pub mod snapshot; pub use fuzz::FuzzBackendWrapper; mod diagnostic; -use crate::executor::{ - backend::error::DatabaseError, inspector::cheatcodes::util::with_journaled_account, -}; +use crate::executor::inspector::cheatcodes::util::with_journaled_account; pub use diagnostic::RevertDiagnostic; pub mod error; +pub use error::{DatabaseError, DatabaseResult}; + mod in_memory_db; // A `revm::Database` that is used in forking mode diff --git a/evm/src/executor/backend/snapshot.rs b/evm/src/executor/backend/snapshot.rs index eb610d87d8cd..f7e1d3ad6bba 100644 --- a/evm/src/executor/backend/snapshot.rs +++ b/evm/src/executor/backend/snapshot.rs @@ -2,13 +2,12 @@ use ethers::types::{Address, H256, U256}; use hashbrown::HashMap as Map; use revm::{AccountInfo, Env, JournaledState}; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; /// A minimal abstraction of a state at a certain point in time #[derive(Default, Debug, Serialize, Deserialize)] pub struct StateSnapshot { - pub accounts: BTreeMap, - pub storage: BTreeMap>, + pub accounts: Map, + pub storage: Map>, pub block_hashes: Map, } diff --git a/evm/src/executor/fork/cache.rs b/evm/src/executor/fork/cache.rs index c1a3f0634e18..9f0d831f42da 100644 --- a/evm/src/executor/fork/cache.rs +++ b/evm/src/executor/fork/cache.rs @@ -3,20 +3,14 @@ use ethers::types::{Address, H256, U256}; use parking_lot::RwLock; use revm::{Account, AccountInfo, DatabaseCommit, KECCAK_EMPTY}; use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; -use std::{ - collections::{BTreeMap, BTreeSet}, - fs, - io::BufWriter, - path::PathBuf, - sync::Arc, -}; +use std::{collections::BTreeSet, fs, io::BufWriter, path::PathBuf, sync::Arc}; use tracing::{trace, trace_span, warn}; use tracing_error::InstrumentResult; use url::Url; use crate::HashMap as Map; -pub type StorageInfo = BTreeMap; +pub type StorageInfo = Map; /// A shareable Block database #[derive(Clone, Debug)] @@ -62,12 +56,12 @@ impl BlockchainDb { } /// Returns the map that holds the account related info - pub fn accounts(&self) -> &RwLock> { + pub fn accounts(&self) -> &RwLock> { &self.db.accounts } /// Returns the map that holds the storage related info - pub fn storage(&self) -> &RwLock> { + pub fn storage(&self) -> &RwLock> { &self.db.storage } @@ -164,9 +158,9 @@ impl<'de> Deserialize<'de> for BlockchainDbMeta { #[derive(Debug, Default)] pub struct MemDb { /// Account related data - pub accounts: RwLock>, + pub accounts: RwLock>, /// Storage related data - pub storage: RwLock>, + pub storage: RwLock>, /// All retrieved block hashes pub block_hashes: RwLock>, } @@ -351,8 +345,8 @@ impl<'de> Deserialize<'de> for JsonBlockCacheData { #[derive(Deserialize)] struct Data { meta: BlockchainDbMeta, - accounts: BTreeMap, - storage: BTreeMap, + accounts: Map, + storage: Map, block_hashes: Map, } From 670d1b3757969cacfbcab245caa0287d571b6f53 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 1 Sep 2022 21:02:30 +0200 Subject: [PATCH 05/13] refactor: make anvil compile again --- anvil/src/eth/api.rs | 15 ++-- anvil/src/eth/backend/db.rs | 68 +++++++++++------- anvil/src/eth/backend/executor.rs | 21 +++++- anvil/src/eth/backend/mem/fork_db.rs | 18 +++-- anvil/src/eth/backend/mem/in_memory_db.rs | 71 +++++++++--------- anvil/src/eth/backend/mem/mod.rs | 88 ++++++++++++++--------- anvil/src/eth/backend/mem/state.rs | 33 +++++---- anvil/src/eth/backend/mem/storage.rs | 2 +- anvil/src/eth/backend/validate.rs | 4 +- anvil/src/eth/error.rs | 7 +- anvil/src/eth/util.rs | 25 +------ anvil/src/hardfork.rs | 2 +- 12 files changed, 204 insertions(+), 150 deletions(-) diff --git a/anvil/src/eth/api.rs b/anvil/src/eth/api.rs index dd8f22558bf3..d62d7eb5d94a 100644 --- a/anvil/src/eth/api.rs +++ b/anvil/src/eth/api.rs @@ -53,7 +53,10 @@ use ethers::{ }; use forge::{executor::DatabaseRef, revm::BlockEnv}; use foundry_common::ProviderBuilder; -use foundry_evm::revm::{return_ok, return_revert, Return}; +use foundry_evm::{ + executor::backend::DatabaseError, + revm::{return_ok, return_revert, Return}, +}; use futures::channel::mpsc::Receiver; use parking_lot::RwLock; use std::{sync::Arc, time::Duration}; @@ -749,7 +752,7 @@ impl EthApi { // pre-validate self.backend.validate_pool_transaction(&pending_transaction).await?; - let on_chain_nonce = self.backend.current_nonce(*pending_transaction.sender()).await; + let on_chain_nonce = self.backend.current_nonce(*pending_transaction.sender()).await?; let from = *pending_transaction.sender(); let nonce = *pending_transaction.transaction.nonce(); let requires = required_marker(nonce, on_chain_nonce, from); @@ -784,7 +787,7 @@ impl EthApi { if fork.predates_fork(number.as_u64()) { if overrides.is_some() { return Err(BlockchainError::StateOverrideError( - "not available on past forked blocks".into(), + "not available on past forked blocks".to_string(), )) } return Ok(fork.call(&request, Some(number.into())).await?) @@ -1235,7 +1238,7 @@ impl EthApi { /// Handler for ETH RPC call: `anvil_impersonateAccount` pub async fn anvil_impersonate_account(&self, address: Address) -> Result<()> { node_info!("anvil_impersonateAccount"); - self.backend.impersonate(address).await; + self.backend.impersonate(address).await?; Ok(()) } @@ -1244,7 +1247,7 @@ impl EthApi { /// Handler for ETH RPC call: `anvil_stopImpersonatingAccount` pub async fn anvil_stop_impersonating_account(&self, address: Address) -> Result<()> { node_info!("anvil_stopImpersonatingAccount"); - self.backend.stop_impersonating(address).await; + self.backend.stop_impersonating(address).await?; Ok(()) } @@ -1720,7 +1723,7 @@ impl EthApi { block_env: BlockEnv, ) -> Result where - D: DatabaseRef, + D: DatabaseRef, { // call takes at least this amount const MIN_GAS: U256 = U256([21_000, 0, 0, 0]); diff --git a/anvil/src/eth/backend/db.rs b/anvil/src/eth/backend/db.rs index aef9649991c8..ff12641acb09 100644 --- a/anvil/src/eth/backend/db.rs +++ b/anvil/src/eth/backend/db.rs @@ -10,7 +10,7 @@ use ethers::{ use forge::revm::KECCAK_EMPTY; use foundry_evm::{ executor::{ - backend::{snapshot::StateSnapshot, MemDb}, + backend::{snapshot::StateSnapshot, DatabaseError, DatabaseResult, MemDb}, DatabaseRef, }, revm::{ @@ -21,14 +21,14 @@ use foundry_evm::{ }; use hash_db::HashDB; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; + /// Type alias for the `HashDB` representation of the Database pub type AsHashDB = Box>>; /// Helper trait get access to the data in `HashDb` form #[auto_impl::auto_impl(Box)] -pub trait MaybeHashDatabase: DatabaseRef { +pub trait MaybeHashDatabase: DatabaseRef { /// Return the DB as read-only hashdb and the root key fn maybe_as_hash_db(&self) -> Option<(AsHashDB, H256)> { None @@ -47,7 +47,7 @@ pub trait MaybeHashDatabase: DatabaseRef { impl<'a, T: 'a + MaybeHashDatabase + ?Sized> MaybeHashDatabase for &'a T where - &'a T: DatabaseRef, + &'a T: DatabaseRef, { fn maybe_as_hash_db(&self) -> Option<(AsHashDB, H256)> { T::maybe_as_hash_db(self) @@ -64,27 +64,39 @@ where } /// This bundles all required revm traits -pub trait Db: DatabaseRef + Database + DatabaseCommit + MaybeHashDatabase + Send + Sync { +pub trait Db: + DatabaseRef + + Database + + DatabaseCommit + + MaybeHashDatabase + + Send + + Sync +{ /// Inserts an account fn insert_account(&mut self, address: Address, account: AccountInfo); /// Sets the nonce of the given address - fn set_nonce(&mut self, address: Address, nonce: u64) { - let mut info = self.basic(address); + fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<()> { + let mut info = + self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; info.nonce = nonce; self.insert_account(address, info); + Ok(()) } /// Sets the balance of the given address - fn set_balance(&mut self, address: Address, balance: U256) { - let mut info = self.basic(address); + fn set_balance(&mut self, address: Address, balance: U256) -> DatabaseResult<()> { + let mut info = + self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; info.balance = balance; self.insert_account(address, info); + Ok(()) } /// Sets the balance of the given address - fn set_code(&mut self, address: Address, code: Bytes) { - let mut info = self.basic(address); + fn set_code(&mut self, address: Address, code: Bytes) -> DatabaseResult<()> { + let mut info = + self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; let code_hash = if code.as_ref().is_empty() { KECCAK_EMPTY } else { @@ -93,19 +105,20 @@ pub trait Db: DatabaseRef + Database + DatabaseCommit + MaybeHashDatabase + Send info.code_hash = code_hash; info.code = Some(Bytecode::new_raw(code.0).to_checked()); self.insert_account(address, info); + Ok(()) } /// Sets the balance of the given address - fn set_storage_at(&mut self, address: Address, slot: U256, val: U256); + fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()>; /// inserts a blockhash for the given number fn insert_block_hash(&mut self, number: U256, hash: H256); /// Write all chain data to serialized bytes buffer - fn dump_state(&self) -> Option; + fn dump_state(&self) -> DatabaseResult>; /// Deserialize and add all chain data to the backend storage - fn load_state(&mut self, buf: SerializableState) -> bool; + fn load_state(&mut self, buf: SerializableState) -> DatabaseResult; /// Creates a new snapshot fn snapshot(&mut self) -> U256; @@ -128,12 +141,12 @@ pub trait Db: DatabaseRef + Database + DatabaseCommit + MaybeHashDatabase + Send /// This is useful to create blocks without actually writing to the `Db`, but rather in the cache of /// the `CacheDB` see also /// [Backend::pending_block()](crate::eth::backend::mem::Backend::pending_block()) -impl Db for CacheDB { +impl + Send + Sync + Clone> Db for CacheDB { fn insert_account(&mut self, address: Address, account: AccountInfo) { self.insert_account_info(address, account) } - fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) { + fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { self.insert_account_storage(address, slot, val) } @@ -141,12 +154,12 @@ impl Db for CacheDB { self.block_hashes.insert(number, hash); } - fn dump_state(&self) -> Option { - None + fn dump_state(&self) -> DatabaseResult> { + Ok(None) } - fn load_state(&mut self, _buf: SerializableState) -> bool { - false + fn load_state(&mut self, _buf: SerializableState) -> DatabaseResult { + Ok(false) } fn snapshot(&mut self) -> U256 { @@ -162,14 +175,14 @@ impl Db for CacheDB { } } -impl MaybeHashDatabase for CacheDB { +impl> MaybeHashDatabase for CacheDB { fn maybe_as_hash_db(&self) -> Option<(AsHashDB, H256)> { Some(trie_hash_db(&self.accounts)) } fn clear_into_snapshot(&mut self) -> StateSnapshot { let db_accounts = std::mem::take(&mut self.accounts); - let mut accounts = BTreeMap::new(); - let mut account_storage = BTreeMap::new(); + let mut accounts = HashMap::new(); + let mut account_storage = HashMap::new(); for (addr, mut acc) in db_accounts { account_storage.insert(addr, std::mem::take(&mut acc.storage)); @@ -213,19 +226,20 @@ impl StateDb { } impl DatabaseRef for StateDb { - fn basic(&self, address: H160) -> AccountInfo { + type Error = DatabaseError; + fn basic(&self, address: H160) -> DatabaseResult> { self.0.basic(address) } - fn code_by_hash(&self, code_hash: H256) -> Bytecode { + fn code_by_hash(&self, code_hash: H256) -> DatabaseResult { self.0.code_by_hash(code_hash) } - fn storage(&self, address: H160, index: U256) -> U256 { + fn storage(&self, address: H160, index: U256) -> DatabaseResult { self.0.storage(address, index) } - fn block_hash(&self, number: U256) -> H256 { + fn block_hash(&self, number: U256) -> DatabaseResult { self.0.block_hash(number) } } diff --git a/anvil/src/eth/backend/executor.rs b/anvil/src/eth/backend/executor.rs index 4be940117f55..78c50491d27a 100644 --- a/anvil/src/eth/backend/executor.rs +++ b/anvil/src/eth/backend/executor.rs @@ -19,6 +19,7 @@ use ethers::{ }; use forge::revm::ExecutionResult; use foundry_evm::{ + executor::backend::DatabaseError, revm, revm::{BlockEnv, CfgEnv, Env, Return, SpecId, TransactOut}, trace::node::CallTraceNode, @@ -132,6 +133,12 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' invalid.push(tx); continue } + TransactionExecutionOutcome::DatabaseError(_, err) => { + // Note: this is only possible in forking mode, if for example a rpc request + // failed + trace!(target: "backend", ?err, "Failed to execute transaction due to database error"); + continue + } }; let receipt = tx.create_receipt(); cumulative_gas_used = cumulative_gas_used.saturating_add(receipt.gas_used()); @@ -206,6 +213,8 @@ pub enum TransactionExecutionOutcome { Invalid(Arc, InvalidTransactionError), /// Execution skipped because could exceed gas limit Exhausted(Arc), + /// When an error occurred during execution + DatabaseError(Arc, DatabaseError), } impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator @@ -215,7 +224,17 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator fn next(&mut self) -> Option { let transaction = self.pending.next()?; - let account = self.db.basic(*transaction.pending_transaction.sender()); + let sender = *transaction.pending_transaction.sender(); + let account = match self.db.basic(sender) { + Ok(Some(account)) => account, + Ok(None) => { + return Some(TransactionExecutionOutcome::DatabaseError( + transaction, + DatabaseError::MissingAccount(sender), + )) + } + Err(err) => return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)), + }; let env = self.env_for(&transaction.pending_transaction); // check that we comply with the block's gas limit let max_gas = self.gas_used.saturating_add(U256::from(env.tx.gas_limit)); diff --git a/anvil/src/eth/backend/mem/fork_db.rs b/anvil/src/eth/backend/mem/fork_db.rs index 3ef81fd7ed37..88e745536801 100644 --- a/anvil/src/eth/backend/mem/fork_db.rs +++ b/anvil/src/eth/backend/mem/fork_db.rs @@ -6,7 +6,10 @@ use crate::{ use ethers::prelude::H256; use forge::revm::Database; pub use foundry_evm::executor::fork::database::ForkedDatabase; -use foundry_evm::executor::{backend::snapshot::StateSnapshot, fork::database::ForkDbSnapshot}; +use foundry_evm::executor::{ + backend::{snapshot::StateSnapshot, DatabaseError, DatabaseResult}, + fork::database::ForkDbSnapshot, +}; /// Implement the helper for the fork database impl Db for ForkedDatabase { @@ -14,9 +17,10 @@ impl Db for ForkedDatabase { self.database_mut().insert_account(address, account) } - fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) { + fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { // this ensures the account is loaded first - let _ = Database::basic(self, address); + let _ = Database::basic(self, address)? + .ok_or(DatabaseError::MissingAccount(address))?; self.database_mut().set_storage_at(address, slot, val) } @@ -24,12 +28,12 @@ impl Db for ForkedDatabase { self.inner().block_hashes().write().insert(number, hash); } - fn dump_state(&self) -> Option { - None + fn dump_state(&self) -> DatabaseResult> { + Ok(None) } - fn load_state(&mut self, _buf: SerializableState) -> bool { - false + fn load_state(&mut self, _buf: SerializableState) -> DatabaseResult { + Ok(false) } fn snapshot(&mut self) -> U256 { diff --git a/anvil/src/eth/backend/mem/in_memory_db.rs b/anvil/src/eth/backend/mem/in_memory_db.rs index 2e0b4afde78e..a39f24bd7583 100644 --- a/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/anvil/src/eth/backend/mem/in_memory_db.rs @@ -14,7 +14,7 @@ use tracing::{trace, warn}; // reexport for convenience use crate::mem::state::storage_trie_db; -use foundry_evm::executor::backend::snapshot::StateSnapshot; +use foundry_evm::executor::backend::{snapshot::StateSnapshot, DatabaseResult}; pub use foundry_evm::executor::{backend::MemDb, DatabaseRef}; impl Db for MemDb { @@ -22,27 +22,28 @@ impl Db for MemDb { self.inner.insert_account_info(address, account) } - fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) { - self.inner.insert_account_storage(address, slot, val) + fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { + Ok(self.inner.insert_account_storage(address, slot, val)?) } fn insert_block_hash(&mut self, number: U256, hash: H256) { self.inner.block_hashes.insert(number, hash); } - fn dump_state(&self) -> Option { + fn dump_state(&self) -> DatabaseResult> { let accounts = self .inner .accounts .clone() .into_iter() - .map(|(k, v)| { - let code = v - .info - .code - .unwrap_or_else(|| self.inner.code_by_hash(v.info.code_hash)) - .to_checked(); - ( + .map(|(k, v)| -> DatabaseResult<_> { + let code = if let Some(code) = v.info.code { + code + } else { + self.inner.code_by_hash(v.info.code_hash)? + } + .to_checked(); + Ok(( k, SerializableAccountRecord { nonce: v.info.nonce, @@ -50,14 +51,14 @@ impl Db for MemDb { code: code.bytes()[..code.len()].to_vec().into(), storage: v.storage.into_iter().collect(), }, - ) + )) }) - .collect(); + .collect::>()?; - Some(SerializableState { accounts }) + Ok(Some(SerializableState { accounts })) } - fn load_state(&mut self, state: SerializableState) -> bool { + fn load_state(&mut self, state: SerializableState) -> DatabaseResult { for (addr, account) in state.accounts.into_iter() { let old_account = self.inner.accounts.get(&addr); @@ -81,11 +82,11 @@ impl Db for MemDb { ); for (k, v) in account.storage.into_iter() { - self.set_storage_at(addr, k, v); + self.set_storage_at(addr, k, v)?; } } - true + Ok(true) } /// Creates a new snapshot @@ -129,11 +130,13 @@ impl MaybeHashDatabase for MemDb { } fn clear_into_snapshot(&mut self) -> StateSnapshot { - self.inner.clear_into_snapshot() + // self.inner.clear_into_snapshot() + todo!() } - fn init_from_snapshot(&mut self, snapshot: StateSnapshot) { - self.inner.init_from_snapshot(snapshot) + fn init_from_snapshot(&mut self, _snapshot: StateSnapshot) { + // self.inner.init_from_snapshot(snapshot) + todo!() } } @@ -174,20 +177,20 @@ mod tests { }, ); - dump_db.set_storage_at(test_addr, "0x1234567".into(), "0x1".into()); + dump_db.set_storage_at(test_addr, "0x1234567".into(), "0x1".into()).unwrap(); - let state = dump_db.dump_state().unwrap(); + let state = dump_db.dump_state().unwrap().unwrap(); let mut load_db = MemDb::default(); - load_db.load_state(state); + load_db.load_state(state).unwrap(); - let loaded_account = load_db.basic(test_addr); + let loaded_account = load_db.basic(test_addr).unwrap().unwrap(); assert_eq!(loaded_account.balance, 123456.into()); - assert_eq!(load_db.code_by_hash(loaded_account.code_hash), contract_code); + assert_eq!(load_db.code_by_hash(loaded_account.code_hash).unwrap(), contract_code); assert_eq!(loaded_account.nonce, 1234); - assert_eq!(load_db.storage(test_addr, "0x1234567".into()), "0x1".into()); + assert_eq!(load_db.storage(test_addr, "0x1234567".into()).unwrap(), "0x1".into()); } // verifies that multiple accounts can be loaded at a time, and storage is merged within those @@ -214,8 +217,8 @@ mod tests { }, ); - db.set_storage_at(test_addr, "0x1234567".into(), "0x1".into()); - db.set_storage_at(test_addr, "0x1234568".into(), "0x2".into()); + db.set_storage_at(test_addr, "0x1234567".into(), "0x1".into()).unwrap(); + db.set_storage_at(test_addr, "0x1234568".into(), "0x2".into()).unwrap(); let mut new_state = SerializableState::default(); @@ -242,17 +245,17 @@ mod tests { }, ); - db.load_state(new_state); + db.load_state(new_state).unwrap(); - let loaded_account = db.basic(test_addr); - let loaded_account2 = db.basic(test_addr2); + let loaded_account = db.basic(test_addr).unwrap().unwrap(); + let loaded_account2 = db.basic(test_addr2).unwrap().unwrap(); assert_eq!(loaded_account2.nonce, 1); assert_eq!(loaded_account.balance, 100100.into()); - assert_eq!(db.code_by_hash(loaded_account.code_hash), contract_code); + assert_eq!(db.code_by_hash(loaded_account.code_hash).unwrap(), contract_code); assert_eq!(loaded_account.nonce, 1234); - assert_eq!(db.storage(test_addr, "0x1234567".into()), "0x1".into()); - assert_eq!(db.storage(test_addr, "0x1234568".into()), "0x5".into()); + assert_eq!(db.storage(test_addr, "0x1234567".into()).unwrap(), "0x1".into()); + assert_eq!(db.storage(test_addr, "0x1234568".into()).unwrap(), "0x5".into()); } } diff --git a/anvil/src/eth/backend/mem/mod.rs b/anvil/src/eth/backend/mem/mod.rs index 67021826ee2d..c776dc099b0f 100644 --- a/anvil/src/eth/backend/mem/mod.rs +++ b/anvil/src/eth/backend/mem/mod.rs @@ -54,6 +54,7 @@ use forge::{ }; use foundry_evm::{ decode::decode_revert, + executor::backend::{DatabaseError, DatabaseResult}, revm, revm::{ db::CacheDB, Account, CreateScheme, Env, SpecId, TransactOut, TransactTo, TxEnv, @@ -183,7 +184,7 @@ impl Backend { /// Applies the configured genesis settings /// /// This will fund, create the genesis accounts - async fn apply_genesis(&self) { + async fn apply_genesis(&self) -> DatabaseResult<()> { trace!(target: "backend", "setting genesis balances"); let mut db = self.db.write().await; @@ -195,7 +196,8 @@ impl Backend { fork_genesis_infos.clear(); for address in self.genesis.accounts.iter().copied() { - let mut info = db.basic(address); + let mut info = + db.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; info.balance = self.genesis.balance; db.insert_account(address, info.clone()); @@ -210,19 +212,20 @@ impl Backend { // apply the genesis.json alloc self.genesis.apply_genesis_json_alloc(db); + Ok(()) } /// Sets the account to impersonate /// /// Returns `true` if the account is already impersonated - pub async fn impersonate(&self, addr: Address) -> bool { + pub async fn impersonate(&self, addr: Address) -> DatabaseResult { if self.cheats.is_impersonated(addr) { - return true + return Ok(true) } // need to bypass EIP-3607: Reject transactions from senders with deployed code by setting // the code hash to `KECCAK_EMPTY` temporarily and also remove the code itself and add back // when we stop impersonating - let mut account = self.db.read().await.basic(addr); + let mut account = self.get_account(addr).await?; let mut code_hash = None; let mut code = None; if account.code_hash != KECCAK_EMPTY { @@ -230,20 +233,21 @@ impl Backend { code = account.code.take(); self.db.write().await.insert_account(addr, account); } - self.cheats.impersonate(addr, code_hash, code) + Ok(self.cheats.impersonate(addr, code_hash, code)) } /// Removes the account that from the impersonated set /// /// If the impersonated `addr` is a contract then we also reset the code here - pub async fn stop_impersonating(&self, addr: Address) { + pub async fn stop_impersonating(&self, addr: Address) -> DatabaseResult<()> { if let Some((Some(code_hash), code)) = self.cheats.stop_impersonating(&addr) { let mut db = self.db.write().await; - let mut account = db.basic(addr); + let mut account = db.basic(addr)?.ok_or(DatabaseError::MissingAccount(addr))?; account.code_hash = code_hash; account.code = code; db.insert_account(addr, account) } + Ok(()) } /// Returns the configured fork, if any @@ -256,6 +260,11 @@ impl Backend { &self.db } + /// Returns the `AccountInfo` from the database + pub async fn get_account(&self, address: Address) -> DatabaseResult { + self.db.read().await.basic(address)?.ok_or(DatabaseError::MissingAccount(address)) + } + /// Whether we're forked off some remote client pub fn is_fork(&self) -> bool { self.fork.is_some() @@ -368,13 +377,13 @@ impl Backend { } /// Returns balance of the given account. - pub async fn current_balance(&self, address: Address) -> U256 { - self.db.read().await.basic(address).balance + pub async fn current_balance(&self, address: Address) -> DatabaseResult { + Ok(self.get_account(address).await?.balance) } /// Returns balance of the given account. - pub async fn current_nonce(&self, address: Address) -> U256 { - self.db.read().await.basic(address).nonce.into() + pub async fn current_nonce(&self, address: Address) -> DatabaseResult { + Ok(self.get_account(address).await?.nonce.into()) } /// Sets the coinbase address @@ -383,18 +392,18 @@ impl Backend { } /// Sets the nonce of the given address - pub async fn set_nonce(&self, address: Address, nonce: U256) { - self.db.write().await.set_nonce(address, nonce.try_into().unwrap_or(u64::MAX)); + pub async fn set_nonce(&self, address: Address, nonce: U256) -> DatabaseResult<()> { + self.db.write().await.set_nonce(address, nonce.try_into().unwrap_or(u64::MAX)) } /// Sets the balance of the given address - pub async fn set_balance(&self, address: Address, balance: U256) { - self.db.write().await.set_balance(address, balance); + pub async fn set_balance(&self, address: Address, balance: U256) -> DatabaseResult<()> { + self.db.write().await.set_balance(address, balance) } /// Sets the code of the given address - pub async fn set_code(&self, address: Address, code: Bytes) { - self.db.write().await.set_code(address, code); + pub async fn set_code(&self, address: Address, code: Bytes) -> DatabaseResult<()> { + self.db.write().await.set_code(address, code) } /// Sets the value for the given slot of the given address @@ -491,7 +500,7 @@ impl Backend { self.db .read() .await - .dump_state() + .dump_state()? .map(|s| serde_json::to_vec(&s).unwrap_or_default().into()) .ok_or_else(|| { RpcError::invalid_params( @@ -506,7 +515,7 @@ impl Backend { let state: SerializableState = serde_json::from_slice(&buf.0).map_err(|_| BlockchainError::FailedToDecodeStateDump)?; - if !self.db.write().await.load_state(state) { + if !self.db.write().await.load_state(state)? { Err(RpcError::invalid_params( "Loading state not supported with the current configuration", ) @@ -800,7 +809,7 @@ impl Backend { block_env: BlockEnv, ) -> Result<(Return, TransactOut, u64, State), BlockchainError> where - D: DatabaseRef, + D: DatabaseRef, { let mut inspector = Inspector::default(); let mut evm = revm::EVM::new(); @@ -820,13 +829,16 @@ impl Backend { block_env: BlockEnv, ) -> Result<(Return, TransactOut, u64, AccessList), BlockchainError> where - D: DatabaseRef, + D: DatabaseRef, { let from = request.from.unwrap_or_default(); - let to = request.to.unwrap_or_else(|| { - let nonce = state.basic(from).nonce; + let to = if let Some(to) = request.to { + to + } else { + let nonce = + state.basic(from)?.ok_or(DatabaseError::MissingAccount(from))?.nonce; get_contract_address(from, nonce) - }); + }; let mut tracer = AccessListTracer::new( AccessList(request.access_list.clone().unwrap_or_default()), @@ -1273,7 +1285,7 @@ impl Backend { ) -> Result { self.with_database_at(block_request, |db, _| { trace!(target: "backend", "get storage for {:?} at {:?}", address, index); - let val = db.storage(address, index); + let val = db.storage(address, index)?; Ok(u256_to_h256_be(val)) }) .await? @@ -1297,10 +1309,11 @@ impl Backend { address: Address, ) -> Result where - D: DatabaseRef, + D: DatabaseRef, { trace!(target: "backend", "get code for {:?}", address); - let account = state.basic(address); + let account = + state.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; if account.code_hash == KECCAK_EMPTY { // if the code hash is `KECCAK_EMPTY`, we check no further return Ok(Default::default()) @@ -1308,7 +1321,7 @@ impl Backend { let code = if let Some(code) = account.code { code } else { - state.code_by_hash(account.code_hash) + state.code_by_hash(account.code_hash)? }; Ok(code.bytes()[..code.len()].to_vec().into()) } @@ -1331,10 +1344,10 @@ impl Backend { address: Address, ) -> Result where - D: DatabaseRef, + D: DatabaseRef, { trace!(target: "backend", "get balance for {:?}", address); - Ok(state.basic(address).balance) + Ok(state.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?.balance) } /// Returns the nonce of the address @@ -1347,7 +1360,11 @@ impl Backend { ) -> Result { self.with_database_at(block_request, |db, _| { trace!(target: "backend", "get nonce for {:?}", address); - Ok(db.basic(address).nonce.into()) + Ok(db + .basic(address)? + .ok_or(DatabaseError::MissingAccount(address))? + .nonce + .into()) }) .await? } @@ -1677,10 +1694,11 @@ impl TransactionValidator for Backend { async fn validate_pool_transaction( &self, tx: &PendingTransaction, - ) -> Result<(), InvalidTransactionError> { - let account = self.db.read().await.basic(*tx.sender()); + ) -> Result<(), BlockchainError> { + let address = *tx.sender(); + let account = self.get_account(address).await?; let env = self.next_env(); - self.validate_pool_transaction_for(tx, &account, &env) + Ok(self.validate_pool_transaction_for(tx, &account, &env)?) } fn validate_pool_transaction_for( diff --git a/anvil/src/eth/backend/mem/state.rs b/anvil/src/eth/backend/mem/state.rs index 8eb33cb263e0..cb8f8a4ca61a 100644 --- a/anvil/src/eth/backend/mem/state.rs +++ b/anvil/src/eth/backend/mem/state.rs @@ -1,5 +1,5 @@ //! Support for generating the state root for memdb storage -use std::collections::BTreeMap; + use crate::eth::{backend::db::AsHashDB, error::BlockchainError}; use anvil_core::eth::{state::StateOverride, trie::RefSecTrieDBMut}; @@ -16,7 +16,11 @@ use forge::{ Bytecode, }, }; -use foundry_evm::revm::{AccountInfo, Log}; +use foundry_evm::{ + executor::backend::DatabaseError, + revm::{AccountInfo, Log}, + HashMap as Map, +}; use memory_db::HashKey; use trie_db::TrieMut; @@ -40,7 +44,7 @@ pub fn log_rlp_hash(logs: Vec) -> H256 { } /// Returns storage trie of an account as `HashDB` -pub fn storage_trie_db(storage: &BTreeMap) -> (AsHashDB, H256) { +pub fn storage_trie_db(storage: &Map) -> (AsHashDB, H256) { // Populate DB with full trie from entries. let (db, root) = { let mut db = , _>>::default(); @@ -62,7 +66,7 @@ pub fn storage_trie_db(storage: &BTreeMap) -> (AsHashDB, H256) { } /// Returns the account data as `HashDB` -pub fn trie_hash_db(accounts: &BTreeMap) -> (AsHashDB, H256) { +pub fn trie_hash_db(accounts: &Map) -> (AsHashDB, H256) { let accounts = trie_accounts(accounts); // Populate DB with full trie from entries. @@ -82,7 +86,7 @@ pub fn trie_hash_db(accounts: &BTreeMap) -> (AsHashDB, H256) } /// Returns all RLP-encoded Accounts -pub fn trie_accounts(accounts: &BTreeMap) -> Vec<(Address, Bytes)> { +pub fn trie_accounts(accounts: &Map) -> Vec<(Address, Bytes)> { accounts .iter() .map(|(address, account)| { @@ -92,12 +96,12 @@ pub fn trie_accounts(accounts: &BTreeMap) -> Vec<(Address, B .collect() } -pub fn state_merkle_trie_root(accounts: &BTreeMap) -> H256 { +pub fn state_merkle_trie_root(accounts: &Map) -> H256 { trie_hash_db(accounts).1 } /// Returns the RLP for this account. -pub fn trie_account_rlp(info: &AccountInfo, storage: &BTreeMap) -> Bytes { +pub fn trie_account_rlp(info: &AccountInfo, storage: &Map) -> Bytes { let mut stream = RlpStream::new_list(4); stream.append(&info.nonce); stream.append(&info.balance); @@ -112,11 +116,12 @@ pub fn apply_state_override( state: D, ) -> Result, BlockchainError> where - D: DatabaseRef, + D: DatabaseRef, { let mut cache_db = CacheDB::new(state); for (account, account_overrides) in overrides.iter() { - let mut account_info = cache_db.basic(*account); + let mut account_info = + cache_db.basic(*account)?.ok_or(DatabaseError::MissingAccount(*account))?; if let Some(nonce) = account_overrides.nonce { account_info.nonce = nonce; @@ -136,7 +141,7 @@ where match (&account_overrides.state, &account_overrides.state_diff) { (Some(_), Some(_)) => { return Err(BlockchainError::StateOverrideError( - "state and state_diff can't be used together".into(), + "state and state_diff can't be used together".to_string(), )) } (None, None) => (), @@ -147,11 +152,15 @@ where .iter() .map(|(key, value)| (key.into_uint(), value.into_uint())) .collect(), - ); + )?; } (None, Some(account_state_diff)) => { for (key, value) in account_state_diff.iter() { - cache_db.insert_account_storage(*account, key.into_uint(), value.into_uint()); + cache_db.insert_account_storage( + *account, + key.into_uint(), + value.into_uint(), + )?; } } }; diff --git a/anvil/src/eth/backend/mem/storage.rs b/anvil/src/eth/backend/mem/storage.rs index 96a5c2107624..9bc7ad01035e 100644 --- a/anvil/src/eth/backend/mem/storage.rs +++ b/anvil/src/eth/backend/mem/storage.rs @@ -310,7 +310,7 @@ mod tests { let loaded = storage.get(&one).unwrap(); - let acc = loaded.basic(addr); + let acc = loaded.basic(addr).unwrap().unwrap(); assert_eq!(acc.balance, 1337u64.into()); } } diff --git a/anvil/src/eth/backend/validate.rs b/anvil/src/eth/backend/validate.rs index bce654cb6d9a..83f68f5c4c14 100644 --- a/anvil/src/eth/backend/validate.rs +++ b/anvil/src/eth/backend/validate.rs @@ -1,6 +1,6 @@ //! Support for validating transactions at certain stages -use crate::eth::error::InvalidTransactionError; +use crate::eth::error::{BlockchainError, InvalidTransactionError}; use anvil_core::eth::transaction::PendingTransaction; use foundry_evm::revm::{AccountInfo, Env}; @@ -15,7 +15,7 @@ pub trait TransactionValidator { async fn validate_pool_transaction( &self, tx: &PendingTransaction, - ) -> Result<(), InvalidTransactionError>; + ) -> Result<(), BlockchainError>; /// Validates the transaction against a specific account before entering the pool fn validate_pool_transaction_for( diff --git a/anvil/src/eth/error.rs b/anvil/src/eth/error.rs index 2bed36a2a675..1f81321b345d 100644 --- a/anvil/src/eth/error.rs +++ b/anvil/src/eth/error.rs @@ -12,7 +12,7 @@ use ethers::{ types::{Bytes, SignatureError, U256}, }; use foundry_common::SELECTOR_LEN; -use foundry_evm::revm::Return; +use foundry_evm::{executor::backend::DatabaseError, revm::Return}; use serde::Serialize; use tracing::error; @@ -70,6 +70,8 @@ pub enum BlockchainError { StateOverrideError(String), #[error("Timestamp error: {0}")] TimestampError(String), + #[error(transparent)] + DatabaseError(#[from] DatabaseError), } impl From for BlockchainError { @@ -268,6 +270,9 @@ impl ToRpcResponseResult for Result { err @ BlockchainError::TimestampError(_) => { RpcError::invalid_params(err.to_string()) } + BlockchainError::DatabaseError(err) => { + RpcError::internal_error_with(err.to_string()) + } } .into(), } diff --git a/anvil/src/eth/util.rs b/anvil/src/eth/util.rs index 02e95fc38514..8250fca2062a 100644 --- a/anvil/src/eth/util.rs +++ b/anvil/src/eth/util.rs @@ -1,31 +1,10 @@ use ethers::abi::Address; use forge::revm::SpecId; +use foundry_evm::revm::precompiles::Precompiles; use std::fmt; -macro_rules! precompiles_for { - ($spec:ident) => {{ - let precompiles = - revm_precompiles::Precompiles::new::<{ SpecId::to_precompile_id(SpecId::$spec) }>(); - precompiles.as_slice().iter().map(|(a, _)| a).copied().collect() - }}; -} - pub fn get_precompiles_for(spec_id: SpecId) -> Vec
{ - match spec_id { - SpecId::FRONTIER => precompiles_for!(FRONTIER), - SpecId::HOMESTEAD => precompiles_for!(HOMESTEAD), - SpecId::TANGERINE => precompiles_for!(TANGERINE), - SpecId::SPURIOUS_DRAGON => precompiles_for!(SPURIOUS_DRAGON), - SpecId::BYZANTIUM => precompiles_for!(BYZANTIUM), - SpecId::CONSTANTINOPLE => precompiles_for!(CONSTANTINOPLE), - SpecId::PETERSBURG => precompiles_for!(PETERSBURG), - SpecId::ISTANBUL => precompiles_for!(ISTANBUL), - SpecId::MUIRGLACIER => precompiles_for!(MUIRGLACIER), - SpecId::BERLIN => precompiles_for!(BERLIN), - SpecId::LONDON => precompiles_for!(LONDON), - SpecId::MERGE => precompiles_for!(MERGE), - SpecId::LATEST => precompiles_for!(LATEST), - } + Precompiles::new(spec_id.to_precompile_id()).addresses().into_iter().copied().collect() } /// wrapper type that displays byte as hex diff --git a/anvil/src/hardfork.rs b/anvil/src/hardfork.rs index cbe545f796f5..029af9d2b276 100644 --- a/anvil/src/hardfork.rs +++ b/anvil/src/hardfork.rs @@ -132,7 +132,7 @@ impl From for SpecId { Hardfork::Constantinople => SpecId::CONSTANTINOPLE, Hardfork::Petersburg => SpecId::PETERSBURG, Hardfork::Istanbul => SpecId::ISTANBUL, - Hardfork::Muirglacier => SpecId::MUIRGLACIER, + Hardfork::Muirglacier => SpecId::MUIR_GLACIER, Hardfork::Berlin => SpecId::BERLIN, Hardfork::London => SpecId::LONDON, Hardfork::ArrowGlacier => SpecId::LONDON, From 61afb08ea52677bf96d2f65492a75d9a89325093 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 1 Sep 2022 21:12:43 +0200 Subject: [PATCH 06/13] unify error types --- anvil/src/eth/backend/db.rs | 10 ++--- anvil/src/eth/backend/mem/fork_db.rs | 3 +- anvil/src/eth/backend/mem/in_memory_db.rs | 8 ++-- anvil/src/eth/backend/mem/mod.rs | 15 ++------ anvil/src/eth/backend/mem/state.rs | 1 - evm/src/executor/backend/in_memory_db.rs | 47 ++++++++++++++++++----- 6 files changed, 48 insertions(+), 36 deletions(-) diff --git a/anvil/src/eth/backend/db.rs b/anvil/src/eth/backend/db.rs index ff12641acb09..c2f65648bbe9 100644 --- a/anvil/src/eth/backend/db.rs +++ b/anvil/src/eth/backend/db.rs @@ -22,7 +22,6 @@ use foundry_evm::{ use hash_db::HashDB; use serde::{Deserialize, Serialize}; - /// Type alias for the `HashDB` representation of the Database pub type AsHashDB = Box>>; @@ -77,8 +76,7 @@ pub trait Db: /// Sets the nonce of the given address fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<()> { - let mut info = - self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let mut info = self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; info.nonce = nonce; self.insert_account(address, info); Ok(()) @@ -86,8 +84,7 @@ pub trait Db: /// Sets the balance of the given address fn set_balance(&mut self, address: Address, balance: U256) -> DatabaseResult<()> { - let mut info = - self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let mut info = self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; info.balance = balance; self.insert_account(address, info); Ok(()) @@ -95,8 +92,7 @@ pub trait Db: /// Sets the balance of the given address fn set_code(&mut self, address: Address, code: Bytes) -> DatabaseResult<()> { - let mut info = - self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let mut info = self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; let code_hash = if code.as_ref().is_empty() { KECCAK_EMPTY } else { diff --git a/anvil/src/eth/backend/mem/fork_db.rs b/anvil/src/eth/backend/mem/fork_db.rs index 88e745536801..ae8d15502bff 100644 --- a/anvil/src/eth/backend/mem/fork_db.rs +++ b/anvil/src/eth/backend/mem/fork_db.rs @@ -19,8 +19,7 @@ impl Db for ForkedDatabase { fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { // this ensures the account is loaded first - let _ = Database::basic(self, address)? - .ok_or(DatabaseError::MissingAccount(address))?; + let _ = Database::basic(self, address)?.ok_or(DatabaseError::MissingAccount(address))?; self.database_mut().set_storage_at(address, slot, val) } diff --git a/anvil/src/eth/backend/mem/in_memory_db.rs b/anvil/src/eth/backend/mem/in_memory_db.rs index a39f24bd7583..d8cb959768ae 100644 --- a/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/anvil/src/eth/backend/mem/in_memory_db.rs @@ -130,13 +130,11 @@ impl MaybeHashDatabase for MemDb { } fn clear_into_snapshot(&mut self) -> StateSnapshot { - // self.inner.clear_into_snapshot() - todo!() + self.inner.clear_into_snapshot() } - fn init_from_snapshot(&mut self, _snapshot: StateSnapshot) { - // self.inner.init_from_snapshot(snapshot) - todo!() + fn init_from_snapshot(&mut self, snapshot: StateSnapshot) { + self.inner.init_from_snapshot(snapshot) } } diff --git a/anvil/src/eth/backend/mem/mod.rs b/anvil/src/eth/backend/mem/mod.rs index c776dc099b0f..e6236e221c62 100644 --- a/anvil/src/eth/backend/mem/mod.rs +++ b/anvil/src/eth/backend/mem/mod.rs @@ -196,8 +196,7 @@ impl Backend { fork_genesis_infos.clear(); for address in self.genesis.accounts.iter().copied() { - let mut info = - db.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let mut info = db.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; info.balance = self.genesis.balance; db.insert_account(address, info.clone()); @@ -835,8 +834,7 @@ impl Backend { let to = if let Some(to) = request.to { to } else { - let nonce = - state.basic(from)?.ok_or(DatabaseError::MissingAccount(from))?.nonce; + let nonce = state.basic(from)?.ok_or(DatabaseError::MissingAccount(from))?.nonce; get_contract_address(from, nonce) }; @@ -1312,8 +1310,7 @@ impl Backend { D: DatabaseRef, { trace!(target: "backend", "get code for {:?}", address); - let account = - state.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let account = state.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; if account.code_hash == KECCAK_EMPTY { // if the code hash is `KECCAK_EMPTY`, we check no further return Ok(Default::default()) @@ -1360,11 +1357,7 @@ impl Backend { ) -> Result { self.with_database_at(block_request, |db, _| { trace!(target: "backend", "get nonce for {:?}", address); - Ok(db - .basic(address)? - .ok_or(DatabaseError::MissingAccount(address))? - .nonce - .into()) + Ok(db.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?.nonce.into()) }) .await? } diff --git a/anvil/src/eth/backend/mem/state.rs b/anvil/src/eth/backend/mem/state.rs index cb8f8a4ca61a..d2ef1c809906 100644 --- a/anvil/src/eth/backend/mem/state.rs +++ b/anvil/src/eth/backend/mem/state.rs @@ -1,6 +1,5 @@ //! Support for generating the state root for memdb storage - use crate::eth::{backend::db::AsHashDB, error::BlockchainError}; use anvil_core::eth::{state::StateOverride, trie::RefSecTrieDBMut}; use bytes::Bytes; diff --git a/evm/src/executor/backend/in_memory_db.rs b/evm/src/executor/backend/in_memory_db.rs index 48ebcef9f07d..89876a00871b 100644 --- a/evm/src/executor/backend/in_memory_db.rs +++ b/evm/src/executor/backend/in_memory_db.rs @@ -5,10 +5,13 @@ use ethers::{ types::Address, }; use hashbrown::HashMap as Map; -use revm::{db::DatabaseRef, Account, AccountInfo, Bytecode, Database, DatabaseCommit, InMemoryDB}; +use revm::{db::DatabaseRef, Account, AccountInfo, Bytecode, Database, DatabaseCommit}; +use revm::db::{CacheDB, EmptyDB}; use crate::executor::snapshot::Snapshots; +pub type InMemoryDB = CacheDB; + /// In memory Database for anvil /// /// This acts like a wrapper type for [InMemoryDB] but is capable of applying snapshots @@ -20,45 +23,45 @@ pub struct MemDb { impl Default for MemDb { fn default() -> Self { - Self { inner: InMemoryDB::default(), snapshots: Default::default() } + Self { inner: CacheDB::new(Default::default()), snapshots: Default::default() } } } impl DatabaseRef for MemDb { type Error = DatabaseError; fn basic(&self, address: Address) -> Result, Self::Error> { - Ok(DatabaseRef::basic(&self.inner, address)?) + DatabaseRef::basic(&self.inner, address) } fn code_by_hash(&self, code_hash: H256) -> Result { - Ok(DatabaseRef::code_by_hash(&self.inner, code_hash)?) + DatabaseRef::code_by_hash(&self.inner, code_hash) } fn storage(&self, address: Address, index: U256) -> Result { - Ok(DatabaseRef::storage(&self.inner, address, index)?) + DatabaseRef::storage(&self.inner, address, index) } fn block_hash(&self, number: U256) -> Result { - Ok(DatabaseRef::block_hash(&self.inner, number)?) + DatabaseRef::block_hash(&self.inner, number) } } impl Database for MemDb { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { - Ok(Database::basic(&mut self.inner, address)?) + Database::basic(&mut self.inner, address) } fn code_by_hash(&mut self, code_hash: H256) -> Result { - Ok(Database::code_by_hash(&mut self.inner, code_hash)?) + Database::code_by_hash(&mut self.inner, code_hash) } fn storage(&mut self, address: Address, index: U256) -> Result { - Ok(Database::storage(&mut self.inner, address, index)?) + Database::storage(&mut self.inner, address, index) } fn block_hash(&mut self, number: U256) -> Result { - Ok(Database::block_hash(&mut self.inner, number)?) + Database::block_hash(&mut self.inner, number) } } @@ -67,3 +70,27 @@ impl DatabaseCommit for MemDb { DatabaseCommit::commit(&mut self.inner, changes) } } + + +/// An empty database that always returns default values when queried. +/// +/// This is just a simple wrapper for `revm::EmptyDB` but implements `DatabaseError` instead, this way we can unify all different `Database` impls +#[derive(Debug, Default, Clone)] +pub struct EmptyDBWrapper(EmptyDB); + +impl DatabaseRef for EmptyDBWrapper { + type Error = DatabaseError; + fn basic(&self, address: Address) -> Result, Self::Error> { + Ok(self.0.basic(address)?) + } + fn code_by_hash(&self, code_hash: H256) -> Result { + Ok(self.0.code_by_hash(code_hash)?) + } + fn storage(&self, address: Address, index: U256) -> Result { + Ok(self.0.storage(address, index)?) + } + + fn block_hash(&self, number: U256) -> Result { + Ok(self.0.block_hash(number)?) + } +} \ No newline at end of file From d90f58bfcfe47b4a98e3edd61e4c119e4ac6b5dc Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 1 Sep 2022 23:11:58 +0200 Subject: [PATCH 07/13] fix more breaking changes --- Cargo.lock | 46 +++++++++---------- Cargo.toml | 4 +- anvil/src/eth/api.rs | 8 ++-- anvil/src/eth/backend/db.rs | 10 ++-- anvil/src/eth/backend/executor.rs | 12 ++--- anvil/src/eth/backend/genesis.rs | 10 ++-- anvil/src/eth/backend/mem/fork_db.rs | 4 +- anvil/src/eth/backend/mem/in_memory_db.rs | 2 +- anvil/src/eth/backend/mem/mod.rs | 30 +++++++----- anvil/src/eth/backend/mem/state.rs | 3 +- anvil/tests/it/anvil_api.rs | 8 +++- cli/src/cmd/forge/script/runner.rs | 14 +++--- evm/src/executor/backend/in_memory_db.rs | 13 ++++-- evm/src/executor/backend/mod.rs | 5 +- evm/src/executor/inspector/cheatcodes/mod.rs | 2 +- evm/src/executor/inspector/cheatcodes/util.rs | 2 - evm/src/executor/mod.rs | 13 ++---- forge/src/runner.rs | 12 ++--- forge/tests/it/invariant.rs | 4 +- 19 files changed, 104 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47f8fc8ce5ce..be9eaa373601 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -828,7 +828,7 @@ dependencies = [ "k256", "lazy_static", "serde", - "sha2 0.10.2", + "sha2 0.10.3", "thiserror", ] @@ -845,7 +845,7 @@ dependencies = [ "hmac", "pbkdf2 0.11.0", "rand 0.8.5", - "sha2 0.10.2", + "sha2 0.10.3", "thiserror", ] @@ -865,7 +865,7 @@ dependencies = [ "ripemd", "serde", "serde_derive", - "sha2 0.10.2", + "sha2 0.10.3", "sha3", "thiserror", ] @@ -1564,7 +1564,7 @@ dependencies = [ "scrypt", "serde", "serde_json", - "sha2 0.10.2", + "sha2 0.10.3", "sha3", "thiserror", "uuid", @@ -1834,7 +1834,7 @@ dependencies = [ "home", "rand 0.8.5", "semver", - "sha2 0.10.2", + "sha2 0.10.3", "thiserror", "trezor-client", ] @@ -1864,7 +1864,7 @@ dependencies = [ "semver", "serde", "serde_json", - "sha2 0.10.2", + "sha2 0.10.3", "solang-parser", "svm-rs", "svm-rs-builds", @@ -3050,7 +3050,7 @@ dependencies = [ "cfg-if 1.0.0", "ecdsa", "elliptic-curve", - "sha2 0.10.2", + "sha2 0.10.3", "sha3", ] @@ -3798,7 +3798,7 @@ dependencies = [ "digest 0.10.3", "hmac", "password-hash 0.3.2", - "sha2 0.10.2", + "sha2 0.10.3", ] [[package]] @@ -3810,7 +3810,7 @@ dependencies = [ "digest 0.10.3", "hmac", "password-hash 0.4.2", - "sha2 0.10.2", + "sha2 0.10.3", ] [[package]] @@ -4477,7 +4477,6 @@ dependencies = [ [[package]] name = "revm" version = "1.9.0" -source = "git+https://github.com/bluealloy/revm#99eb1f5e74221198d3c8da883a6a73025f9f37d6" dependencies = [ "arrayref", "auto_impl 1.0.1", @@ -4486,7 +4485,7 @@ dependencies = [ "hex", "num_enum", "primitive-types", - "revm_precompiles 1.1.0 (git+https://github.com/bluealloy/revm)", + "revm_precompiles 1.1.0", "rlp", "serde", "sha3", @@ -4495,15 +4494,16 @@ dependencies = [ [[package]] name = "revm_precompiles" version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af88e7e9feb30cc4ed64645f09b966e84a1f6be56551ce5f1691105def45705d" dependencies = [ "bytes", + "hashbrown 0.12.0", + "k256", "num", + "once_cell", "primitive-types", "ripemd", - "secp256k1 0.23.4", - "sha2 0.10.2", + "secp256k1 0.24.0", + "sha2 0.10.3", "sha3", "substrate-bn", ] @@ -4511,17 +4511,15 @@ dependencies = [ [[package]] name = "revm_precompiles" version = "1.1.0" -source = "git+https://github.com/bluealloy/revm#99eb1f5e74221198d3c8da883a6a73025f9f37d6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af88e7e9feb30cc4ed64645f09b966e84a1f6be56551ce5f1691105def45705d" dependencies = [ "bytes", - "hashbrown 0.12.0", - "k256", "num", - "once_cell", "primitive-types", "ripemd", - "secp256k1 0.24.0", - "sha2 0.10.2", + "secp256k1 0.23.4", + "sha2 0.10.3", "sha3", "substrate-bn", ] @@ -4734,7 +4732,7 @@ dependencies = [ "hmac", "pbkdf2 0.11.0", "salsa20", - "sha2 0.10.2", + "sha2 0.10.3", ] [[package]] @@ -4987,9 +4985,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "899bf02746a2c92bf1053d9327dadb252b01af1f81f90cdb902411f518bc7215" dependencies = [ "cfg-if 1.0.0", "cpufeatures", diff --git a/Cargo.toml b/Cargo.toml index 9ac29933a535..125333d3fdb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,5 +64,5 @@ debug = 0 #ethers-solc = { path = "../ethers-rs/ethers-solc" } [patch.crates-io] -#revm = { path = "../../revm/crates/revm" } -revm = { git = "https://github.com/bluealloy/revm" } +revm = { path = "../revm/crates/revm" } +#revm = { git = "https://github.com/bluealloy/revm" } diff --git a/anvil/src/eth/api.rs b/anvil/src/eth/api.rs index d62d7eb5d94a..4a3fc79dbc88 100644 --- a/anvil/src/eth/api.rs +++ b/anvil/src/eth/api.rs @@ -1339,7 +1339,7 @@ impl EthApi { /// Handler for RPC call: `anvil_setBalance` pub async fn anvil_set_balance(&self, address: Address, balance: U256) -> Result<()> { node_info!("anvil_setBalance"); - self.backend.set_balance(address, balance).await; + self.backend.set_balance(address, balance).await?; Ok(()) } @@ -1348,7 +1348,7 @@ impl EthApi { /// Handler for RPC call: `anvil_setCode` pub async fn anvil_set_code(&self, address: Address, code: Bytes) -> Result<()> { node_info!("anvil_setCode"); - self.backend.set_code(address, code).await; + self.backend.set_code(address, code).await?; Ok(()) } @@ -1357,7 +1357,7 @@ impl EthApi { /// Handler for RPC call: `anvil_setNonce` pub async fn anvil_set_nonce(&self, address: Address, nonce: U256) -> Result<()> { node_info!("anvil_setNonce"); - self.backend.set_nonce(address, nonce).await; + self.backend.set_nonce(address, nonce).await?; Ok(()) } @@ -1371,7 +1371,7 @@ impl EthApi { val: H256, ) -> Result { node_info!("anvil_setStorageAt"); - self.backend.set_storage_at(address, slot, val).await; + self.backend.set_storage_at(address, slot, val).await?; Ok(true) } diff --git a/anvil/src/eth/backend/db.rs b/anvil/src/eth/backend/db.rs index c2f65648bbe9..4b27d5923ef1 100644 --- a/anvil/src/eth/backend/db.rs +++ b/anvil/src/eth/backend/db.rs @@ -21,6 +21,7 @@ use foundry_evm::{ }; use hash_db::HashDB; use serde::{Deserialize, Serialize}; +use std::fmt; /// Type alias for the `HashDB` representation of the Database pub type AsHashDB = Box>>; @@ -68,6 +69,7 @@ pub trait Db: + Database + DatabaseCommit + MaybeHashDatabase + + fmt::Debug + Send + Sync { @@ -76,7 +78,7 @@ pub trait Db: /// Sets the nonce of the given address fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<()> { - let mut info = self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let mut info = self.basic(address)?.unwrap_or_default(); info.nonce = nonce; self.insert_account(address, info); Ok(()) @@ -84,7 +86,7 @@ pub trait Db: /// Sets the balance of the given address fn set_balance(&mut self, address: Address, balance: U256) -> DatabaseResult<()> { - let mut info = self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let mut info = self.basic(address)?.unwrap_or_default(); info.balance = balance; self.insert_account(address, info); Ok(()) @@ -92,7 +94,7 @@ pub trait Db: /// Sets the balance of the given address fn set_code(&mut self, address: Address, code: Bytes) -> DatabaseResult<()> { - let mut info = self.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let mut info = self.basic(address)?.unwrap_or_default(); let code_hash = if code.as_ref().is_empty() { KECCAK_EMPTY } else { @@ -137,7 +139,7 @@ pub trait Db: /// This is useful to create blocks without actually writing to the `Db`, but rather in the cache of /// the `CacheDB` see also /// [Backend::pending_block()](crate::eth::backend::mem::Backend::pending_block()) -impl + Send + Sync + Clone> Db for CacheDB { +impl + Send + Sync + Clone + fmt::Debug> Db for CacheDB { fn insert_account(&mut self, address: Address, account: AccountInfo) { self.insert_account_info(address, account) } diff --git a/anvil/src/eth/backend/executor.rs b/anvil/src/eth/backend/executor.rs index 78c50491d27a..75a9d41c9001 100644 --- a/anvil/src/eth/backend/executor.rs +++ b/anvil/src/eth/backend/executor.rs @@ -225,15 +225,11 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator fn next(&mut self) -> Option { let transaction = self.pending.next()?; let sender = *transaction.pending_transaction.sender(); - let account = match self.db.basic(sender) { - Ok(Some(account)) => account, - Ok(None) => { - return Some(TransactionExecutionOutcome::DatabaseError( - transaction, - DatabaseError::MissingAccount(sender), - )) + let account = match self.db.basic(sender).map(|acc| acc.unwrap_or_default()) { + Ok(account) => account, + Err(err) => { + return { Some(TransactionExecutionOutcome::DatabaseError(transaction, err)) } } - Err(err) => return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)), }; let env = self.env_for(&transaction.pending_transaction); // check that we comply with the block's gas limit diff --git a/anvil/src/eth/backend/genesis.rs b/anvil/src/eth/backend/genesis.rs index 96ba41d4cfd7..849a4cf43676 100644 --- a/anvil/src/eth/backend/genesis.rs +++ b/anvil/src/eth/backend/genesis.rs @@ -6,7 +6,7 @@ use ethers::{ types::{Address, U256}, }; use forge::revm::KECCAK_EMPTY; -use foundry_evm::revm::AccountInfo; +use foundry_evm::{executor::backend::DatabaseResult, revm::AccountInfo}; use parking_lot::Mutex; use std::sync::Arc; use tokio::sync::RwLockWriteGuard; @@ -47,7 +47,10 @@ impl GenesisConfig { } /// If an initial `genesis.json` was provided, this applies the account alloc to the db - pub fn apply_genesis_json_alloc(&self, mut db: RwLockWriteGuard<'_, dyn Db>) { + pub fn apply_genesis_json_alloc( + &self, + mut db: RwLockWriteGuard<'_, dyn Db>, + ) -> DatabaseResult<()> { if let Some(ref genesis) = self.genesis_init { for (addr, mut acc) in genesis.alloc.accounts.clone() { let storage = std::mem::take(&mut acc.storage); @@ -55,9 +58,10 @@ impl GenesisConfig { db.insert_account(addr, acc.into()); // insert all storage values for (k, v) in storage.iter() { - db.set_storage_at(addr, k.into_uint(), v.into_uint()); + db.set_storage_at(addr, k.into_uint(), v.into_uint())?; } } } + Ok(()) } } diff --git a/anvil/src/eth/backend/mem/fork_db.rs b/anvil/src/eth/backend/mem/fork_db.rs index ae8d15502bff..b4a7aff59f56 100644 --- a/anvil/src/eth/backend/mem/fork_db.rs +++ b/anvil/src/eth/backend/mem/fork_db.rs @@ -7,7 +7,7 @@ use ethers::prelude::H256; use forge::revm::Database; pub use foundry_evm::executor::fork::database::ForkedDatabase; use foundry_evm::executor::{ - backend::{snapshot::StateSnapshot, DatabaseError, DatabaseResult}, + backend::{snapshot::StateSnapshot, DatabaseResult}, fork::database::ForkDbSnapshot, }; @@ -19,7 +19,7 @@ impl Db for ForkedDatabase { fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { // this ensures the account is loaded first - let _ = Database::basic(self, address)?.ok_or(DatabaseError::MissingAccount(address))?; + let _ = Database::basic(self, address)?; self.database_mut().set_storage_at(address, slot, val) } diff --git a/anvil/src/eth/backend/mem/in_memory_db.rs b/anvil/src/eth/backend/mem/in_memory_db.rs index d8cb959768ae..5735e8b65cbb 100644 --- a/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/anvil/src/eth/backend/mem/in_memory_db.rs @@ -23,7 +23,7 @@ impl Db for MemDb { } fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { - Ok(self.inner.insert_account_storage(address, slot, val)?) + self.inner.insert_account_storage(address, slot, val) } fn insert_block_hash(&mut self, number: U256, hash: H256) { diff --git a/anvil/src/eth/backend/mem/mod.rs b/anvil/src/eth/backend/mem/mod.rs index e6236e221c62..7981c6ce000a 100644 --- a/anvil/src/eth/backend/mem/mod.rs +++ b/anvil/src/eth/backend/mem/mod.rs @@ -177,7 +177,8 @@ impl Backend { active_snapshots: Arc::new(Mutex::new(Default::default())), }; - backend.apply_genesis().await; + // Note: this can only fail in forking mode, in which case we can't recover + backend.apply_genesis().await.expect("Failed to create genesis"); backend } @@ -196,7 +197,7 @@ impl Backend { fork_genesis_infos.clear(); for address in self.genesis.accounts.iter().copied() { - let mut info = db.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let mut info = db.basic(address)?.unwrap_or_default(); info.balance = self.genesis.balance; db.insert_account(address, info.clone()); @@ -210,7 +211,7 @@ impl Backend { } // apply the genesis.json alloc - self.genesis.apply_genesis_json_alloc(db); + self.genesis.apply_genesis_json_alloc(db)?; Ok(()) } @@ -241,7 +242,7 @@ impl Backend { pub async fn stop_impersonating(&self, addr: Address) -> DatabaseResult<()> { if let Some((Some(code_hash), code)) = self.cheats.stop_impersonating(&addr) { let mut db = self.db.write().await; - let mut account = db.basic(addr)?.ok_or(DatabaseError::MissingAccount(addr))?; + let mut account = db.basic(addr)?.unwrap_or_default(); account.code_hash = code_hash; account.code = code; db.insert_account(addr, account) @@ -261,7 +262,7 @@ impl Backend { /// Returns the `AccountInfo` from the database pub async fn get_account(&self, address: Address) -> DatabaseResult { - self.db.read().await.basic(address)?.ok_or(DatabaseError::MissingAccount(address)) + Ok(self.db.read().await.basic(address)?.unwrap_or_default()) } /// Whether we're forked off some remote client @@ -320,7 +321,7 @@ impl Backend { } // reset the genesis.json alloc - self.genesis.apply_genesis_json_alloc(db); + self.genesis.apply_genesis_json_alloc(db)?; Ok(()) } else { @@ -406,8 +407,13 @@ impl Backend { } /// Sets the value for the given slot of the given address - pub async fn set_storage_at(&self, address: Address, slot: U256, val: H256) { - self.db.write().await.set_storage_at(address, slot, val.into_uint()); + pub async fn set_storage_at( + &self, + address: Address, + slot: U256, + val: H256, + ) -> DatabaseResult<()> { + self.db.write().await.set_storage_at(address, slot, val.into_uint()) } /// Returns true for post London @@ -834,7 +840,7 @@ impl Backend { let to = if let Some(to) = request.to { to } else { - let nonce = state.basic(from)?.ok_or(DatabaseError::MissingAccount(from))?.nonce; + let nonce = state.basic(from)?.unwrap_or_default().nonce; get_contract_address(from, nonce) }; @@ -1310,7 +1316,7 @@ impl Backend { D: DatabaseRef, { trace!(target: "backend", "get code for {:?}", address); - let account = state.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let account = state.basic(address)?.unwrap_or_default(); if account.code_hash == KECCAK_EMPTY { // if the code hash is `KECCAK_EMPTY`, we check no further return Ok(Default::default()) @@ -1344,7 +1350,7 @@ impl Backend { D: DatabaseRef, { trace!(target: "backend", "get balance for {:?}", address); - Ok(state.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?.balance) + Ok(state.basic(address)?.unwrap_or_default().balance) } /// Returns the nonce of the address @@ -1357,7 +1363,7 @@ impl Backend { ) -> Result { self.with_database_at(block_request, |db, _| { trace!(target: "backend", "get nonce for {:?}", address); - Ok(db.basic(address)?.ok_or(DatabaseError::MissingAccount(address))?.nonce.into()) + Ok(db.basic(address)?.unwrap_or_default().nonce.into()) }) .await? } diff --git a/anvil/src/eth/backend/mem/state.rs b/anvil/src/eth/backend/mem/state.rs index d2ef1c809906..d2e8371024cf 100644 --- a/anvil/src/eth/backend/mem/state.rs +++ b/anvil/src/eth/backend/mem/state.rs @@ -119,8 +119,7 @@ where { let mut cache_db = CacheDB::new(state); for (account, account_overrides) in overrides.iter() { - let mut account_info = - cache_db.basic(*account)?.ok_or(DatabaseError::MissingAccount(*account))?; + let mut account_info = cache_db.basic(*account)?.unwrap_or_default(); if let Some(nonce) = account_overrides.nonce { account_info.nonce = nonce; diff --git a/anvil/tests/it/anvil_api.rs b/anvil/tests/it/anvil_api.rs index b6d354c5c35d..a1a6c684555c 100644 --- a/anvil/tests/it/anvil_api.rs +++ b/anvil/tests/it/anvil_api.rs @@ -55,15 +55,19 @@ async fn can_set_storage() { #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_account() { + crate::init_tracing(); let (api, handle) = spawn(NodeConfig::test()).await; let provider = handle.http_provider(); let impersonate = Address::random(); let to = Address::random(); let val = 1337u64; - + let funding = U256::from(1e18 as u64); // fund the impersonated account - api.anvil_set_balance(impersonate, U256::from(1e18 as u64)).await.unwrap(); + api.anvil_set_balance(impersonate, funding).await.unwrap(); + + let balance = api.balance(impersonate, None).await.unwrap(); + assert_eq!(balance, funding); let tx = TransactionRequest::new().from(impersonate).to(to).value(val); diff --git a/cli/src/cmd/forge/script/runner.rs b/cli/src/cmd/forge/script/runner.rs index b0d85ac0ded4..5b9e643ec9a9 100644 --- a/cli/src/cmd/forge/script/runner.rs +++ b/cli/src/cmd/forge/script/runner.rs @@ -38,7 +38,7 @@ impl ScriptRunner { if !is_broadcast { if self.sender == Config::DEFAULT_SENDER { // We max out their balance so that they can deploy and make calls. - self.executor.set_balance(self.sender, U256::MAX); + self.executor.set_balance(self.sender, U256::MAX)?; } if need_create2_deployer { @@ -46,10 +46,10 @@ impl ScriptRunner { } } - self.executor.set_nonce(self.sender, sender_nonce.as_u64()); + self.executor.set_nonce(self.sender, sender_nonce.as_u64())?; // We max out their balance so that they can deploy and make calls. - self.executor.set_balance(CALLER, U256::MAX); + self.executor.set_balance(CALLER, U256::MAX)?; // Deploy libraries let mut traces: Vec<(TraceKind, CallTraceArena)> = libraries @@ -78,7 +78,7 @@ impl ScriptRunner { .map_err(|err| eyre::eyre!("Failed to deploy script:\n{}", err))?; traces.extend(constructor_traces.map(|traces| (TraceKind::Deployment, traces)).into_iter()); - self.executor.set_balance(address, self.initial_balance); + self.executor.set_balance(address, self.initial_balance)?; // Optionally call the `setUp` function let (success, gas_used, labeled_addresses, transactions, debug) = if !setup { @@ -113,8 +113,10 @@ impl ScriptRunner { // any broadcasts, then the EVM cheatcode module hasn't corrected the nonce. // So we have to if transactions.is_none() || transactions.as_ref().unwrap().is_empty() { - self.executor - .set_nonce(self.sender, sender_nonce.as_u64() + libraries.len() as u64); + self.executor.set_nonce( + self.sender, + sender_nonce.as_u64() + libraries.len() as u64, + )?; } ( diff --git a/evm/src/executor/backend/in_memory_db.rs b/evm/src/executor/backend/in_memory_db.rs index 89876a00871b..540d21a4dfbb 100644 --- a/evm/src/executor/backend/in_memory_db.rs +++ b/evm/src/executor/backend/in_memory_db.rs @@ -5,8 +5,10 @@ use ethers::{ types::Address, }; use hashbrown::HashMap as Map; -use revm::{db::DatabaseRef, Account, AccountInfo, Bytecode, Database, DatabaseCommit}; -use revm::db::{CacheDB, EmptyDB}; +use revm::{ + db::{CacheDB, DatabaseRef, EmptyDB}, + Account, AccountInfo, Bytecode, Database, DatabaseCommit, +}; use crate::executor::snapshot::Snapshots; @@ -48,6 +50,7 @@ impl DatabaseRef for MemDb { impl Database for MemDb { type Error = DatabaseError; + #[track_caller] fn basic(&mut self, address: Address) -> Result, Self::Error> { Database::basic(&mut self.inner, address) } @@ -71,10 +74,10 @@ impl DatabaseCommit for MemDb { } } - /// An empty database that always returns default values when queried. /// -/// This is just a simple wrapper for `revm::EmptyDB` but implements `DatabaseError` instead, this way we can unify all different `Database` impls +/// This is just a simple wrapper for `revm::EmptyDB` but implements `DatabaseError` instead, this +/// way we can unify all different `Database` impls #[derive(Debug, Default, Clone)] pub struct EmptyDBWrapper(EmptyDB); @@ -93,4 +96,4 @@ impl DatabaseRef for EmptyDBWrapper { fn block_hash(&self, number: U256) -> Result { Ok(self.0.block_hash(number)?) } -} \ No newline at end of file +} diff --git a/evm/src/executor/backend/mod.rs b/evm/src/executor/backend/mod.rs index 85d16e3cc61b..072b8936c3d9 100644 --- a/evm/src/executor/backend/mod.rs +++ b/evm/src/executor/backend/mod.rs @@ -574,9 +574,8 @@ impl Backend { let mut journaled_state = self.fork_init_journaled_state.clone(); for loaded_account in loaded_accounts.iter().copied() { trace!(?loaded_account, "replacing account on init"); - let fork_account = fork - .db - .basic(loaded_account)? + // TODO(mattsse) check + let fork_account = Database::basic(&mut fork.db, loaded_account)? .ok_or(DatabaseError::MissingAccount(loaded_account))?; let init_account = journaled_state.state.get_mut(&loaded_account).expect("exists; qed"); diff --git a/evm/src/executor/inspector/cheatcodes/mod.rs b/evm/src/executor/inspector/cheatcodes/mod.rs index d25a4249c6e7..57acdd18a086 100644 --- a/evm/src/executor/inspector/cheatcodes/mod.rs +++ b/evm/src/executor/inspector/cheatcodes/mod.rs @@ -47,7 +47,7 @@ mod fuzz; mod snapshot; /// Utility cheatcodes (`sign` etc.) pub mod util; -pub use util::{DEFAULT_CREATE2_DEPLOYER, MISSING_CREATE2_DEPLOYER}; +pub use util::DEFAULT_CREATE2_DEPLOYER; mod config; use crate::executor::backend::RevertDiagnostic; diff --git a/evm/src/executor/inspector/cheatcodes/util.rs b/evm/src/executor/inspector/cheatcodes/util.rs index 552ccfcded8a..14b9381e6ea6 100644 --- a/evm/src/executor/inspector/cheatcodes/util.rs +++ b/evm/src/executor/inspector/cheatcodes/util.rs @@ -26,8 +26,6 @@ const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/"; pub const DEFAULT_CREATE2_DEPLOYER: H160 = H160([ 78, 89, 180, 72, 71, 179, 121, 87, 133, 136, 146, 12, 167, 143, 191, 38, 192, 180, 149, 108, ]); -pub const MISSING_CREATE2_DEPLOYER: &str = - "CREATE2 Deployer not present on this chain. [0x4e59b44847b379578588920ca78fbf26c0b4956c]"; // keccak(Error(string)) pub static REVERT_PREFIX: [u8; 4] = [8, 195, 121, 160]; diff --git a/evm/src/executor/mod.rs b/evm/src/executor/mod.rs index ec033a4c0597..c3e15b073423 100644 --- a/evm/src/executor/mod.rs +++ b/evm/src/executor/mod.rs @@ -157,8 +157,7 @@ impl Executor { /// Set the balance of an account. pub fn set_balance(&mut self, address: Address, amount: U256) -> DatabaseResult<&mut Self> { trace!(?address, ?amount, "setting account balance"); - let mut account = - self.backend_mut().basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let mut account = self.backend_mut().basic(address)?.unwrap_or_default(); account.balance = amount; self.backend_mut().insert_account_info(address, account); @@ -167,13 +166,12 @@ impl Executor { /// Gets the balance of an account pub fn get_balance(&self, address: Address) -> DatabaseResult { - Ok(self.backend().basic(address)?.ok_or(DatabaseError::MissingAccount(address))?.balance) + Ok(self.backend().basic(address)?.map(|acc| acc.balance).unwrap_or_default()) } /// Set the nonce of an account. pub fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<&mut Self> { - let mut account = - self.backend_mut().basic(address)?.ok_or(DatabaseError::MissingAccount(address))?; + let mut account = self.backend_mut().basic(address)?.unwrap_or_default(); account.nonce = nonce; self.backend_mut().insert_account_info(address, account); @@ -561,10 +559,7 @@ impl Executor { ) -> Result { // Construct a new VM with the state changeset let mut backend = self.backend().clone_empty(); - backend.insert_account_info( - address, - self.backend().basic(address)?.ok_or(DatabaseError::MissingAccount(address))?, - ); + backend.insert_account_info(address, self.backend().basic(address)?.unwrap_or_default()); backend.commit(state_changeset); let executor = Executor::new(backend, self.env.clone(), self.inspector_config.clone(), self.gas_limit); diff --git a/forge/src/runner.rs b/forge/src/runner.rs index e47d5d203550..11a716f03550 100644 --- a/forge/src/runner.rs +++ b/forge/src/runner.rs @@ -76,11 +76,11 @@ impl<'a> ContractRunner<'a> { trace!(?setup, "Setting test contract"); // We max out their balance so that they can deploy and make calls. - self.executor.set_balance(self.sender, U256::MAX); - self.executor.set_balance(CALLER, U256::MAX); + self.executor.set_balance(self.sender, U256::MAX)?; + self.executor.set_balance(CALLER, U256::MAX)?; // We set the nonce of the deployer accounts to 1 to get the same addresses as DappTools - self.executor.set_nonce(self.sender, 1); + self.executor.set_nonce(self.sender, 1)?; // Deploy libraries let mut traces = Vec::with_capacity(self.predeploy_libs.len()); @@ -136,9 +136,9 @@ impl<'a> ContractRunner<'a> { // Now we set the contracts initial balance, and we also reset `self.sender`s and `CALLER`s // balance to the initial balance we want - self.executor.set_balance(address, self.initial_balance); - self.executor.set_balance(self.sender, self.initial_balance); - self.executor.set_balance(CALLER, self.initial_balance); + self.executor.set_balance(address, self.initial_balance)?; + self.executor.set_balance(self.sender, self.initial_balance)?; + self.executor.set_balance(CALLER, self.initial_balance)?; self.executor.deploy_create2_deployer()?; diff --git a/forge/tests/it/invariant.rs b/forge/tests/it/invariant.rs index 731b39d51cdd..83fc165e31e3 100644 --- a/forge/tests/it/invariant.rs +++ b/forge/tests/it/invariant.rs @@ -159,8 +159,8 @@ fn test_invariant_shrink() { .expect("`InvariantInnerContract` should have failed with a counterexample."); match counter { - CounterExample::Single(_) => assert!(false, "CounterExample should be a sequence."), + CounterExample::Single(_) => panic!("CounterExample should be a sequence."), // `fuzz_seed` at 100 makes this sequence shrinkable from 4 to 2. - CounterExample::Sequence(sequence) => assert!(sequence.len() == 2), + CounterExample::Sequence(sequence) => assert_eq!(sequence.len(), 2), }; } From db01698e370f6c912b14fd0b416dd05b380eef1c Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 2 Sep 2022 16:55:37 +0200 Subject: [PATCH 08/13] bump revm --- Cargo.lock | 18 ++++++++++-------- Cargo.toml | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be9eaa373601..6ba177247fc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4477,6 +4477,7 @@ dependencies = [ [[package]] name = "revm" version = "1.9.0" +source = "git+https://github.com/bluealloy/revm#8f4348dc93022cffb3730d9db5d3ab1aad77676a" dependencies = [ "arrayref", "auto_impl 1.0.1", @@ -4485,7 +4486,7 @@ dependencies = [ "hex", "num_enum", "primitive-types", - "revm_precompiles 1.1.0", + "revm_precompiles 1.1.0 (git+https://github.com/bluealloy/revm)", "rlp", "serde", "sha3", @@ -4494,15 +4495,14 @@ dependencies = [ [[package]] name = "revm_precompiles" version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af88e7e9feb30cc4ed64645f09b966e84a1f6be56551ce5f1691105def45705d" dependencies = [ "bytes", - "hashbrown 0.12.0", - "k256", "num", - "once_cell", "primitive-types", "ripemd", - "secp256k1 0.24.0", + "secp256k1 0.23.4", "sha2 0.10.3", "sha3", "substrate-bn", @@ -4511,14 +4511,16 @@ dependencies = [ [[package]] name = "revm_precompiles" version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af88e7e9feb30cc4ed64645f09b966e84a1f6be56551ce5f1691105def45705d" +source = "git+https://github.com/bluealloy/revm#8f4348dc93022cffb3730d9db5d3ab1aad77676a" dependencies = [ "bytes", + "hashbrown 0.12.0", + "k256", "num", + "once_cell", "primitive-types", "ripemd", - "secp256k1 0.23.4", + "secp256k1 0.24.0", "sha2 0.10.3", "sha3", "substrate-bn", diff --git a/Cargo.toml b/Cargo.toml index 125333d3fdb3..27860c5a770b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,5 +64,5 @@ debug = 0 #ethers-solc = { path = "../ethers-rs/ethers-solc" } [patch.crates-io] -revm = { path = "../revm/crates/revm" } -#revm = { git = "https://github.com/bluealloy/revm" } +#revm = { path = "../revm/crates/revm" } +revm = { git = "https://github.com/bluealloy/revm" } From 6cbc6bd853afb735fe7f40b85be08c0495716d74 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 2 Sep 2022 16:57:41 +0200 Subject: [PATCH 09/13] chore: rustfmt --- anvil/src/eth/backend/executor.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/anvil/src/eth/backend/executor.rs b/anvil/src/eth/backend/executor.rs index db4d212abb6e..65426e33eaab 100644 --- a/anvil/src/eth/backend/executor.rs +++ b/anvil/src/eth/backend/executor.rs @@ -227,9 +227,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator let sender = *transaction.pending_transaction.sender(); let account = match self.db.basic(sender).map(|acc| acc.unwrap_or_default()) { Ok(account) => account, - Err(err) => { - return { Some(TransactionExecutionOutcome::DatabaseError(transaction, err)) } - } + Err(err) => return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)), }; let env = self.env_for(&transaction.pending_transaction); // check that we comply with the block's gas limit From f00c954c104342a3859893199cdaed1dc5eefad1 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 2 Sep 2022 18:28:57 +0200 Subject: [PATCH 10/13] fix: always load missing accounts --- evm/Cargo.toml | 2 +- evm/src/executor/backend/in_memory_db.rs | 88 +++++++++++++++++++++++- evm/src/executor/fork/database.rs | 42 +++++++++++ 3 files changed, 128 insertions(+), 4 deletions(-) diff --git a/evm/Cargo.toml b/evm/Cargo.toml index 425d8563b24e..c14bde66c510 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -29,7 +29,7 @@ tracing-subscriber = "0.3" tracing-error = "0.2.0" # Threading/futures -tokio = { version = "1", features = ["time"] } +tokio = { version = "1", features = ["time", "macros"] } parking_lot = "0.12.0" futures = "0.3.21" once_cell = "1.13" diff --git a/evm/src/executor/backend/in_memory_db.rs b/evm/src/executor/backend/in_memory_db.rs index 540d21a4dfbb..706fd3f2d20a 100644 --- a/evm/src/executor/backend/in_memory_db.rs +++ b/evm/src/executor/backend/in_memory_db.rs @@ -1,5 +1,5 @@ //! The in memory DB -use crate::executor::backend::error::DatabaseError; +use crate::executor::backend::{error::DatabaseError, DatabaseResult}; use ethers::{ prelude::{H256, U256}, types::Address, @@ -23,6 +23,22 @@ pub struct MemDb { pub snapshots: Snapshots, } +impl MemDb { + /// the [`Database`](revm::Database) implementation for `CacheDB` manages an `AccountState` for the `DbAccount`, this will be set to `AccountState::NotExisting` if the account does not exist yet. This is because there's a distinction between "non-existing" and "empty", See + /// If an account is `NotExisting`, `Database(Ref)::basic` will always return `None` for the + /// requested `AccountInfo`. To prevent + /// + /// This will ensure that a missing account is never marked as `NotExisting` + fn ensure_loaded(&mut self, address: Address) -> DatabaseResult { + if let Some(acc) = DatabaseRef::basic(self, address)? { + Ok(acc) + } else { + self.inner.insert_account_info(address, Default::default()); + Ok(Default::default()) + } + } +} + impl Default for MemDb { fn default() -> Self { Self { inner: CacheDB::new(Default::default()), snapshots: Default::default() } @@ -50,9 +66,9 @@ impl DatabaseRef for MemDb { impl Database for MemDb { type Error = DatabaseError; - #[track_caller] + fn basic(&mut self, address: Address) -> Result, Self::Error> { - Database::basic(&mut self.inner, address) + self.ensure_loaded(address).map(Some) } fn code_by_hash(&mut self, code_hash: H256) -> Result { @@ -60,6 +76,7 @@ impl Database for MemDb { } fn storage(&mut self, address: Address, index: U256) -> Result { + self.ensure_loaded(address)?; Database::storage(&mut self.inner, address, index) } @@ -97,3 +114,68 @@ impl DatabaseRef for EmptyDBWrapper { Ok(self.0.block_hash(number)?) } } + +#[cfg(test)] +mod tests { + use super::*; + + /// Ensures the `Database(Ref)` implementation for `revm::CacheDB` works as expected + /// + /// Demonstrates how calling `Database::basic` works if an account does not exist + #[test] + fn cache_db_insert_basic_non_existing() { + let mut db = CacheDB::new(EmptyDBWrapper::default()); + let address = Address::random(); + + // call `basic` on a non-existing account + let info = Database::basic(&mut db, address).unwrap(); + assert!(info.is_none()); + let mut info = info.unwrap_or_default(); + info.balance = 500u64.into(); + + // insert the modified account info + db.insert_account_info(address, info); + + // when fetching again, the `AccountInfo` is still `None` because the state of the account is `AccountState::NotExisting`, See + let info = Database::basic(&mut db, address).unwrap(); + assert!(info.is_none()); + } + + /// Demonstrates how to insert a new account but not mark it as non-existing + #[test] + fn cache_db_insert_basic_default() { + let mut db = CacheDB::new(EmptyDBWrapper::default()); + let address = Address::random(); + + let info = DatabaseRef::basic(&db, address).unwrap(); + assert!(info.is_none()); + let mut info = info.unwrap_or_default(); + info.balance = 500u64.into(); + + // insert the modified account info + db.insert_account_info(address, info.clone()); + + let loaded = Database::basic(&mut db, address).unwrap(); + assert!(loaded.is_some()); + assert_eq!(loaded.unwrap(), info) + } + + /// Demonstrates that `Database::basic` for `MemDb` will always return the `AccountInfo` + #[test] + fn mem_db_insert_basic_default() { + let mut db = MemDb::default(); + let address = Address::random(); + + let info = Database::basic(&mut db, address).unwrap(); + assert!(info.is_some()); + let mut info = info.unwrap(); + info.balance = 500u64.into(); + + // insert the modified account info + db.inner.insert_account_info(address, info.clone()); + + let loaded = Database::basic(&mut db, address).unwrap(); + assert!(loaded.is_some()); + assert_eq!(loaded.unwrap(), info) + } +} diff --git a/evm/src/executor/fork/database.rs b/evm/src/executor/fork/database.rs index bf28f9a3ad91..4e51aa7e9550 100644 --- a/evm/src/executor/fork/database.rs +++ b/evm/src/executor/fork/database.rs @@ -152,6 +152,9 @@ impl Database for ForkedDatabase { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { + // Note: this will always return Some, since the `SharedBackend` will always load the + // account, this differs from `::basic`, See also + // [MemDb::ensure_loaded](crate::executor::backend::MemDb::ensure_loaded) Database::basic(&mut self.cache_db, address) } @@ -258,3 +261,42 @@ impl DatabaseRef for ForkDbSnapshot { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::executor::fork::BlockchainDbMeta; + use foundry_common::get_http_provider; + use std::collections::BTreeSet; + + /// Demonstrates that `Database::basic` for `ForkedDatabase` will always return the + /// `AccountInfo` + #[tokio::test(flavor = "multi_thread")] + async fn fork_db_insert_basic_default() { + let rpc = foundry_utils::rpc::next_http_rpc_endpoint(); + let provider = get_http_provider(rpc.clone()); + let meta = BlockchainDbMeta { + cfg_env: Default::default(), + block_env: Default::default(), + hosts: BTreeSet::from([rpc]), + }; + let db = BlockchainDb::new(meta, None); + + let backend = SharedBackend::spawn_backend(Arc::new(provider), db.clone(), None).await; + + let mut db = ForkedDatabase::new(backend, db); + let address = Address::random(); + + let info = Database::basic(&mut db, address).unwrap(); + assert!(info.is_some()); + let mut info = info.unwrap(); + info.balance = 500u64.into(); + + // insert the modified account info + db.database_mut().insert_account_info(address, info.clone()); + + let loaded = Database::basic(&mut db, address).unwrap(); + assert!(loaded.is_some()); + assert_eq!(loaded.unwrap(), info); + } +} From eeab142fa5289becc1a91fdcb71025426652785a Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 2 Sep 2022 18:51:41 +0200 Subject: [PATCH 11/13] fix: timestamp setup --- anvil/tests/it/anvil_api.rs | 1 - anvil/tests/it/fork.rs | 17 +++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/anvil/tests/it/anvil_api.rs b/anvil/tests/it/anvil_api.rs index a1a6c684555c..01d23fa8f2bf 100644 --- a/anvil/tests/it/anvil_api.rs +++ b/anvil/tests/it/anvil_api.rs @@ -55,7 +55,6 @@ async fn can_set_storage() { #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_account() { - crate::init_tracing(); let (api, handle) = spawn(NodeConfig::test()).await; let provider = handle.http_provider(); diff --git a/anvil/tests/it/fork.rs b/anvil/tests/it/fork.rs index cfb241c406f1..3825c62048a6 100644 --- a/anvil/tests/it/fork.rs +++ b/anvil/tests/it/fork.rs @@ -327,11 +327,11 @@ async fn can_reset_properly() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_timestamp() { + let start = std::time::Instant::now(); + let (api, handle) = spawn(fork_config()).await; let provider = handle.http_provider(); - let start = std::time::Instant::now(); - let block = provider.get_block(BLOCK_NUMBER).await.unwrap().unwrap(); assert_eq!(block.timestamp.as_u64(), BLOCK_TIMESTAMP); @@ -339,14 +339,16 @@ async fn test_fork_timestamp() { let from = accounts[0].address(); let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from); - let _tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + assert_eq!(tx.status, Some(1u64.into())); + + let elapsed = start.elapsed().as_secs(); let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); // ensure the diff between the new mined block and the original block is within the elapsed time - let elapsed = start.elapsed().as_secs() + 1; let diff = block.timestamp - BLOCK_TIMESTAMP; - assert!(diff <= elapsed.into()); + assert!(diff <= elapsed.into(), "diff={}, elapsed={}", diff, elapsed); let start = std::time::Instant::now(); // reset to check timestamp works after resetting @@ -579,12 +581,15 @@ async fn test_reset_fork_on_new_blocks() { let provider = Provider::try_from(endpoint).unwrap(); let mut stream = provider.watch_blocks().await.unwrap(); + // the http watcher may fetch multiple blocks at once, so we set a timeout here to offset edge + // cases where the stream immediately returns a block + tokio::time::sleep(Chain::Mainnet.average_blocktime_hint().unwrap()).await; stream.next().await.unwrap(); stream.next().await.unwrap(); let next_block = anvil_provider.get_block_number().await.unwrap(); - assert!(next_block > current_block) + assert!(next_block > current_block, "nextblock={} currentblock={}", next_block, current_block) } #[tokio::test(flavor = "multi_thread")] From 78d7ec54646b0d85e9cb42f2d6546ed05b5b6b0a Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 2 Sep 2022 19:41:29 +0200 Subject: [PATCH 12/13] make tests work again --- evm/src/executor/backend/in_memory_db.rs | 47 +++++++++++------------- evm/src/executor/backend/mod.rs | 15 ++++---- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/evm/src/executor/backend/in_memory_db.rs b/evm/src/executor/backend/in_memory_db.rs index 706fd3f2d20a..7baa861b4cc0 100644 --- a/evm/src/executor/backend/in_memory_db.rs +++ b/evm/src/executor/backend/in_memory_db.rs @@ -1,5 +1,5 @@ //! The in memory DB -use crate::executor::backend::{error::DatabaseError, DatabaseResult}; +use crate::executor::backend::error::DatabaseError; use ethers::{ prelude::{H256, U256}, types::Address, @@ -12,31 +12,18 @@ use revm::{ use crate::executor::snapshot::Snapshots; -pub type InMemoryDB = CacheDB; +/// Type alias for an in memory database +/// +/// See `EmptyDBWrapper` +pub type FoundryEvmInMemoryDB = CacheDB; /// In memory Database for anvil /// /// This acts like a wrapper type for [InMemoryDB] but is capable of applying snapshots #[derive(Debug)] pub struct MemDb { - pub inner: InMemoryDB, - pub snapshots: Snapshots, -} - -impl MemDb { - /// the [`Database`](revm::Database) implementation for `CacheDB` manages an `AccountState` for the `DbAccount`, this will be set to `AccountState::NotExisting` if the account does not exist yet. This is because there's a distinction between "non-existing" and "empty", See - /// If an account is `NotExisting`, `Database(Ref)::basic` will always return `None` for the - /// requested `AccountInfo`. To prevent - /// - /// This will ensure that a missing account is never marked as `NotExisting` - fn ensure_loaded(&mut self, address: Address) -> DatabaseResult { - if let Some(acc) = DatabaseRef::basic(self, address)? { - Ok(acc) - } else { - self.inner.insert_account_info(address, Default::default()); - Ok(Default::default()) - } - } + pub inner: FoundryEvmInMemoryDB, + pub snapshots: Snapshots, } impl Default for MemDb { @@ -68,7 +55,8 @@ impl Database for MemDb { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { - self.ensure_loaded(address).map(Some) + // Note: this will always return `Some(AccountInfo)`, See `EmptyDBWrapper` + Database::basic(&mut self.inner, address) } fn code_by_hash(&mut self, code_hash: H256) -> Result { @@ -76,7 +64,6 @@ impl Database for MemDb { } fn storage(&mut self, address: Address, index: U256) -> Result { - self.ensure_loaded(address)?; Database::storage(&mut self.inner, address, index) } @@ -95,13 +82,23 @@ impl DatabaseCommit for MemDb { /// /// This is just a simple wrapper for `revm::EmptyDB` but implements `DatabaseError` instead, this /// way we can unify all different `Database` impls +/// +/// This will also _always_ return `Some(AccountInfo)`: +/// +/// The [`Database`](revm::Database) implementation for `CacheDB` manages an `AccountState` for the `DbAccount`, this will be set to `AccountState::NotExisting` if the account does not exist yet. This is because there's a distinction between "non-existing" and "empty", See +/// If an account is `NotExisting`, `Database(Ref)::basic` will always return `None` for the +/// requested `AccountInfo`. To prevent +/// +/// This will ensure that a missing account is never marked as `NotExisting` #[derive(Debug, Default, Clone)] pub struct EmptyDBWrapper(EmptyDB); impl DatabaseRef for EmptyDBWrapper { type Error = DatabaseError; + fn basic(&self, address: Address) -> Result, Self::Error> { - Ok(self.0.basic(address)?) + // Note: this will always return `Some(AccountInfo)`, for the reason explained above + Ok(Some(self.0.basic(address)?.unwrap_or_default())) } fn code_by_hash(&self, code_hash: H256) -> Result { Ok(self.0.code_by_hash(code_hash)?) @@ -124,7 +121,7 @@ mod tests { /// Demonstrates how calling `Database::basic` works if an account does not exist #[test] fn cache_db_insert_basic_non_existing() { - let mut db = CacheDB::new(EmptyDBWrapper::default()); + let mut db = CacheDB::new(EmptyDB::default()); let address = Address::random(); // call `basic` on a non-existing account @@ -144,7 +141,7 @@ mod tests { /// Demonstrates how to insert a new account but not mark it as non-existing #[test] fn cache_db_insert_basic_default() { - let mut db = CacheDB::new(EmptyDBWrapper::default()); + let mut db = CacheDB::new(EmptyDB::default()); let address = Address::random(); let info = DatabaseRef::basic(&db, address).unwrap(); diff --git a/evm/src/executor/backend/mod.rs b/evm/src/executor/backend/mod.rs index 072b8936c3d9..417bc4420f0e 100644 --- a/evm/src/executor/backend/mod.rs +++ b/evm/src/executor/backend/mod.rs @@ -16,8 +16,8 @@ pub use in_memory_db::MemDb; use revm::{ db::{CacheDB, DatabaseRef}, precompiles::Precompiles, - Account, AccountInfo, Bytecode, Database, DatabaseCommit, Env, ExecutionResult, InMemoryDB, - Inspector, JournaledState, Log, SpecId, TransactTo, KECCAK_EMPTY, + Account, AccountInfo, Bytecode, Database, DatabaseCommit, Env, ExecutionResult, Inspector, + JournaledState, Log, SpecId, TransactTo, KECCAK_EMPTY, }; use std::collections::{HashMap, HashSet}; use tracing::{trace, warn}; @@ -30,6 +30,7 @@ use crate::executor::inspector::cheatcodes::util::with_journaled_account; pub use diagnostic::RevertDiagnostic; pub mod error; +use crate::executor::backend::in_memory_db::FoundryEvmInMemoryDB; pub use error::{DatabaseError, DatabaseResult}; mod in_memory_db; @@ -263,7 +264,7 @@ pub struct Backend { /// The access point for managing forks forks: MultiFork, // The default in memory db - mem_db: InMemoryDB, + mem_db: FoundryEvmInMemoryDB, /// The journaled_state to use to initialize new forks with /// /// The way [`revm::JournaledState`] works is, that it holds the "hot" accounts loaded from the @@ -310,7 +311,7 @@ impl Backend { let mut backend = Self { forks, - mem_db: InMemoryDB::default(), + mem_db: CacheDB::new(Default::default()), fork_init_journaled_state: inner.new_journaled_state(), active_fork_ids: None, inner, @@ -336,7 +337,7 @@ impl Backend { pub fn clone_empty(&self) -> Self { Self { forks: self.forks.clone(), - mem_db: InMemoryDB::default(), + mem_db: CacheDB::new(Default::default()), fork_init_journaled_state: self.inner.new_journaled_state(), active_fork_ids: None, inner: Default::default(), @@ -459,7 +460,7 @@ impl Backend { } /// Returns the memory db used if not in forking mode - pub fn mem_db(&self) -> &InMemoryDB { + pub fn mem_db(&self) -> &FoundryEvmInMemoryDB { &self.mem_db } @@ -987,7 +988,7 @@ impl Database for Backend { #[derive(Debug, Clone)] pub enum BackendDatabaseSnapshot { /// Simple in-memory [revm::Database] - InMemory(InMemoryDB), + InMemory(FoundryEvmInMemoryDB), /// Contains the entire forking mode database Forked(LocalForkId, ForkId, ForkLookupIndex, Box), } From c30e99333c611e31385870b22cabdcbc0c620a17 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 5 Sep 2022 17:16:45 +0200 Subject: [PATCH 13/13] bump revm --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e9521109fb3..de333852a475 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4466,7 +4466,7 @@ dependencies = [ [[package]] name = "revm" version = "1.9.0" -source = "git+https://github.com/bluealloy/revm#8f4348dc93022cffb3730d9db5d3ab1aad77676a" +source = "git+https://github.com/bluealloy/revm#da041d513db70661904e09c55cdc471168ec3df6" dependencies = [ "arrayref", "auto_impl 1.0.1", @@ -4500,7 +4500,7 @@ dependencies = [ [[package]] name = "revm_precompiles" version = "1.1.0" -source = "git+https://github.com/bluealloy/revm#8f4348dc93022cffb3730d9db5d3ab1aad77676a" +source = "git+https://github.com/bluealloy/revm#da041d513db70661904e09c55cdc471168ec3df6" dependencies = [ "bytes", "hashbrown 0.12.0",