Skip to content

Commit

Permalink
chore(anvil): use dyn DatabaseRef instead of generics (#8920)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes authored Sep 21, 2024
1 parent a301f26 commit ed3ed15
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 58 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

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

1 change: 0 additions & 1 deletion crates/anvil/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ clap = { version = "4", features = [
], optional = true }
clap_complete = { version = "4", optional = true }
chrono.workspace = true
auto_impl.workspace = true
ctrlc = { version = "3", optional = true }
fdlimit = { version = "0.3", optional = true }
clap_complete_fig = "4"
Expand Down
13 changes: 5 additions & 8 deletions crates/anvil/src/eth/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2445,23 +2445,20 @@ impl EthApi {
state,
)?);
}
self.do_estimate_gas_with_state(request, state, block)
self.do_estimate_gas_with_state(request, &state, block)
})
.await?
}

/// Estimates the gas usage of the `request` with the state.
///
/// This will execute the transaction request and find the best gas limit via binary search.
fn do_estimate_gas_with_state<D>(
fn do_estimate_gas_with_state(
&self,
mut request: WithOtherFields<TransactionRequest>,
state: D,
state: &dyn DatabaseRef<Error = DatabaseError>,
block_env: BlockEnv,
) -> Result<u128>
where
D: DatabaseRef<Error = DatabaseError>,
{
) -> Result<u128> {
// If the request is a simple native token transfer we can optimize
// We assume it's a transfer if we have no input data.
let to = request.to.as_ref().and_then(TxKind::to);
Expand Down Expand Up @@ -2497,7 +2494,7 @@ impl EthApi {
// If we have non-zero gas price, cap gas limit by sender balance
if gas_price > 0 {
if let Some(from) = request.from {
let mut available_funds = self.backend.get_balance_with_state(&state, from)?;
let mut available_funds = self.backend.get_balance_with_state(state, from)?;
if let Some(value) = request.value {
if value > available_funds {
return Err(InvalidTransactionError::InsufficientFunds.into());
Expand Down
26 changes: 23 additions & 3 deletions crates/anvil/src/eth/backend/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ use serde::{
use std::{collections::BTreeMap, fmt, path::Path};

/// Helper trait get access to the full state data of the database
#[auto_impl::auto_impl(Box)]
pub trait MaybeFullDatabase: DatabaseRef<Error = DatabaseError> {
/// Returns a reference to the database as a `dyn DatabaseRef`.
// TODO: Required until trait upcasting is stabilized: <https://github.com/rust-lang/rust/issues/65991>
fn as_dyn(&self) -> &dyn DatabaseRef<Error = DatabaseError>;

fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
None
}
Expand All @@ -51,6 +54,10 @@ impl<'a, T: 'a + MaybeFullDatabase + ?Sized> MaybeFullDatabase for &'a T
where
&'a T: DatabaseRef<Error = DatabaseError>,
{
fn as_dyn(&self) -> &dyn DatabaseRef<Error = DatabaseError> {
T::as_dyn(self)
}

fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
T::maybe_as_full_db(self)
}
Expand All @@ -69,7 +76,6 @@ where
}

/// Helper trait to reset the DB if it's forked
#[auto_impl::auto_impl(Box)]
pub trait MaybeForkedDatabase {
fn maybe_reset(&mut self, _url: Option<String>, block_number: BlockId) -> Result<(), String>;

Expand All @@ -79,7 +85,6 @@ pub trait MaybeForkedDatabase {
}

/// This bundles all required revm traits
#[auto_impl::auto_impl(Box)]
pub trait Db:
DatabaseRef<Error = DatabaseError>
+ Database<Error = DatabaseError>
Expand Down Expand Up @@ -188,6 +193,13 @@ pub trait Db:
fn current_state(&self) -> StateDb;
}

impl dyn Db {
// TODO: Required until trait upcasting is stabilized: <https://github.com/rust-lang/rust/issues/65991>
pub fn as_dbref(&self) -> &dyn DatabaseRef<Error = DatabaseError> {
self.as_dyn()
}
}

/// Convenience impl only used to use any `Db` on the fly as the db layer for revm's CacheDB
/// This is useful to create blocks without actually writing to the `Db`, but rather in the cache of
/// the `CacheDB` see also
Expand Down Expand Up @@ -230,6 +242,10 @@ impl<T: DatabaseRef<Error = DatabaseError> + Send + Sync + Clone + fmt::Debug> D
}

impl<T: DatabaseRef<Error = DatabaseError>> MaybeFullDatabase for CacheDB<T> {
fn as_dyn(&self) -> &dyn DatabaseRef<Error = DatabaseError> {
self
}

fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
Some(&self.accounts)
}
Expand Down Expand Up @@ -338,6 +354,10 @@ impl DatabaseRef for StateDb {
}

impl MaybeFullDatabase for StateDb {
fn as_dyn(&self) -> &dyn DatabaseRef<Error = DatabaseError> {
self.0.as_dyn()
}

fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
self.0.maybe_as_full_db()
}
Expand Down
10 changes: 5 additions & 5 deletions crates/anvil/src/eth/backend/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ pub struct ExecutedTransactions {
}

/// An executor for a series of transactions
pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> {
pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> {
/// where to insert the transactions
pub db: &'a mut Db,
/// type used to validate before inclusion
pub validator: Validator,
pub validator: &'a V,
/// all pending transactions
pub pending: std::vec::IntoIter<Arc<PoolTransaction>>,
pub block_env: BlockEnv,
Expand All @@ -111,7 +111,7 @@ pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator>
pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
}

impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<'a, DB, Validator> {
impl<'a, DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'a, DB, V> {
/// Executes all transactions and puts them in a new block with the provided `timestamp`
pub fn execute(mut self) -> ExecutedTransactions {
let mut transactions = Vec::new();
Expand Down Expand Up @@ -262,8 +262,8 @@ pub enum TransactionExecutionOutcome {
DatabaseError(Arc<PoolTransaction>, DatabaseError),
}

impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator
for &'b mut TransactionExecutor<'a, DB, Validator>
impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator
for &'b mut TransactionExecutor<'a, DB, V>
{
type Item = TransactionExecutionOutcome;

Expand Down
15 changes: 11 additions & 4 deletions crates/anvil/src/eth/backend/mem/fork_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ use crate::{
use alloy_primitives::{Address, B256, U256, U64};
use alloy_rpc_types::BlockId;
use foundry_evm::{
backend::{BlockchainDb, DatabaseResult, RevertSnapshotAction, StateSnapshot},
backend::{BlockchainDb, DatabaseError, DatabaseResult, RevertSnapshotAction, StateSnapshot},
fork::database::ForkDbSnapshot,
revm::Database,
revm::{primitives::BlockEnv, Database},
};
use revm::DatabaseRef;

pub use foundry_evm::fork::database::ForkedDatabase;
use foundry_evm::revm::primitives::BlockEnv;

/// Implement the helper for the fork database
impl Db for ForkedDatabase {
fn insert_account(&mut self, address: Address, account: AccountInfo) {
self.database_mut().insert_account(address, account)
Expand Down Expand Up @@ -87,6 +86,10 @@ impl Db for ForkedDatabase {
}

impl MaybeFullDatabase for ForkedDatabase {
fn as_dyn(&self) -> &dyn DatabaseRef<Error = DatabaseError> {
self
}

fn clear_into_snapshot(&mut self) -> StateSnapshot {
let db = self.inner().db();
let accounts = std::mem::take(&mut *db.accounts.write());
Expand Down Expand Up @@ -118,6 +121,10 @@ impl MaybeFullDatabase for ForkedDatabase {
}

impl MaybeFullDatabase for ForkDbSnapshot {
fn as_dyn(&self) -> &dyn DatabaseRef<Error = DatabaseError> {
self
}

fn clear_into_snapshot(&mut self) -> StateSnapshot {
std::mem::take(&mut self.snapshot)
}
Expand Down
4 changes: 4 additions & 0 deletions crates/anvil/src/eth/backend/mem/in_memory_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ impl Db for MemDb {
}

impl MaybeFullDatabase for MemDb {
fn as_dyn(&self) -> &dyn DatabaseRef<Error = foundry_evm::backend::DatabaseError> {
self
}

fn maybe_as_full_db(&self) -> Option<&HashMap<Address, DbAccount>> {
Some(&self.inner.accounts)
}
Expand Down
67 changes: 32 additions & 35 deletions crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -857,16 +857,19 @@ impl Backend {
}

/// Creates an EVM instance with optionally injected precompiles.
fn new_evm_with_inspector_ref<DB, I>(
#[allow(clippy::type_complexity)]
fn new_evm_with_inspector_ref<'i, 'db>(
&self,
db: DB,
db: &'db dyn DatabaseRef<Error = DatabaseError>,
env: EnvWithHandlerCfg,
inspector: I,
) -> revm::Evm<'_, I, WrapDatabaseRef<DB>>
where
DB: revm::DatabaseRef,
I: InspectorExt<WrapDatabaseRef<DB>>,
{
inspector: &'i mut dyn InspectorExt<
WrapDatabaseRef<&'db dyn DatabaseRef<Error = DatabaseError>>,
>,
) -> revm::Evm<
'_,
&'i mut dyn InspectorExt<WrapDatabaseRef<&'db dyn DatabaseRef<Error = DatabaseError>>>,
WrapDatabaseRef<&'db dyn DatabaseRef<Error = DatabaseError>>,
> {
let mut evm = new_evm_with_inspector_ref(db, env, inspector);
if let Some(factory) = &self.precompile_factory {
inject_precompiles(&mut evm, factory.precompiles());
Expand All @@ -892,7 +895,7 @@ impl Backend {

let db = self.db.read().await;
let mut inspector = self.build_inspector();
let mut evm = self.new_evm_with_inspector_ref(&**db, env, &mut inspector);
let mut evm = self.new_evm_with_inspector_ref(db.as_dyn(), env, &mut inspector);
let ResultAndState { result, state } = evm.transact()?;
let (exit_reason, gas_used, out, logs) = match result {
ExecutionResult::Success { reason, gas_used, logs, output, .. } => {
Expand Down Expand Up @@ -1011,7 +1014,7 @@ impl Backend {
env.block.timestamp = U256::from(self.time.next_timestamp());

let executor = TransactionExecutor {
db: &mut *db,
db: &mut **db,
validator: self,
pending: pool_transactions.into_iter(),
block_env: env.block.clone(),
Expand Down Expand Up @@ -1151,10 +1154,10 @@ impl Backend {
self.with_database_at(block_request, |state, block| {
let block_number = block.number.to::<u64>();
let (exit, out, gas, state) = match overrides {
None => self.call_with_state(state, request, fee_details, block),
None => self.call_with_state(state.as_dyn(), request, fee_details, block),
Some(overrides) => {
let state = state::apply_state_override(overrides.into_iter().collect(), state)?;
self.call_with_state(state, request, fee_details, block)
self.call_with_state(state.as_dyn(), request, fee_details, block)
},
}?;
trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number);
Expand Down Expand Up @@ -1263,16 +1266,13 @@ impl Backend {
inspector
}

pub fn call_with_state<D>(
pub fn call_with_state(
&self,
state: D,
state: &dyn DatabaseRef<Error = DatabaseError>,
request: WithOtherFields<TransactionRequest>,
fee_details: FeeDetails,
block_env: BlockEnv,
) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError>
where
D: DatabaseRef<Error = DatabaseError>,
{
) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
let mut inspector = self.build_inspector();

let env = self.build_call_env(request, fee_details, block_env);
Expand Down Expand Up @@ -1319,8 +1319,11 @@ impl Backend {
);

let env = self.build_call_env(request, fee_details, block);
let mut evm =
self.new_evm_with_inspector_ref(state, env, &mut inspector);
let mut evm = self.new_evm_with_inspector_ref(
state.as_dyn(),
env,
&mut inspector,
);
let ResultAndState { result, state: _ } = evm.transact()?;

drop(evm);
Expand Down Expand Up @@ -1352,7 +1355,7 @@ impl Backend {
.with_tracing_config(TracingInspectorConfig::from_geth_config(&config));

let env = self.build_call_env(request, fee_details, block);
let mut evm = self.new_evm_with_inspector_ref(state, env, &mut inspector);
let mut evm = self.new_evm_with_inspector_ref(state.as_dyn(), env, &mut inspector);
let ResultAndState { result, state: _ } = evm.transact()?;

let (exit_reason, gas_used, out) = match result {
Expand Down Expand Up @@ -1381,16 +1384,13 @@ impl Backend {
.await?
}

pub fn build_access_list_with_state<D>(
pub fn build_access_list_with_state(
&self,
state: D,
state: &dyn DatabaseRef<Error = DatabaseError>,
request: WithOtherFields<TransactionRequest>,
fee_details: FeeDetails,
block_env: BlockEnv,
) -> Result<(InstructionResult, Option<Output>, u64, AccessList), BlockchainError>
where
D: DatabaseRef<Error = DatabaseError>,
{
) -> Result<(InstructionResult, Option<Output>, u64, AccessList), BlockchainError> {
let from = request.from.unwrap_or_default();
let to = if let Some(TxKind::Call(to)) = request.to {
to
Expand Down Expand Up @@ -1911,7 +1911,7 @@ impl Backend {

let db = self.db.read().await;
let block = self.env.read().block.clone();
Ok(f(Box::new(&*db), block))
Ok(f(Box::new(&**db), block))
}

pub async fn storage_at(
Expand All @@ -1937,17 +1937,14 @@ impl Backend {
address: Address,
block_request: Option<BlockRequest>,
) -> Result<Bytes, BlockchainError> {
self.with_database_at(block_request, |db, _| self.get_code_with_state(db, address)).await?
self.with_database_at(block_request, |db, _| self.get_code_with_state(&db, address)).await?
}

pub fn get_code_with_state<D>(
pub fn get_code_with_state(
&self,
state: D,
state: &dyn DatabaseRef<Error = DatabaseError>,
address: Address,
) -> Result<Bytes, BlockchainError>
where
D: DatabaseRef<Error = DatabaseError>,
{
) -> Result<Bytes, BlockchainError> {
trace!(target: "backend", "get code for {:?}", address);
let account = state.basic_ref(address)?.unwrap_or_default();
if account.code_hash == KECCAK_EMPTY {
Expand Down
1 change: 0 additions & 1 deletion crates/anvil/src/eth/backend/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use foundry_evm::revm::primitives::{AccountInfo, EnvWithHandlerCfg};

/// A trait for validating transactions
#[async_trait::async_trait]
#[auto_impl::auto_impl(&, Box)]
pub trait TransactionValidator {
/// Validates the transaction's validity when it comes to nonce, payment
///
Expand Down

0 comments on commit ed3ed15

Please sign in to comment.