Skip to content

Commit

Permalink
build: use new revm with analysis cache (foundry-rs#2527)
Browse files Browse the repository at this point in the history
* build: use new revm with analysis cache

* refactor: use checked bytecode

See bluealloy/revm#121 (comment)

* build: use git revm

* build: use revm 1.8

* test: fix test

* fix: correct bytecode getters/setters

Whenever we output the bytecode of an account, whether to
a file or in a response, we need to return the *original*
bytecode using `Bytecode::len`, otherwise the bytecode
will differ depending on whether the bytecode has been
checked, analyzed or not.

* refactor: use `Bytecode::hash`

* fix: get original account code for traces

* refactor: remove unsafe code
  • Loading branch information
onbjerg authored and iFrostizz committed Nov 9, 2022
1 parent dc18a2d commit ac0826b
Show file tree
Hide file tree
Showing 21 changed files with 101 additions and 79 deletions.
23 changes: 21 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions anvil/src/eth/backend/db.rs
Expand Up @@ -9,7 +9,7 @@ use ethers::{
use forge::revm::KECCAK_EMPTY;
use foundry_evm::{
executor::DatabaseRef,
revm::{db::CacheDB, Database, DatabaseCommit, InMemoryDB},
revm::{db::CacheDB, Bytecode, Database, DatabaseCommit, InMemoryDB},
HashMap,
};

Expand Down Expand Up @@ -43,7 +43,7 @@ pub trait Db: DatabaseRef + Database + DatabaseCommit + Send + Sync {
H256::from_slice(&keccak256(code.as_ref())[..])
};
info.code_hash = code_hash;
info.code = Some(code.to_vec().into());
info.code = Some(Bytecode::new_raw(code.0).to_checked());
self.insert_account(address, info);
}

Expand Down Expand Up @@ -123,7 +123,7 @@ impl DatabaseRef for StateDb {
self.0.basic(address)
}

fn code_by_hash(&self, code_hash: H256) -> bytes::Bytes {
fn code_by_hash(&self, code_hash: H256) -> Bytecode {
self.0.code_by_hash(code_hash)
}

Expand Down
29 changes: 18 additions & 11 deletions anvil/src/eth/backend/mem/in_memory_db.rs
Expand Up @@ -11,7 +11,7 @@ use tracing::{trace, warn};
// reexport for convenience
pub use foundry_evm::executor::{backend::MemDb, DatabaseRef};

use forge::revm::KECCAK_EMPTY;
use forge::revm::{Bytecode, KECCAK_EMPTY};

impl Db for MemDb {
fn insert_account(&mut self, address: Address, account: AccountInfo) {
Expand All @@ -29,16 +29,17 @@ impl Db for MemDb {
.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();
(
k,
SerializableAccountRecord {
nonce: v.info.nonce,
balance: v.info.balance,
code: v
.info
.code
.unwrap_or_else(|| self.inner.code_by_hash(v.info.code_hash))
.into(),
code: code.bytes()[..code.len()].to_vec().into(),
storage: v.storage.into_iter().collect(),
},
)
Expand All @@ -57,7 +58,11 @@ impl Db for MemDb {
AccountInfo {
balance: account.balance,
code_hash: KECCAK_EMPTY, // will be set automatically
code: if account.code.0.is_empty() { None } else { Some(account.code.0) },
code: if account.code.0.is_empty() {
None
} else {
Some(Bytecode::new_raw(account.code.0).to_checked())
},
// use max nonce in case account is imported multiple times with difference
// nonces to prevent collisions
nonce: std::cmp::max(
Expand Down Expand Up @@ -110,7 +115,7 @@ mod tests {
Address,
};
use bytes::Bytes;
use forge::revm::KECCAK_EMPTY;
use forge::revm::{Bytecode, KECCAK_EMPTY};
use foundry_evm::{
executor::{backend::MemDb, DatabaseRef},
HashMap,
Expand All @@ -126,7 +131,8 @@ mod tests {

let mut dump_db = MemDb::default();

let contract_code: Bytes = Bytes::from("fake contract code");
let contract_code: Bytecode =
Bytecode::new_raw(Bytes::from("fake contract code")).to_checked();

dump_db.insert_account(
test_addr,
Expand Down Expand Up @@ -163,7 +169,8 @@ mod tests {
let test_addr2: Address =
Address::from_str("0x70997970c51812dc3a010c7d01b50e0d17dc79c8").unwrap();

let contract_code: Bytes = Bytes::from("fake contract code");
let contract_code: Bytecode =
Bytecode::new_raw(Bytes::from("fake contract code")).to_checked();

let mut db = MemDb::default();

Expand Down Expand Up @@ -199,7 +206,7 @@ mod tests {
test_addr,
SerializableAccountRecord {
balance: 100100.into(),
code: contract_code.clone().into(),
code: contract_code.bytes()[..contract_code.len()].to_vec().into(),
nonce: 100,
storage: new_storage,
},
Expand Down
7 changes: 3 additions & 4 deletions anvil/src/eth/backend/mem/mod.rs
@@ -1,5 +1,4 @@
//! In memory blockchain backend

use crate::{
eth::{
backend::{
Expand Down Expand Up @@ -1096,11 +1095,11 @@ impl Backend {
trace!(target: "backend", "get code for {:?}", address);
let account = db.basic(address);
let code = if let Some(code) = account.code {
code.into()
code
} else {
db.code_by_hash(account.code_hash).into()
db.code_by_hash(account.code_hash)
};
Ok(code)
Ok(code.bytes()[..code.len()].to_vec().into())
})
}

Expand Down
2 changes: 1 addition & 1 deletion evm/Cargo.toml
Expand Up @@ -36,7 +36,7 @@ once_cell = "1.13"
# EVM
bytes = "1.1.0"
hashbrown = { version = "0.12", features = ["serde"] }
revm = { version = "1.7", default-features = false, features = ["std", "k256", "with-serde", "memory_limit"] }
revm = { version = "1.8", default-features = false, features = ["std", "k256", "with-serde", "memory_limit"] }

# Fuzzer
proptest = "1.0.0"
Expand Down
2 changes: 1 addition & 1 deletion evm/src/coverage/visitor.rs
Expand Up @@ -481,7 +481,7 @@ impl Visitor {

// We found a push, so we do some PC -> IC translation accounting, but we also check if
// this push is coupled with the JUMPI we are interested in.
if opcode_infos[op as usize].is_push {
if opcode_infos[op as usize].is_push() {
let element = if let Some(element) = source_map.get(pc - cumulative_push_size) {
element
} else {
Expand Down
9 changes: 4 additions & 5 deletions evm/src/executor/backend/fuzz.rs
Expand Up @@ -5,12 +5,11 @@ use crate::{
},
Address,
};
use bytes::Bytes;
use ethers::prelude::{H160, H256, U256};
use hashbrown::HashMap as Map;
use revm::{
db::DatabaseRef, Account, AccountInfo, Database, Env, Inspector, Log, Return, SubRoutine,
TransactOut,
db::DatabaseRef, Account, AccountInfo, Bytecode, Database, Env, Inspector, Log, Return,
SubRoutine, TransactOut,
};
use std::borrow::Cow;
use tracing::trace;
Expand Down Expand Up @@ -142,7 +141,7 @@ impl<'a> DatabaseRef for FuzzBackendWrapper<'a> {
DatabaseRef::basic(self.backend.as_ref(), address)
}

fn code_by_hash(&self, code_hash: H256) -> Bytes {
fn code_by_hash(&self, code_hash: H256) -> Bytecode {
DatabaseRef::code_by_hash(self.backend.as_ref(), code_hash)
}

Expand All @@ -159,7 +158,7 @@ impl<'a> Database for FuzzBackendWrapper<'a> {
fn basic(&mut self, address: H160) -> AccountInfo {
DatabaseRef::basic(self, address)
}
fn code_by_hash(&mut self, code_hash: H256) -> Bytes {
fn code_by_hash(&mut self, code_hash: H256) -> Bytecode {
DatabaseRef::code_by_hash(self, code_hash)
}
fn storage(&mut self, address: H160, index: U256) -> U256 {
Expand Down
8 changes: 3 additions & 5 deletions evm/src/executor/backend/in_memory_db.rs
@@ -1,9 +1,7 @@
//! The in memory DB

use bytes::Bytes;
use ethers::prelude::{H160, H256, U256};
use hashbrown::HashMap as Map;
use revm::{db::DatabaseRef, Account, AccountInfo, Database, DatabaseCommit, InMemoryDB};
use revm::{db::DatabaseRef, Account, AccountInfo, Bytecode, Database, DatabaseCommit, InMemoryDB};

use crate::executor::snapshot::Snapshots;

Expand All @@ -27,7 +25,7 @@ impl DatabaseRef for MemDb {
DatabaseRef::basic(&self.inner, address)
}

fn code_by_hash(&self, code_hash: H256) -> Bytes {
fn code_by_hash(&self, code_hash: H256) -> Bytecode {
DatabaseRef::code_by_hash(&self.inner, code_hash)
}

Expand All @@ -45,7 +43,7 @@ impl Database for MemDb {
Database::basic(&mut self.inner, address)
}

fn code_by_hash(&mut self, code_hash: H256) -> Bytes {
fn code_by_hash(&mut self, code_hash: H256) -> Bytecode {
Database::code_by_hash(&mut self.inner, code_hash)
}

Expand Down
11 changes: 5 additions & 6 deletions evm/src/executor/backend/mod.rs
Expand Up @@ -2,16 +2,15 @@ use crate::executor::{
fork::{CreateFork, ForkId, MultiFork, SharedBackend},
snapshot::Snapshots,
};
use bytes::Bytes;
use ethers::{
prelude::{H160, H256, U256},
types::Address,
};
use hashbrown::HashMap as Map;
use revm::{
db::{CacheDB, DatabaseRef},
Account, AccountInfo, Database, DatabaseCommit, Env, InMemoryDB, Inspector, Log, Return,
SubRoutine, TransactOut, TransactTo, KECCAK_EMPTY,
Account, AccountInfo, Bytecode, Database, DatabaseCommit, Env, InMemoryDB, Inspector, Log,
Return, SubRoutine, TransactOut, TransactTo, KECCAK_EMPTY,
};
use std::collections::{HashMap, HashSet};
use tracing::{trace, warn};
Expand Down Expand Up @@ -702,7 +701,7 @@ impl DatabaseRef for Backend {
}
}

fn code_by_hash(&self, code_hash: H256) -> Bytes {
fn code_by_hash(&self, code_hash: H256) -> Bytecode {
if let Some(db) = self.active_fork_db() {
db.code_by_hash(code_hash)
} else {
Expand Down Expand Up @@ -736,7 +735,7 @@ impl<'a> DatabaseRef for &'a mut Backend {
}
}

fn code_by_hash(&self, code_hash: H256) -> Bytes {
fn code_by_hash(&self, code_hash: H256) -> Bytecode {
if let Some(db) = self.active_fork_db() {
DatabaseRef::code_by_hash(db, code_hash)
} else {
Expand Down Expand Up @@ -780,7 +779,7 @@ impl Database for Backend {
}
}

fn code_by_hash(&mut self, code_hash: H256) -> Bytes {
fn code_by_hash(&mut self, code_hash: H256) -> Bytecode {
if let Some(db) = self.active_fork_db_mut() {
db.code_by_hash(code_hash)
} else {
Expand Down
13 changes: 8 additions & 5 deletions evm/src/executor/fork/backend.rs
@@ -1,6 +1,4 @@
//! Smart caching and deduplication of requests when using a forking provider
use revm::{db::DatabaseRef, AccountInfo, KECCAK_EMPTY};

use crate::executor::fork::{cache::FlushJsonBlockCacheDB, BlockchainDb};
use ethers::{
core::abi::ethereum_types::BigEndianHash,
Expand All @@ -14,6 +12,7 @@ use futures::{
task::{Context, Poll},
Future, FutureExt,
};
use revm::{db::DatabaseRef, AccountInfo, Bytecode, KECCAK_EMPTY};
use std::{
collections::{hash_map::Entry, HashMap, VecDeque},
pin::Pin,
Expand Down Expand Up @@ -280,8 +279,12 @@ where
};

// update the cache
let acc =
AccountInfo { nonce: nonce.as_u64(), balance, code, code_hash };
let acc = AccountInfo {
nonce: nonce.as_u64(),
balance,
code: code.map(|bytes| Bytecode::new_raw(bytes).to_checked()),
code_hash,
};
pin.db.accounts().write().insert(addr, acc.clone());

// notify all listeners
Expand Down Expand Up @@ -495,7 +498,7 @@ impl DatabaseRef for SharedBackend {
})
}

fn code_by_hash(&self, _address: H256) -> bytes::Bytes {
fn code_by_hash(&self, _address: H256) -> Bytecode {
panic!("Should not be called. Code is already loaded.")
}

Expand Down
13 changes: 3 additions & 10 deletions evm/src/executor/fork/cache.rs
@@ -1,8 +1,5 @@
//! Cache related abstraction
use ethers::{
types::{Address, H256, U256},
utils::keccak256,
};
use ethers::types::{Address, H256, U256};
use parking_lot::RwLock;
use revm::{Account, AccountInfo, DatabaseCommit, Filth, KECCAK_EMPTY};
use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
Expand Down Expand Up @@ -197,12 +194,8 @@ impl MemDb {
storage.remove(&add);
} else {
// insert account
if let Some(code_hash) = acc
.info
.code
.as_ref()
.filter(|code| !code.is_empty())
.map(|code| H256::from_slice(&keccak256(code)))
if let Some(code_hash) =
acc.info.code.as_ref().filter(|code| !code.is_empty()).map(|code| code.hash())
{
acc.info.code_hash = code_hash;
} else if acc.info.code_hash.is_zero() {
Expand Down

0 comments on commit ac0826b

Please sign in to comment.