Skip to content

Commit

Permalink
fix: revm breaking changes (#2967)
Browse files Browse the repository at this point in the history
* fix: revm breaking changes

* more refactoring

* migrate revm

* update map types

* refactor: make anvil compile again

* unify error types

* fix more breaking changes

* bump revm

* chore: rustfmt

* fix: always load missing accounts

* fix: timestamp setup

* make tests work again

* bump revm
  • Loading branch information
mattsse authored Sep 5, 2022
1 parent 8823435 commit 6262fbe
Show file tree
Hide file tree
Showing 36 changed files with 971 additions and 506 deletions.
8 changes: 5 additions & 3 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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/onbjerg/revm", branch = "onbjerg/bytecode-hash" }
#revm = { path = "../revm/crates/revm" }
revm = { git = "https://github.com/bluealloy/revm" }
23 changes: 13 additions & 10 deletions anvil/src/eth/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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?)
Expand Down Expand Up @@ -1238,7 +1241,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(())
}

Expand All @@ -1247,7 +1250,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(())
}

Expand Down Expand Up @@ -1339,7 +1342,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(())
}

Expand All @@ -1348,7 +1351,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(())
}

Expand All @@ -1357,7 +1360,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(())
}

Expand All @@ -1371,7 +1374,7 @@ impl EthApi {
val: H256,
) -> Result<bool> {
node_info!("anvil_setStorageAt");
self.backend.set_storage_at(address, slot, val).await;
self.backend.set_storage_at(address, slot, val).await?;
Ok(true)
}

Expand Down Expand Up @@ -1723,7 +1726,7 @@ impl EthApi {
block_env: BlockEnv,
) -> Result<U256>
where
D: DatabaseRef,
D: DatabaseRef<Error = DatabaseError>,
{
// call takes at least this amount
const MIN_GAS: U256 = U256([21_000, 0, 0, 0]);
Expand Down
66 changes: 39 additions & 27 deletions anvil/src/eth/backend/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -21,14 +21,14 @@ use foundry_evm::{
};
use hash_db::HashDB;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fmt;

/// Type alias for the `HashDB` representation of the Database
pub type AsHashDB = Box<dyn HashDB<KeccakHasher, Vec<u8>>>;

/// Helper trait get access to the data in `HashDb` form
#[auto_impl::auto_impl(Box)]
pub trait MaybeHashDatabase: DatabaseRef {
pub trait MaybeHashDatabase: DatabaseRef<Error = DatabaseError> {
/// Return the DB as read-only hashdb and the root key
fn maybe_as_hash_db(&self) -> Option<(AsHashDB, H256)> {
None
Expand All @@ -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<Error = DatabaseError>,
{
fn maybe_as_hash_db(&self) -> Option<(AsHashDB, H256)> {
T::maybe_as_hash_db(self)
Expand All @@ -64,27 +64,37 @@ where
}

/// This bundles all required revm traits
pub trait Db: DatabaseRef + Database + DatabaseCommit + MaybeHashDatabase + Send + Sync {
pub trait Db:
DatabaseRef<Error = DatabaseError>
+ Database<Error = DatabaseError>
+ DatabaseCommit
+ MaybeHashDatabase
+ fmt::Debug
+ 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)?.unwrap_or_default();
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)?.unwrap_or_default();
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)?.unwrap_or_default();
let code_hash = if code.as_ref().is_empty() {
KECCAK_EMPTY
} else {
Expand All @@ -93,19 +103,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<SerializableState>;
fn dump_state(&self) -> DatabaseResult<Option<SerializableState>>;

/// 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<bool>;

/// Creates a new snapshot
fn snapshot(&mut self) -> U256;
Expand All @@ -128,25 +139,25 @@ 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<T: DatabaseRef + Send + Sync + Clone> Db for CacheDB<T> {
impl<T: DatabaseRef<Error = DatabaseError> + Send + Sync + Clone + fmt::Debug> Db for CacheDB<T> {
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)
}

fn insert_block_hash(&mut self, number: U256, hash: H256) {
self.block_hashes.insert(number, hash);
}

fn dump_state(&self) -> Option<SerializableState> {
None
fn dump_state(&self) -> DatabaseResult<Option<SerializableState>> {
Ok(None)
}

fn load_state(&mut self, _buf: SerializableState) -> bool {
false
fn load_state(&mut self, _buf: SerializableState) -> DatabaseResult<bool> {
Ok(false)
}

fn snapshot(&mut self) -> U256 {
Expand All @@ -162,14 +173,14 @@ impl<T: DatabaseRef + Send + Sync + Clone> Db for CacheDB<T> {
}
}

impl<T: DatabaseRef> MaybeHashDatabase for CacheDB<T> {
impl<T: DatabaseRef<Error = DatabaseError>> MaybeHashDatabase for CacheDB<T> {
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));
Expand Down Expand Up @@ -213,19 +224,20 @@ impl StateDb {
}

impl DatabaseRef for StateDb {
fn basic(&self, address: H160) -> AccountInfo {
type Error = DatabaseError;
fn basic(&self, address: H160) -> DatabaseResult<Option<AccountInfo>> {
self.0.basic(address)
}

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

fn storage(&self, address: H160, index: U256) -> U256 {
fn storage(&self, address: H160, index: U256) -> DatabaseResult<U256> {
self.0.storage(address, index)
}

fn block_hash(&self, number: U256) -> H256 {
fn block_hash(&self, number: U256) -> DatabaseResult<H256> {
self.0.block_hash(number)
}
}
Expand Down
15 changes: 14 additions & 1 deletion anvil/src/eth/backend/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, CallTraceArena},
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -206,6 +213,8 @@ pub enum TransactionExecutionOutcome {
Invalid(Arc<PoolTransaction>, InvalidTransactionError),
/// Execution skipped because could exceed gas limit
Exhausted(Arc<PoolTransaction>),
/// When an error occurred during execution
DatabaseError(Arc<PoolTransaction>, DatabaseError),
}

impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator
Expand All @@ -215,7 +224,11 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator

fn next(&mut self) -> Option<Self::Item> {
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).map(|acc| acc.unwrap_or_default()) {
Ok(account) => account,
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));
Expand Down
10 changes: 7 additions & 3 deletions anvil/src/eth/backend/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -47,17 +47,21 @@ 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);
// insert all accounts
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(())
}
}
Loading

0 comments on commit 6262fbe

Please sign in to comment.