From ed3ed155b06d05060b213e8a7fe86d4ffae42b46 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:17:08 +0200 Subject: [PATCH] chore(anvil): use dyn DatabaseRef instead of generics (#8920) --- Cargo.lock | 1 - crates/anvil/Cargo.toml | 1 - crates/anvil/src/eth/api.rs | 13 ++-- crates/anvil/src/eth/backend/db.rs | 26 ++++++- crates/anvil/src/eth/backend/executor.rs | 10 +-- crates/anvil/src/eth/backend/mem/fork_db.rs | 15 +++-- .../anvil/src/eth/backend/mem/in_memory_db.rs | 4 ++ crates/anvil/src/eth/backend/mem/mod.rs | 67 +++++++++---------- crates/anvil/src/eth/backend/validate.rs | 1 - 9 files changed, 80 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4726ad0420a0..e8dc2fee52d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -873,7 +873,6 @@ dependencies = [ "anvil-rpc", "anvil-server", "async-trait", - "auto_impl", "axum", "bytes", "chrono", diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 77faec1f1e52..0723fdb47f80 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -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" diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index f856fef42c5a..ec2d0dd6a9e6 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2445,7 +2445,7 @@ impl EthApi { state, )?); } - self.do_estimate_gas_with_state(request, state, block) + self.do_estimate_gas_with_state(request, &state, block) }) .await? } @@ -2453,15 +2453,12 @@ impl EthApi { /// 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( + fn do_estimate_gas_with_state( &self, mut request: WithOtherFields, - state: D, + state: &dyn DatabaseRef, block_env: BlockEnv, - ) -> Result - where - D: DatabaseRef, - { + ) -> Result { // 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); @@ -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()); diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 60565c4d5467..a02f9d0e31e1 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -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 { + /// Returns a reference to the database as a `dyn DatabaseRef`. + // TODO: Required until trait upcasting is stabilized: + fn as_dyn(&self) -> &dyn DatabaseRef; + fn maybe_as_full_db(&self) -> Option<&HashMap> { None } @@ -51,6 +54,10 @@ impl<'a, T: 'a + MaybeFullDatabase + ?Sized> MaybeFullDatabase for &'a T where &'a T: DatabaseRef, { + fn as_dyn(&self) -> &dyn DatabaseRef { + T::as_dyn(self) + } + fn maybe_as_full_db(&self) -> Option<&HashMap> { T::maybe_as_full_db(self) } @@ -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, block_number: BlockId) -> Result<(), String>; @@ -79,7 +85,6 @@ pub trait MaybeForkedDatabase { } /// This bundles all required revm traits -#[auto_impl::auto_impl(Box)] pub trait Db: DatabaseRef + Database @@ -188,6 +193,13 @@ pub trait Db: fn current_state(&self) -> StateDb; } +impl dyn Db { + // TODO: Required until trait upcasting is stabilized: + pub fn as_dbref(&self) -> &dyn DatabaseRef { + 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 @@ -230,6 +242,10 @@ impl + Send + Sync + Clone + fmt::Debug> D } impl> MaybeFullDatabase for CacheDB { + fn as_dyn(&self) -> &dyn DatabaseRef { + self + } + fn maybe_as_full_db(&self) -> Option<&HashMap> { Some(&self.accounts) } @@ -338,6 +354,10 @@ impl DatabaseRef for StateDb { } impl MaybeFullDatabase for StateDb { + fn as_dyn(&self) -> &dyn DatabaseRef { + self.0.as_dyn() + } + fn maybe_as_full_db(&self) -> Option<&HashMap> { self.0.maybe_as_full_db() } diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index c84ad52007c2..caa042bf4886 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -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>, pub block_env: BlockEnv, @@ -111,7 +111,7 @@ pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> pub precompile_factory: Option>, } -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(); @@ -262,8 +262,8 @@ pub enum TransactionExecutionOutcome { DatabaseError(Arc, 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; diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index 1329de724a1c..a5f6cf455a22 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -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) @@ -87,6 +86,10 @@ impl Db for ForkedDatabase { } impl MaybeFullDatabase for ForkedDatabase { + fn as_dyn(&self) -> &dyn DatabaseRef { + self + } + fn clear_into_snapshot(&mut self) -> StateSnapshot { let db = self.inner().db(); let accounts = std::mem::take(&mut *db.accounts.write()); @@ -118,6 +121,10 @@ impl MaybeFullDatabase for ForkedDatabase { } impl MaybeFullDatabase for ForkDbSnapshot { + fn as_dyn(&self) -> &dyn DatabaseRef { + self + } + fn clear_into_snapshot(&mut self) -> StateSnapshot { std::mem::take(&mut self.snapshot) } diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index 56cd3815cb08..f984b93bbbcf 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -104,6 +104,10 @@ impl Db for MemDb { } impl MaybeFullDatabase for MemDb { + fn as_dyn(&self) -> &dyn DatabaseRef { + self + } + fn maybe_as_full_db(&self) -> Option<&HashMap> { Some(&self.inner.accounts) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 53dc9898f073..d8846b3b86f7 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -857,16 +857,19 @@ impl Backend { } /// Creates an EVM instance with optionally injected precompiles. - fn new_evm_with_inspector_ref( + #[allow(clippy::type_complexity)] + fn new_evm_with_inspector_ref<'i, 'db>( &self, - db: DB, + db: &'db dyn DatabaseRef, env: EnvWithHandlerCfg, - inspector: I, - ) -> revm::Evm<'_, I, WrapDatabaseRef> - where - DB: revm::DatabaseRef, - I: InspectorExt>, - { + inspector: &'i mut dyn InspectorExt< + WrapDatabaseRef<&'db dyn DatabaseRef>, + >, + ) -> revm::Evm< + '_, + &'i mut dyn InspectorExt>>, + WrapDatabaseRef<&'db dyn DatabaseRef>, + > { let mut evm = new_evm_with_inspector_ref(db, env, inspector); if let Some(factory) = &self.precompile_factory { inject_precompiles(&mut evm, factory.precompiles()); @@ -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, .. } => { @@ -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(), @@ -1151,10 +1154,10 @@ impl Backend { self.with_database_at(block_request, |state, block| { let block_number = block.number.to::(); 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); @@ -1263,16 +1266,13 @@ impl Backend { inspector } - pub fn call_with_state( + pub fn call_with_state( &self, - state: D, + state: &dyn DatabaseRef, request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, - ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> - where - D: DatabaseRef, - { + ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> { let mut inspector = self.build_inspector(); let env = self.build_call_env(request, fee_details, block_env); @@ -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); @@ -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 { @@ -1381,16 +1384,13 @@ impl Backend { .await? } - pub fn build_access_list_with_state( + pub fn build_access_list_with_state( &self, - state: D, + state: &dyn DatabaseRef, request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, - ) -> Result<(InstructionResult, Option, u64, AccessList), BlockchainError> - where - D: DatabaseRef, - { + ) -> Result<(InstructionResult, Option, u64, AccessList), BlockchainError> { let from = request.from.unwrap_or_default(); let to = if let Some(TxKind::Call(to)) = request.to { to @@ -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( @@ -1937,17 +1937,14 @@ impl Backend { address: Address, block_request: Option, ) -> Result { - 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( + pub fn get_code_with_state( &self, - state: D, + state: &dyn DatabaseRef, address: Address, - ) -> Result - where - D: DatabaseRef, - { + ) -> Result { trace!(target: "backend", "get code for {:?}", address); let account = state.basic_ref(address)?.unwrap_or_default(); if account.code_hash == KECCAK_EMPTY { diff --git a/crates/anvil/src/eth/backend/validate.rs b/crates/anvil/src/eth/backend/validate.rs index 650ce24a5501..eca3fd9e3bfe 100644 --- a/crates/anvil/src/eth/backend/validate.rs +++ b/crates/anvil/src/eth/backend/validate.rs @@ -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 ///