Skip to content

Commit

Permalink
fix(anvil): Fix AccessList generation
Browse files Browse the repository at this point in the history
  • Loading branch information
ngotchac committed Aug 18, 2022
1 parent 93ee742 commit 54818ae
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 75 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions anvil/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ ethers = { git = "https://github.com/gakonst/ethers-rs", features = ["ws"] }
trie-db = { version = "0.23" }
hash-db = { version = "0.15" }
memory-db = { version = "0.29" }
revm_precompiles = "1.1.0"

# axum related
axum = { version = "0.5", features = ["ws"] }
Expand Down
66 changes: 30 additions & 36 deletions anvil/src/eth/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use crate::{
},
sign,
sign::Signer,
util::PRECOMPILES,
},
filter::{EthFilter, Filters, LogsFilter},
mem::transaction_build,
Expand All @@ -42,7 +41,7 @@ use ethers::{
providers::ProviderError,
types::{
transaction::{
eip2930::{AccessList, AccessListItem, AccessListWithGasUsed},
eip2930::{AccessList, AccessListWithGasUsed},
eip712::TypedData,
},
Address, Block, BlockId, BlockNumber, Bytes, FeeHistory, Filter, FilteredParams, Log,
Expand All @@ -53,10 +52,7 @@ use ethers::{
};
use forge::{executor::DatabaseRef, revm::BlockEnv};
use foundry_common::ProviderBuilder;
use foundry_evm::{
revm::{return_ok, return_revert, Return},
utils::u256_to_h256_be,
};
use foundry_evm::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 @@ -790,37 +786,35 @@ impl EthApi {
}
}

// ensure tx succeeds
let (exit, out, _, mut state) =
self.backend.call(request.clone(), FeeDetails::zero(), Some(block_request)).await?;

ensure_return_ok(exit, &out)?;

// cleanup state map
if let Some(from) = request.from {
// remove the sender
let _ = state.remove(&from);
}

// remove all precompiles
for precompile in PRECOMPILES.iter() {
let _ = state.remove(precompile);
}

let items = state
.into_iter()
.map(|(address, acc)| {
let storage_keys = acc.storage.into_keys().map(u256_to_h256_be).collect();
AccessListItem { address, storage_keys }
self.backend
.with_database_at(Some(block_request), |state, block_env| {
let (exit, out, _, access_list) = self.backend.build_access_list_with_state(
&state,
request.clone(),
FeeDetails::zero(),
block_env.clone(),
)?;
ensure_return_ok(exit, &out)?;

// execute again but with access list set
request.access_list = Some(access_list.0);

let (exit, out, gas_used, _) = self.backend.call_with_state(
&state,
request.clone(),
FeeDetails::zero(),
block_env,
)?;
ensure_return_ok(exit, &out)?;

let access_list =
request.access_list.take().expect("the access_list was set above");
Ok(AccessListWithGasUsed {
access_list: AccessList(access_list),
gas_used: gas_used.into(),
})
})
.collect::<Vec<_>>();

// execute again but with access list set
request.access_list = Some(items.clone());

let gas_used = self.do_estimate_gas(request, block_number).await?;

Ok(AccessListWithGasUsed { access_list: AccessList(items), gas_used })
.await?
}

/// Estimate gas needed for execution of given contract.
Expand Down
74 changes: 61 additions & 13 deletions anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::{
fees::{FeeDetails, FeeManager},
macros::node_info,
pool::transactions::PoolTransaction,
util::get_precompiles_for,
},
mem::{
in_memory_db::MemDb,
Expand All @@ -42,17 +43,20 @@ use ethers::{
abi::ethereum_types::BigEndianHash,
prelude::{BlockNumber, TxHash, H256, U256, U64},
types::{
Address, Block as EthersBlock, BlockId, Bytes, Filter, FilteredParams, Log, Trace,
Transaction, TransactionReceipt,
transaction::eip2930::AccessList, Address, Block as EthersBlock, BlockId, Bytes, Filter,
FilteredParams, Log, Trace, Transaction, TransactionReceipt,
},
utils::{keccak256, rlp},
utils::{get_contract_address, keccak256, rlp},
};
use forge::{
executor::inspector::AccessListTracer,
revm::{return_ok, return_revert, BlockEnv, Return},
};
use forge::revm::{return_ok, return_revert, BlockEnv};
use foundry_evm::{
decode::decode_revert,
revm,
revm::{
db::CacheDB, Account, CreateScheme, Env, Return, SpecId, TransactOut, TransactTo, TxEnv,
db::CacheDB, Account, CreateScheme, Env, SpecId, TransactOut, TransactTo, TxEnv,
KECCAK_EMPTY,
},
utils::u256_to_h256_be,
Expand Down Expand Up @@ -280,6 +284,10 @@ impl Backend {
self.fork.is_some()
}

pub fn precompiles(&self) -> Vec<Address> {
get_precompiles_for(self.env().read().cfg.spec_id)
}

/// Resets the fork to a fresh state
pub async fn reset_fork(&self, forking: Forking) -> Result<(), BlockchainError> {
if let Some(fork) = self.get_fork() {
Expand Down Expand Up @@ -751,16 +759,12 @@ impl Backend {
}).await?
}

pub fn call_with_state<D>(
fn build_call_env(
&self,
state: D,
request: EthTransactionRequest,
fee_details: FeeDetails,
block_env: BlockEnv,
) -> Result<(Return, TransactOut, u64, State), BlockchainError>
where
D: DatabaseRef,
{
) -> Result<Env, BlockchainError> {
let EthTransactionRequest { from, to, gas, value, data, nonce, access_list, .. } = request;

let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = fee_details;
Expand All @@ -774,9 +778,10 @@ impl Backend {
}

let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| self.gas_price());
let caller = from.unwrap_or_default();

env.tx = TxEnv {
caller: from.unwrap_or_default(),
caller,
gas_limit: gas_limit.as_u64(),
gas_price,
gas_priority_fee: max_priority_fee_per_gas,
Expand All @@ -790,16 +795,59 @@ impl Backend {
nonce: nonce.map(|n| n.as_u64()),
access_list: to_access_list(access_list.unwrap_or_default()),
};
Ok(env)
}

pub fn call_with_state<D>(
&self,
state: D,
request: EthTransactionRequest,
fee_details: FeeDetails,
block_env: BlockEnv,
) -> Result<(Return, TransactOut, u64, State), BlockchainError>
where
D: DatabaseRef,
{
let mut inspector = Inspector::default();
let mut evm = revm::EVM::new();
evm.env = env;
evm.env = self.build_call_env(request, fee_details, block_env)?;
evm.database(state);
let (exit, out, gas, state, _) = evm.inspect_ref(&mut inspector);
inspector.print_logs();
Ok((exit, out, gas, state))
}

pub fn build_access_list_with_state<D>(
&self,
state: D,
request: EthTransactionRequest,
fee_details: FeeDetails,
block_env: BlockEnv,
) -> Result<(Return, TransactOut, u64, AccessList), BlockchainError>
where
D: DatabaseRef,
{
let from = request.from.unwrap_or_default();
let to = request.to.unwrap_or_else(|| {
let nonce = state.basic(from).nonce;
get_contract_address(from, nonce)
});

let mut tracer = AccessListTracer::new(
AccessList(request.access_list.clone().unwrap_or_default()),
from,
to,
self.precompiles(),
);

let mut evm = revm::EVM::new();
evm.env = self.build_call_env(request, fee_details, block_env)?;
evm.database(state);
let (exit, out, gas, _, _) = evm.inspect_ref(&mut tracer);
let access_list = tracer.access_list();
Ok((exit, out, gas, access_list))
}

/// returns all receipts for the given transactions
fn get_receipts(&self, tx_hashes: impl IntoIterator<Item = TxHash>) -> Vec<TypedReceipt> {
let storage = self.blockchain.storage.read();
Expand Down
49 changes: 24 additions & 25 deletions anvil/src/eth/util.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
use ethers::types::H160;
use ethers::abi::Address;
use forge::revm::SpecId;
use std::fmt;

macro_rules! precompile_addr {
($idx:expr) => {{
H160([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, $idx])
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()
}};
}

/// All ethereum precompiles ref <https://ethereum.github.io/yellowpaper/paper.pdf>
pub static PRECOMPILES: [H160; 9] = [
// ecrecover
precompile_addr!(1),
// keccak
precompile_addr!(2),
// ripemd
precompile_addr!(3),
// identity
precompile_addr!(4),
// modexp
precompile_addr!(5),
// ecadd
precompile_addr!(6),
// ecmul
precompile_addr!(7),
// ecpairing
precompile_addr!(8),
// blake2f
precompile_addr!(9),
];
pub fn get_precompiles_for(spec_id: SpecId) -> Vec<Address> {
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),
}
}

/// wrapper type that displays byte as hex
pub struct HexDisplay<'a>(&'a [u8]);
Expand Down
Loading

0 comments on commit 54818ae

Please sign in to comment.