From cf2a2dbf97f19e2c484a3397ff7ba50dac8aa2fe Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 28 Mar 2023 18:33:56 +0200 Subject: [PATCH] refactor(rpc): extract prepare_call_env logic (#2012) --- crates/rpc/rpc/src/eth/api/call.rs | 135 ++------------------------- crates/rpc/rpc/src/eth/api/server.rs | 2 +- crates/rpc/rpc/src/eth/revm_utils.rs | 122 +++++++++++++++++++++++- 3 files changed, 130 insertions(+), 129 deletions(-) diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index 44db47a313..b8c09e5e6d 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -2,33 +2,27 @@ use crate::{ eth::{ - error::{EthApiError, EthResult, InvalidTransactionError, RevertError}, + error::{EthResult, InvalidTransactionError, RevertError}, revm_utils::{ build_call_evm_env, cap_tx_gas_limit_with_caller_allowance, get_precompiles, inspect, - transact, + prepare_call_env, transact, }, EthTransactions, }, EthApi, }; use ethers_core::utils::get_contract_address; -use reth_primitives::{AccessList, Address, BlockId, BlockNumberOrTag, U256}; +use reth_primitives::{AccessList, BlockId, BlockNumberOrTag, U256}; use reth_provider::{BlockProvider, EvmEnvProvider, StateProvider, StateProviderFactory}; use reth_revm::{ access_list::AccessListInspector, database::{State, SubState}, }; -use reth_rpc_types::{ - state::{AccountOverride, StateOverride}, - CallRequest, -}; +use reth_rpc_types::{state::StateOverride, CallRequest}; use reth_transaction_pool::TransactionPool; use revm::{ - db::{CacheDB, DatabaseRef}, - primitives::{ - BlockEnv, Bytecode, CfgEnv, Env, ExecutionResult, Halt, ResultAndState, TransactTo, - }, - Database, + db::DatabaseRef, + primitives::{BlockEnv, CfgEnv, Env, ExecutionResult, Halt, ResultAndState, TransactTo}, }; use tracing::trace; @@ -43,7 +37,7 @@ where Network: Send + Sync + 'static, { /// Executes the call request at the given [BlockId] - pub(crate) async fn execute_call_at( + pub(crate) async fn transact_call_at( &self, request: CallRequest, at: BlockId, @@ -51,53 +45,9 @@ where ) -> EthResult<(ResultAndState, Env)> { let (cfg, block_env, at) = self.evm_env_at(at).await?; let state = self.state_at(at)?; - self.call_with(cfg, block_env, request, state, state_overrides) - } - - /// Executes the call request using the given environment against the state provider - /// - /// Does not commit any changes to the database - fn call_with( - &self, - mut cfg: CfgEnv, - block: BlockEnv, - request: CallRequest, - state: S, - state_overrides: Option, - ) -> EthResult<(ResultAndState, Env)> - where - S: StateProvider, - { - // we want to disable this in eth_call, since this is common practice used by other node - // impls and providers - cfg.disable_block_gas_limit = true; - - // Disabled because eth_call is sometimes used with eoa senders - // See - cfg.disable_eip3607 = true; - - let request_gas = request.gas; - - let mut env = build_call_evm_env(cfg, block, request)?; - let mut db = SubState::new(State::new(state)); - // apply state overrides - if let Some(state_overrides) = state_overrides { - apply_state_overrides(state_overrides, &mut db)?; - } - - // The basefee should be ignored for eth_call - // See: - // - env.block.basefee = U256::ZERO; - - if request_gas.is_none() && env.tx.gas_price > U256::ZERO { - trace!(target: "rpc::eth::call", ?env, "Applying gas limit cap"); - // no gas limit was provided in the request, so we need to cap the request's gas limit - cap_tx_gas_limit_with_caller_allowance(&mut db, &mut env.tx)?; - } - + let env = prepare_call_env(cfg, block_env, request, &mut db, state_overrides)?; trace!(target: "rpc::eth::call", ?env, "Executing call"); transact(&mut db, env) } @@ -335,72 +285,3 @@ where Ok(inspector.into_access_list()) } } - -/// Applies the given state overrides (a set of [AccountOverride]) to the [CacheDB]. -fn apply_state_overrides(overrides: StateOverride, db: &mut CacheDB) -> EthResult<()> -where - DB: DatabaseRef, - EthApiError: From<::Error>, -{ - for (account, account_overrides) in overrides { - apply_account_override(account, account_overrides, db)?; - } - Ok(()) -} - -/// Applies a single [AccountOverride] to the [CacheDB]. -fn apply_account_override( - account: Address, - account_override: AccountOverride, - db: &mut CacheDB, -) -> EthResult<()> -where - DB: DatabaseRef, - EthApiError: From<::Error>, -{ - let mut account_info = db.basic(account)?.unwrap_or_default(); - - if let Some(nonce) = account_override.nonce { - account_info.nonce = nonce.as_u64(); - } - if let Some(code) = account_override.code { - account_info.code = Some(Bytecode::new_raw(code.0)); - } - if let Some(balance) = account_override.balance { - account_info.balance = balance; - } - - db.insert_account_info(account, account_info); - - // We ensure that not both state and state_diff are set. - // If state is set, we must mark the account as "NewlyCreated", so that the old storage - // isn't read from - match (account_override.state, account_override.state_diff) { - (Some(_), Some(_)) => return Err(EthApiError::BothStateAndStateDiffInOverride(account)), - (None, None) => { - // nothing to do - } - (Some(new_account_state), None) => { - db.replace_account_storage( - account, - new_account_state - .into_iter() - .map(|(slot, value)| { - (U256::from_be_bytes(slot.0), U256::from_be_bytes(value.0)) - }) - .collect(), - )?; - } - (None, Some(account_state_diff)) => { - for (slot, value) in account_state_diff { - db.insert_account_storage( - account, - U256::from_be_bytes(slot.0), - U256::from_be_bytes(value.0), - )?; - } - } - }; - - Ok(()) -} diff --git a/crates/rpc/rpc/src/eth/api/server.rs b/crates/rpc/rpc/src/eth/api/server.rs index 299d1fdbee..eba99f95d2 100644 --- a/crates/rpc/rpc/src/eth/api/server.rs +++ b/crates/rpc/rpc/src/eth/api/server.rs @@ -208,7 +208,7 @@ where ) -> Result { trace!(target: "rpc::eth", ?request, ?block_number, ?state_overrides, "Serving eth_call"); let (res, _env) = self - .execute_call_at( + .transact_call_at( request, block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), state_overrides, diff --git a/crates/rpc/rpc/src/eth/revm_utils.rs b/crates/rpc/rpc/src/eth/revm_utils.rs index a21942efc6..ed919a1e5e 100644 --- a/crates/rpc/rpc/src/eth/revm_utils.rs +++ b/crates/rpc/rpc/src/eth/revm_utils.rs @@ -2,12 +2,18 @@ use crate::eth::error::{EthApiError, EthResult, InvalidTransactionError}; use reth_primitives::{AccessList, Address, U256}; -use reth_rpc_types::CallRequest; +use reth_rpc_types::{ + state::{AccountOverride, StateOverride}, + CallRequest, +}; use revm::{ + db::CacheDB, precompile::{Precompiles, SpecId as PrecompilesSpecId}, primitives::{BlockEnv, CfgEnv, Env, ResultAndState, SpecId, TransactTo, TxEnv}, Database, Inspector, }; +use revm_primitives::{db::DatabaseRef, Bytecode}; +use tracing::trace; /// Returns the addresses of the precompiles corresponding to the SpecId. pub(crate) fn get_precompiles(spec_id: &SpecId) -> Vec { @@ -57,6 +63,51 @@ where Ok((res, evm.env)) } +/// Prepares the [Env] for execution. +/// +/// Does not commit any changes to the underlying database. +pub(crate) fn prepare_call_env( + mut cfg: CfgEnv, + block: BlockEnv, + request: CallRequest, + db: &mut CacheDB, + state_overrides: Option, +) -> EthResult +where + DB: DatabaseRef, + EthApiError: From<::Error>, +{ + // we want to disable this in eth_call, since this is common practice used by other node + // impls and providers + cfg.disable_block_gas_limit = true; + + // Disabled because eth_call is sometimes used with eoa senders + // See + cfg.disable_eip3607 = true; + + let request_gas = request.gas; + + let mut env = build_call_evm_env(cfg, block, request)?; + + // apply state overrides + if let Some(state_overrides) = state_overrides { + apply_state_overrides(state_overrides, db)?; + } + + // The basefee should be ignored for eth_call + // See: + // + env.block.basefee = U256::ZERO; + + if request_gas.is_none() && env.tx.gas_price > U256::ZERO { + trace!(target: "rpc::eth::call", ?env, "Applying gas limit cap"); + // no gas limit was provided in the request, so we need to cap the request's gas limit + cap_tx_gas_limit_with_caller_allowance(db, &mut env.tx)?; + } + + Ok(env) +} + /// Creates a new [Env] to be used for executing the [CallRequest] in `eth_call`. /// /// Note: this does _not_ access the Database to check the sender. @@ -197,3 +248,72 @@ impl CallFees { } } } + +/// Applies the given state overrides (a set of [AccountOverride]) to the [CacheDB]. +fn apply_state_overrides(overrides: StateOverride, db: &mut CacheDB) -> EthResult<()> +where + DB: DatabaseRef, + EthApiError: From<::Error>, +{ + for (account, account_overrides) in overrides { + apply_account_override(account, account_overrides, db)?; + } + Ok(()) +} + +/// Applies a single [AccountOverride] to the [CacheDB]. +fn apply_account_override( + account: Address, + account_override: AccountOverride, + db: &mut CacheDB, +) -> EthResult<()> +where + DB: DatabaseRef, + EthApiError: From<::Error>, +{ + let mut account_info = db.basic(account)?.unwrap_or_default(); + + if let Some(nonce) = account_override.nonce { + account_info.nonce = nonce.as_u64(); + } + if let Some(code) = account_override.code { + account_info.code = Some(Bytecode::new_raw(code.0)); + } + if let Some(balance) = account_override.balance { + account_info.balance = balance; + } + + db.insert_account_info(account, account_info); + + // We ensure that not both state and state_diff are set. + // If state is set, we must mark the account as "NewlyCreated", so that the old storage + // isn't read from + match (account_override.state, account_override.state_diff) { + (Some(_), Some(_)) => return Err(EthApiError::BothStateAndStateDiffInOverride(account)), + (None, None) => { + // nothing to do + } + (Some(new_account_state), None) => { + db.replace_account_storage( + account, + new_account_state + .into_iter() + .map(|(slot, value)| { + (U256::from_be_bytes(slot.0), U256::from_be_bytes(value.0)) + }) + .collect(), + )?; + } + (None, Some(account_state_diff)) => { + for (slot, value) in account_state_diff { + db.insert_account_storage( + account, + U256::from_be_bytes(slot.0), + U256::from_be_bytes(value.0), + )?; + } + } + }; + + Ok(()) +}