Skip to content

Commit

Permalink
Integrate Request evm env filling in ethapi (#9358)
Browse files Browse the repository at this point in the history
Co-authored-by: Emilia Hane <[email protected]>
  • Loading branch information
nkysg and emhane authored Jul 9, 2024
1 parent e97e379 commit 2fa37b1
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 178 deletions.
173 changes: 166 additions & 7 deletions crates/rpc/rpc-eth-api/src/helpers/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use reth_evm::{ConfigureEvm, ConfigureEvmEnv};
use reth_primitives::{
revm_primitives::{
BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, HaltReason,
ResultAndState, TransactTo,
ResultAndState, TransactTo, TxEnv,
},
Bytes, TransactionSignedEcRecovered, TxKind, B256, U256,
};
Expand All @@ -16,8 +16,8 @@ use reth_rpc_eth_types::{
cache::db::{StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper},
error::ensure_success,
revm_utils::{
apply_state_overrides, build_call_evm_env, caller_gas_allowance,
cap_tx_gas_limit_with_caller_allowance, get_precompiles, prepare_call_env,
apply_block_overrides, apply_state_overrides, caller_gas_allowance,
cap_tx_gas_limit_with_caller_allowance, get_precompiles, CallFees,
},
EthApiError, EthResult, RevertError, RpcInvalidTransactionError, StateCacheDb,
};
Expand All @@ -29,6 +29,8 @@ use reth_rpc_types::{
};
use revm::{Database, DatabaseCommit};
use revm_inspectors::access_list::AccessListInspector;
#[cfg(feature = "optimism")]
use revm_primitives::OptimismFields;
use tracing::trace;

use super::{LoadBlock, LoadPendingBlock, LoadState, LoadTransaction, SpawnBlocking, Trace};
Expand Down Expand Up @@ -136,7 +138,7 @@ pub trait EthCall: Call + LoadPendingBlock {
let state_overrides = state_override.take();
let overrides = EvmOverrides::new(state_overrides, block_overrides.clone());

let env = prepare_call_env(
let env = this.prepare_call_env(
cfg.clone(),
block_env.clone(),
tx,
Expand Down Expand Up @@ -206,7 +208,7 @@ pub trait EthCall: Call + LoadPendingBlock {
{
let state = self.state_at_block_id(at)?;

let mut env = build_call_evm_env(cfg, block, request.clone())?;
let mut env = self.build_call_evm_env(cfg, block, request.clone())?;

// we want to disable this in eth_createAccessList, since this is common practice used by
// other node impls and providers <https://github.com/foundry-rs/foundry/issues/4388>
Expand Down Expand Up @@ -359,7 +361,7 @@ pub trait Call: LoadState + SpawnBlocking {
let mut db =
CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state)));

let env = prepare_call_env(
let env = this.prepare_call_env(
cfg,
block_env,
request,
Expand Down Expand Up @@ -530,7 +532,7 @@ pub trait Call: LoadState + SpawnBlocking {
.unwrap_or(block_env_gas_limit);

// Configure the evm env
let mut env = build_call_evm_env(cfg, block, request)?;
let mut env = self.build_call_evm_env(cfg, block, request)?;
let mut db = CacheDB::new(StateProviderDatabase::new(state));

// Apply any state overrides if specified.
Expand Down Expand Up @@ -776,4 +778,161 @@ pub trait Call: LoadState + SpawnBlocking {
}
}
}

/// Configures a new [`TxEnv`] for the [`TransactionRequest`]
///
/// All [`TxEnv`] fields are derived from the given [`TransactionRequest`], if fields are
/// `None`, they fall back to the [`BlockEnv`]'s settings.
fn create_txn_env(
&self,
block_env: &BlockEnv,
request: TransactionRequest,
) -> EthResult<TxEnv> {
// Ensure that if versioned hashes are set, they're not empty
if request.blob_versioned_hashes.as_ref().map_or(false, |hashes| hashes.is_empty()) {
return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into())
}

let TransactionRequest {
from,
to,
gas_price,
max_fee_per_gas,
max_priority_fee_per_gas,
gas,
value,
input,
nonce,
access_list,
chain_id,
blob_versioned_hashes,
max_fee_per_blob_gas,
..
} = request;

let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } =
CallFees::ensure_fees(
gas_price.map(U256::from),
max_fee_per_gas.map(U256::from),
max_priority_fee_per_gas.map(U256::from),
block_env.basefee,
blob_versioned_hashes.as_deref(),
max_fee_per_blob_gas.map(U256::from),
block_env.get_blob_gasprice().map(U256::from),
)?;

let gas_limit = gas.unwrap_or_else(|| block_env.gas_limit.min(U256::from(u64::MAX)).to());
let env = TxEnv {
gas_limit: gas_limit
.try_into()
.map_err(|_| RpcInvalidTransactionError::GasUintOverflow)?,
nonce,
caller: from.unwrap_or_default(),
gas_price,
gas_priority_fee: max_priority_fee_per_gas,
transact_to: to.unwrap_or(TxKind::Create),
value: value.unwrap_or_default(),
data: input.try_into_unique_input()?.unwrap_or_default(),
chain_id,
access_list: access_list.unwrap_or_default().into(),
// EIP-4844 fields
blob_hashes: blob_versioned_hashes.unwrap_or_default(),
max_fee_per_blob_gas,
authorization_list: None,
#[cfg(feature = "optimism")]
optimism: OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() },
};

Ok(env)
}

/// Creates a new [`EnvWithHandlerCfg`] to be used for executing the [`TransactionRequest`] in
/// `eth_call`.
///
/// Note: this does _not_ access the Database to check the sender.
fn build_call_evm_env(
&self,
cfg: CfgEnvWithHandlerCfg,
block: BlockEnv,
request: TransactionRequest,
) -> EthResult<EnvWithHandlerCfg> {
let tx = self.create_txn_env(&block, request)?;
Ok(EnvWithHandlerCfg::new_with_cfg_env(cfg, block, tx))
}

/// Prepares the [`EnvWithHandlerCfg`] for execution.
///
/// Does not commit any changes to the underlying database.
///
/// EVM settings:
/// - `disable_block_gas_limit` is set to `true`
/// - `disable_eip3607` is set to `true`
/// - `disable_base_fee` is set to `true`
/// - `nonce` is set to `None`
fn prepare_call_env<DB>(
&self,
mut cfg: CfgEnvWithHandlerCfg,
mut block: BlockEnv,
request: TransactionRequest,
gas_limit: u64,
db: &mut CacheDB<DB>,
overrides: EvmOverrides,
) -> EthResult<EnvWithHandlerCfg>
where
DB: DatabaseRef,
EthApiError: From<<DB as DatabaseRef>::Error>,
{
// we want to disable this in eth_call, since this is common practice used by other node
// impls and providers <https://github.com/foundry-rs/foundry/issues/4388>
cfg.disable_block_gas_limit = true;

// Disabled because eth_call is sometimes used with eoa senders
// See <https://github.com/paradigmxyz/reth/issues/1959>
cfg.disable_eip3607 = true;

// The basefee should be ignored for eth_call
// See:
// <https://github.com/ethereum/go-ethereum/blob/ee8e83fa5f6cb261dad2ed0a7bbcde4930c41e6c/internal/ethapi/api.go#L985>
cfg.disable_base_fee = true;

// apply block overrides, we need to apply them first so that they take effect when we we
// create the evm env via `build_call_evm_env`, e.g. basefee
if let Some(mut block_overrides) = overrides.block {
if let Some(block_hashes) = block_overrides.block_hash.take() {
// override block hashes
db.block_hashes
.extend(block_hashes.into_iter().map(|(num, hash)| (U256::from(num), hash)))
}
apply_block_overrides(*block_overrides, &mut block);
}

let request_gas = request.gas;
let mut env = self.build_call_evm_env(cfg, block, request)?;
// set nonce to None so that the next nonce is used when transacting the call
env.tx.nonce = None;

// apply state overrides
if let Some(state_overrides) = overrides.state {
apply_state_overrides(state_overrides, db)?;
}

if request_gas.is_none() {
// No gas limit was provided in the request, so we need to cap the transaction gas limit
if env.tx.gas_price > U256::ZERO {
// If gas price is specified, cap transaction gas limit with caller allowance
trace!(target: "rpc::eth::call", ?env, "Applying gas limit cap with caller allowance");
cap_tx_gas_limit_with_caller_allowance(db, &mut env.tx)?;
} else {
// If no gas price is specified, use maximum allowed gas limit. The reason for this
// is that both Erigon and Geth use pre-configured gas cap even if
// it's possible to derive the gas limit from the block:
// <https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/cmd/rpcdaemon/commands/trace_adhoc.go#L956
// https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/eth/ethconfig/config.go#L94>
trace!(target: "rpc::eth::call", ?env, "Applying gas limit cap as the maximum gas limit");
env.tx.gas_limit = gas_limit;
}
}

Ok(env)
}
}
Loading

0 comments on commit 2fa37b1

Please sign in to comment.