From f664107421db5806b82c27b8eb1b11880f328f11 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Fri, 12 Jan 2024 10:56:05 -0400 Subject: [PATCH 1/3] chore: remove EthTransactionRequest usage, migrate AccessListTracer --- Cargo.lock | 1 + crates/anvil/core/src/eth/utils.rs | 7 ++ crates/anvil/src/eth/api.rs | 71 +++++++++----------- crates/anvil/src/eth/backend/mem/mod.rs | 48 +++++++------ crates/evm/evm/Cargo.toml | 1 + crates/evm/evm/src/inspectors/access_list.rs | 16 ++--- 6 files changed, 69 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4140ae6b6467..d5106ad37e49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3145,6 +3145,7 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", + "alloy-rpc-types", "alloy-sol-types", "const-hex", "ethers-core", diff --git a/crates/anvil/core/src/eth/utils.rs b/crates/anvil/core/src/eth/utils.rs index 21711cb3eb9c..e67e5dd4a905 100644 --- a/crates/anvil/core/src/eth/utils.rs +++ b/crates/anvil/core/src/eth/utils.rs @@ -1,4 +1,5 @@ use alloy_primitives::{Address, U256}; +use alloy_rpc_types::AccessListItem as AlloyAccessListItem; use ethers_core::{ types::transaction::eip2930::AccessListItem, utils::{ @@ -26,3 +27,9 @@ pub fn to_revm_access_list(list: Vec) -> Vec<(Address, Vec }) .collect() } + +pub fn alloy_to_revm_access_list(list: Vec) -> Vec<(Address, Vec)> { + list.into_iter() + .map(|item| (item.address, item.storage_keys.into_iter().map(|k| k.into()).collect())) + .collect() +} diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 6fbe1ac166cf..847511b74804 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -46,9 +46,8 @@ use anvil_core::{ eth::{ block::BlockInfo, transaction::{ - call_to_internal_tx_request, to_alloy_proof, to_ethers_access_list, - EthTransactionRequest, LegacyTransaction, PendingTransaction, TransactionKind, - TypedTransaction, TypedTransactionRequest, + call_to_internal_tx_request, to_alloy_proof, EthTransactionRequest, LegacyTransaction, + PendingTransaction, TransactionKind, TypedTransaction, TypedTransactionRequest, }, EthRequest, }, @@ -1007,7 +1006,6 @@ impl EthApi { request.max_priority_fee_per_gas, )? .or_zero_fees(); - let request = call_to_internal_tx_request(&request); // this can be blocking for a bit, especially in forking mode // self.on_blocking_task(|this| async move { @@ -1035,7 +1033,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_createAccessList` pub async fn create_access_list( &self, - request: CallRequest, + mut request: CallRequest, block_number: Option, ) -> Result { node_info!("eth_createAccessList"); @@ -1052,8 +1050,6 @@ impl EthApi { } } - let mut request = call_to_internal_tx_request(&request); - self.backend .with_database_at(Some(block_request), |state, block_env| { let (exit, out, _, access_list) = self.backend.build_access_list_with_state( @@ -1065,7 +1061,7 @@ impl EthApi { ensure_return_ok(exit, &out)?; // execute again but with access list set - request.access_list = Some(to_ethers_access_list(access_list.clone()).0); + request.access_list = Some(access_list.clone()); let (exit, out, gas_used, _) = self.backend.call_with_state( &state, @@ -1474,8 +1470,6 @@ impl EthApi { )? .or_zero_fees(); - let request = call_to_internal_tx_request(&request); - self.backend.call_with_tracing(request, fees, Some(block_request), opts).await } @@ -2187,20 +2181,19 @@ impl EthApi { /// This will execute the [CallRequest] and find the best gas limit via binary search fn do_estimate_gas_with_state( &self, - request: CallRequest, + mut request: CallRequest, state: D, block_env: BlockEnv, ) -> Result where D: DatabaseRef, { - let mut request = call_to_internal_tx_request(&request); - // if the request is a simple transfer we can optimize - let likely_transfer = - request.data.as_ref().map(|data| data.as_ref().is_empty()).unwrap_or(true); + // 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 likely_transfer = request.input.clone().into_input().is_none(); if likely_transfer { if let Some(to) = request.to { - if let Ok(target_code) = self.backend.get_code_with_state(&state, to.to_alloy()) { + if let Ok(target_code) = self.backend.get_code_with_state(&state, to) { if target_code.as_ref().is_empty() { return Ok(MIN_TRANSACTION_GAS); } @@ -2209,34 +2202,33 @@ impl EthApi { } let fees = FeeDetails::new( - request.gas_price.map(ToAlloy::to_alloy), - request.max_fee_per_gas.map(ToAlloy::to_alloy), - request.max_priority_fee_per_gas.map(ToAlloy::to_alloy), + request.gas_price, + request.max_fee_per_gas, + request.max_priority_fee_per_gas, )? .or_zero_fees(); // get the highest possible gas limit, either the request's set value or the currently // configured gas limit - let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit.to_ethers()); + let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit); // check with the funds of the sender if let Some(from) = request.from { let gas_price = fees.gas_price.unwrap_or_default(); if gas_price > U256::ZERO { - let mut available_funds = - self.backend.get_balance_with_state(&state, from.to_alloy())?; + let mut available_funds = self.backend.get_balance_with_state(&state, from)?; if let Some(value) = request.value { - if value > available_funds.to_ethers() { + if value > available_funds { return Err(InvalidTransactionError::InsufficientFunds.into()); } // safe: value < available_funds - available_funds -= value.to_alloy(); + available_funds -= value; } // amount of gas the sender can afford with the `gas_price` let allowance = available_funds.checked_div(gas_price).unwrap_or_default(); - if highest_gas_limit > allowance.to_ethers() { + if highest_gas_limit > allowance { trace!(target: "node", "eth_estimateGas capped by limited user funds"); - highest_gas_limit = allowance.to_ethers(); + highest_gas_limit = allowance; } } } @@ -2264,7 +2256,7 @@ impl EthApi { self.backend.clone(), block_env, fees, - gas_limit.to_alloy(), + gas_limit, )); } } @@ -2275,7 +2267,7 @@ impl EthApi { // succeeded } InstructionResult::OutOfGas | InstructionResult::OutOfFund => { - return Err(InvalidTransactionError::BasicOutOfGas(gas_limit).into()) + return Err(InvalidTransactionError::BasicOutOfGas(gas_limit.to_ethers()).into()) } // need to check if the revert was due to lack of gas or unrelated reason // we're also checking for InvalidFEOpcode here because this can be used to trigger an error common usage in openzeppelin @@ -2289,7 +2281,7 @@ impl EthApi { self.backend.clone(), block_env, fees, - gas_limit.to_alloy(), + gas_limit, )) } else { // the transaction did fail due to lack of gas from the user @@ -2309,17 +2301,18 @@ impl EthApi { // transaction requires to succeed let gas: U256 = U256::from(gas); // Get the starting lowest gas needed depending on the transaction kind. - let mut lowest_gas_limit = determine_base_gas_by_kind(request.clone()); + let mut lowest_gas_limit = + determine_base_gas_by_kind(call_to_internal_tx_request(&request.clone())); // pick a point that's close to the estimated gas let mut mid_gas_limit = std::cmp::min( gas * U256::from(3), - ((highest_gas_limit + lowest_gas_limit.to_ethers()) / 2).to_alloy(), + (highest_gas_limit + lowest_gas_limit) / U256::from(2), ); // Binary search for the ideal gas limit - while (highest_gas_limit - lowest_gas_limit.to_ethers()).to_alloy() > U256::from(1) { - request.gas = Some(mid_gas_limit.to_ethers()); + while (highest_gas_limit - lowest_gas_limit) > U256::from(1) { + request.gas = Some(mid_gas_limit); let ethres = self.backend.call_with_state( &state, request.clone(), @@ -2337,7 +2330,7 @@ impl EthApi { lowest_gas_limit = mid_gas_limit; // new midpoint - mid_gas_limit = ((highest_gas_limit + lowest_gas_limit.to_ethers()) / 2).to_alloy(); + mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / U256::from(2); continue; } @@ -2347,7 +2340,7 @@ impl EthApi { // at the current midpoint, as spending any more gas would // make no sense (as the TX would still succeed). return_ok!() => { - highest_gas_limit = mid_gas_limit.to_ethers(); + highest_gas_limit = mid_gas_limit; } // If the transaction failed due to lack of gas, we can set a floor for the // lowest gas limit at the current midpoint, as spending any @@ -2374,12 +2367,12 @@ impl EthApi { } } // new midpoint - mid_gas_limit = ((highest_gas_limit + lowest_gas_limit.to_ethers()) / 2).to_alloy(); + mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / U256::from(2); } trace!(target : "node", "Estimated Gas for call {:?}", highest_gas_limit); - Ok(highest_gas_limit.to_alloy()) + Ok(highest_gas_limit) } /// Updates the `TransactionOrder` @@ -2647,7 +2640,7 @@ fn ensure_return_ok(exit: InstructionResult, out: &Option) -> Result( - mut request: EthTransactionRequest, + mut request: CallRequest, state: D, backend: Arc, block_env: BlockEnv, @@ -2657,7 +2650,7 @@ fn map_out_of_gas_err( where D: DatabaseRef, { - request.gas = Some(backend.gas_limit()).map(|g| g.to_ethers()); + request.gas = Some(backend.gas_limit()); let (exit, out, _, _) = match backend.call_with_state(&state, request, fees, block_env) { Ok(res) => res, Err(err) => return err, diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index d27b45d2ce70..d55f06041287 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -36,8 +36,8 @@ use alloy_rpc_trace_types::{ }; use alloy_rpc_types::{ state::StateOverride, AccessList, Block as AlloyBlock, BlockId, - BlockNumberOrTag as BlockNumber, Filter, FilteredParams, Header as AlloyHeader, Log, - Transaction, TransactionReceipt, + BlockNumberOrTag as BlockNumber, CallRequest, Filter, FilteredParams, Header as AlloyHeader, + Log, Transaction, TransactionReceipt, }; use anvil_core::{ eth::{ @@ -45,18 +45,16 @@ use anvil_core::{ proof::{AccountProof, BasicAccount, StorageProof}, receipt::{EIP658Receipt, TypedReceipt}, transaction::{ - from_ethers_access_list, EthTransactionRequest, MaybeImpersonatedTransaction, - PendingTransaction, TransactionInfo, TypedTransaction, + MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo, TypedTransaction, }, trie::RefTrieDB, - utils::to_revm_access_list, + utils::alloy_to_revm_access_list, }, types::{Forking, Index}, }; use anvil_rpc::error::RpcError; use ethers::{ abi::ethereum_types::BigEndianHash, - types::transaction::eip2930::AccessList as EthersAccessList, utils::{keccak256, rlp}, }; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; @@ -1024,14 +1022,14 @@ impl Backend { outcome } - /// Executes the `EthTransactionRequest` without writing to the DB + /// Executes the [CallRequest] without writing to the DB /// /// # Errors /// /// Returns an error if the `block_number` is greater than the current height pub async fn call( &self, - request: EthTransactionRequest, + request: CallRequest, fee_details: FeeDetails, block_request: Option, overrides: Option, @@ -1052,15 +1050,15 @@ impl Backend { fn build_call_env( &self, - request: EthTransactionRequest, + request: CallRequest, fee_details: FeeDetails, block_env: BlockEnv, ) -> Env { - let EthTransactionRequest { from, to, gas, value, data, nonce, access_list, .. } = request; + let CallRequest { from, to, gas, value, input, nonce, access_list, .. } = request; let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = fee_details; - let gas_limit = gas.unwrap_or(block_env.gas_limit.to_ethers()); + let gas_limit = gas.unwrap_or(block_env.gas_limit); let mut env = self.env.read().clone(); env.block = block_env; // we want to disable this in eth_call, since this is common practice used by other node @@ -1075,19 +1073,19 @@ impl Backend { let caller = from.unwrap_or_default(); env.tx = TxEnv { - caller: caller.to_alloy(), - gas_limit: gas_limit.as_u64(), + caller, + gas_limit: gas_limit.to::(), gas_price, gas_priority_fee: max_priority_fee_per_gas, transact_to: match to { - Some(addr) => TransactTo::Call(addr.to_alloy()), + Some(addr) => TransactTo::Call(addr), None => TransactTo::Create(CreateScheme::Create), }, - value: value.unwrap_or_default().to_alloy(), - data: data.unwrap_or_default().to_vec().into(), + value: value.unwrap_or_default(), + data: input.into_input().unwrap_or_default(), chain_id: None, - nonce: nonce.map(|n| n.as_u64()), - access_list: to_revm_access_list(access_list.unwrap_or_default()), + nonce: nonce.map(|n| n.to::()), + access_list: alloy_to_revm_access_list(access_list.unwrap_or_default().0), ..Default::default() }; @@ -1103,7 +1101,7 @@ impl Backend { pub fn call_with_state( &self, state: D, - request: EthTransactionRequest, + request: CallRequest, fee_details: FeeDetails, block_env: BlockEnv, ) -> Result<(InstructionResult, Option, u64, State), BlockchainError> @@ -1149,7 +1147,7 @@ impl Backend { pub async fn call_with_tracing( &self, - request: EthTransactionRequest, + request: CallRequest, fee_details: FeeDetails, block_request: Option, opts: GethDefaultTracingOptions, @@ -1189,23 +1187,23 @@ impl Backend { pub fn build_access_list_with_state( &self, state: D, - request: EthTransactionRequest, + request: CallRequest, fee_details: FeeDetails, block_env: BlockEnv, ) -> Result<(InstructionResult, Option, u64, AccessList), BlockchainError> where D: DatabaseRef, { - let from = request.from.unwrap_or_default().to_alloy(); + let from = request.from.unwrap_or_default(); let to = if let Some(to) = request.to { - to.to_alloy() + to } else { let nonce = state.basic_ref(from)?.unwrap_or_default().nonce; from.create(nonce) }; let mut tracer = AccessListTracer::new( - EthersAccessList(request.access_list.clone().unwrap_or_default()), + request.access_list.clone().unwrap_or_default(), from, to, self.precompiles(), @@ -1230,7 +1228,7 @@ impl Backend { } }; let access_list = tracer.access_list(); - Ok((exit_reason, out, gas_used, from_ethers_access_list(access_list))) + Ok((exit_reason, out, gas_used, access_list)) } /// returns all receipts for the given transactions diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index b05871c6494d..ccf2cb1ac4fa 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -24,6 +24,7 @@ alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-sol-types.workspace = true +alloy-rpc-types.workspace = true hashbrown = { version = "0.14", features = ["serde"] } revm = { workspace = true, default-features = false, features = [ "std", diff --git a/crates/evm/evm/src/inspectors/access_list.rs b/crates/evm/evm/src/inspectors/access_list.rs index 37f4e13edbc8..d7425730f6cf 100644 --- a/crates/evm/evm/src/inspectors/access_list.rs +++ b/crates/evm/evm/src/inspectors/access_list.rs @@ -1,6 +1,5 @@ use alloy_primitives::{Address, B256}; -use ethers_core::types::transaction::eip2930::{AccessList, AccessListItem}; -use foundry_common::types::{ToAlloy, ToEthers}; +use alloy_rpc_types::{AccessList, AccessListItem}; use hashbrown::{HashMap, HashSet}; use revm::{ interpreter::{opcode, Interpreter}, @@ -26,23 +25,18 @@ impl AccessListTracer { access_list: access_list .0 .iter() - .map(|v| { - ( - v.address.to_alloy(), - v.storage_keys.iter().copied().map(|v| v.to_alloy()).collect(), - ) - }) + .map(|v| (v.address, v.storage_keys.iter().copied().map(|k| k.into()).collect())) .collect(), } } pub fn access_list(&self) -> AccessList { - AccessList::from( + AccessList( self.access_list .iter() .map(|(address, slots)| AccessListItem { - address: address.to_ethers(), - storage_keys: slots.iter().copied().map(|k| k.to_ethers()).collect(), + address: *address, + storage_keys: slots.iter().copied().map(|k| k.into()).collect(), }) .collect::>(), ) From c78710f07ff5d83fc328be9d5759c48cb9b4f437 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Fri, 12 Jan 2024 11:02:04 -0400 Subject: [PATCH 2/3] chore: remove unneeded conv func --- .../core/src/eth/transaction/ethers_compat.rs | 24 ------------------- crates/anvil/core/src/eth/transaction/mod.rs | 1 - 2 files changed, 25 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/ethers_compat.rs b/crates/anvil/core/src/eth/transaction/ethers_compat.rs index 64e60151670f..88e87a9d7cde 100644 --- a/crates/anvil/core/src/eth/transaction/ethers_compat.rs +++ b/crates/anvil/core/src/eth/transaction/ethers_compat.rs @@ -11,7 +11,6 @@ use crate::eth::{ }; use alloy_primitives::{U128 as rU128, U256 as rU256, U64 as rU64}; use alloy_rpc_types::{ - transaction::request::TransactionRequest as AlloyTransactionRequest, AccessList as AlloyAccessList, CallRequest, Signature, Transaction as AlloyTransaction, }; use ethers_core::types::{ @@ -47,29 +46,6 @@ pub fn to_alloy_storage_proof(proof: &StorageProof) -> alloy_rpc_types::EIP1186S } } -pub fn to_internal_tx_request(request: &AlloyTransactionRequest) -> EthTransactionRequest { - EthTransactionRequest { - from: request.from.map(|a| a.to_ethers()), - to: request.to.map(|a| a.to_ethers()), - gas_price: request.gas_price.map(|g| alloy_primitives::U256::from(g).to_ethers()), - max_fee_per_gas: request - .max_fee_per_gas - .map(|g| alloy_primitives::U256::from(g).to_ethers()), - max_priority_fee_per_gas: request - .max_priority_fee_per_gas - .map(|g| alloy_primitives::U256::from(g).to_ethers()), - gas: request.gas.map(|g| g.to_ethers()), - value: request.value.map(|v| v.to_ethers()), - data: request.data.clone().map(|b| b.clone().0.into()), - nonce: request.nonce.map(|n| n.to::().into()), - chain_id: None, - access_list: request.access_list.clone().map(|a| to_ethers_access_list(a.clone()).0), - transaction_type: request.transaction_type.map(|t| t.to::().into()), - // TODO: Should this be none? - optimism_fields: None, - } -} - pub fn call_to_internal_tx_request(request: &CallRequest) -> EthTransactionRequest { EthTransactionRequest { from: request.from.map(|a| a.to_ethers()), diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 3fcedd7f6638..ef67a4667d71 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -27,7 +27,6 @@ mod ethers_compat; pub use ethers_compat::{ call_to_internal_tx_request, from_ethers_access_list, to_alloy_proof, to_ethers_access_list, - to_internal_tx_request, }; /// The signature used to bypass signing via the `eth_sendUnsignedTransaction` cheat RPC From 1255130623c2fd7e7f594f40f6e47fbb7925ae5a Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Fri, 12 Jan 2024 12:26:11 -0400 Subject: [PATCH 3/3] chore: remove unnecesary clone --- crates/anvil/src/eth/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 847511b74804..c90bdc3bc912 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2302,7 +2302,7 @@ impl EthApi { let gas: U256 = U256::from(gas); // Get the starting lowest gas needed depending on the transaction kind. let mut lowest_gas_limit = - determine_base_gas_by_kind(call_to_internal_tx_request(&request.clone())); + determine_base_gas_by_kind(call_to_internal_tx_request(&request)); // pick a point that's close to the estimated gas let mut mid_gas_limit = std::cmp::min(