mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-03 03:25:01 -05:00
fix: continue bisect on intrinsic gas error (#2048)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
eth::{
|
||||
error::{EthResult, InvalidTransactionError, RevertError},
|
||||
error::{EthApiError, EthResult, InvalidTransactionError, RevertError},
|
||||
revm_utils::{
|
||||
build_call_evm_env, cap_tx_gas_limit_with_caller_allowance, get_precompiles, inspect,
|
||||
prepare_call_env, transact,
|
||||
@@ -21,7 +21,7 @@ use reth_revm::{
|
||||
use reth_rpc_types::{state::StateOverride, CallRequest};
|
||||
use reth_transaction_pool::TransactionPool;
|
||||
use revm::{
|
||||
db::DatabaseRef,
|
||||
db::{CacheDB, DatabaseRef},
|
||||
primitives::{BlockEnv, CfgEnv, Env, ExecutionResult, Halt, ResultAndState, TransactTo},
|
||||
};
|
||||
use tracing::trace;
|
||||
@@ -141,8 +141,22 @@ where
|
||||
let gas_limit = std::cmp::min(U256::from(env.tx.gas_limit), highest_gas_limit);
|
||||
env.block.gas_limit = gas_limit;
|
||||
|
||||
trace!(target: "rpc::eth::estimate", ?env, "Starting gas estimation");
|
||||
|
||||
// execute the call without writing to db
|
||||
let (res, mut env) = transact(&mut db, env)?;
|
||||
let ethres = transact(&mut db, env.clone());
|
||||
|
||||
// Exceptional case: init used too much gas, we need to increase the gas limit and try
|
||||
// again
|
||||
if let Err(EthApiError::InvalidTransaction(InvalidTransactionError::GasTooHigh)) = ethres {
|
||||
// if price or limit was included in the request then we can execute the request
|
||||
// again with the block's gas limit to check if revert is gas related or not
|
||||
if request_gas.is_some() || request_gas_price.is_some() {
|
||||
return Err(map_out_of_gas_err(env_gas_limit, env, &mut db))
|
||||
}
|
||||
}
|
||||
|
||||
let (res, env) = ethres?;
|
||||
match res.result {
|
||||
ExecutionResult::Success { .. } => {
|
||||
// succeeded
|
||||
@@ -154,24 +168,7 @@ where
|
||||
// if price or limit was included in the request then we can execute the request
|
||||
// again with the block's gas limit to check if revert is gas related or not
|
||||
return if request_gas.is_some() || request_gas_price.is_some() {
|
||||
let req_gas_limit = env.tx.gas_limit;
|
||||
env.tx.gas_limit = env_gas_limit.try_into().unwrap_or(u64::MAX);
|
||||
let (res, _) = transact(&mut db, env)?;
|
||||
match res.result {
|
||||
ExecutionResult::Success { .. } => {
|
||||
// transaction succeeded by manually increasing the gas limit to
|
||||
// highest, which means the caller lacks funds to pay for the tx
|
||||
Err(InvalidTransactionError::BasicOutOfGas(U256::from(req_gas_limit))
|
||||
.into())
|
||||
}
|
||||
ExecutionResult::Revert { .. } => {
|
||||
// reverted again after bumping the limit
|
||||
Err(InvalidTransactionError::Revert(RevertError::new(output)).into())
|
||||
}
|
||||
ExecutionResult::Halt { reason, .. } => {
|
||||
Err(InvalidTransactionError::EvmHalt(reason).into())
|
||||
}
|
||||
}
|
||||
Err(map_out_of_gas_err(env_gas_limit, env, &mut db))
|
||||
} else {
|
||||
// the transaction did revert
|
||||
Err(InvalidTransactionError::Revert(RevertError::new(output)).into())
|
||||
@@ -194,11 +191,30 @@ where
|
||||
((highest_gas_limit as u128 + lowest_gas_limit as u128) / 2) as u64,
|
||||
);
|
||||
|
||||
let _last_highest_gas_limit = highest_gas_limit;
|
||||
|
||||
trace!(target: "rpc::eth::estimate", ?env, ?highest_gas_limit, ?lowest_gas_limit, ?mid_gas_limit, "Starting binary search for gas");
|
||||
|
||||
// binary search
|
||||
while (highest_gas_limit - lowest_gas_limit) > 1 {
|
||||
let mut env = env.clone();
|
||||
env.tx.gas_limit = mid_gas_limit;
|
||||
let (res, _) = transact(&mut db, env)?;
|
||||
let ethres = transact(&mut db, env);
|
||||
|
||||
// Exceptional case: init used too much gas, we need to increase the gas limit and try
|
||||
// again
|
||||
if let Err(EthApiError::InvalidTransaction(InvalidTransactionError::GasTooHigh)) =
|
||||
ethres
|
||||
{
|
||||
// increase the lowest gas limit
|
||||
lowest_gas_limit = mid_gas_limit;
|
||||
|
||||
// new midpoint
|
||||
mid_gas_limit = ((highest_gas_limit as u128 + lowest_gas_limit as u128) / 2) as u64;
|
||||
continue
|
||||
}
|
||||
|
||||
let (res, _) = ethres?;
|
||||
match res.result {
|
||||
ExecutionResult::Success { .. } => {
|
||||
// cap the highest gas limit with succeeding gas limit
|
||||
@@ -283,3 +299,34 @@ where
|
||||
Ok(inspector.into_access_list())
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes the requests again after an out of gas error to check if the error is gas related or
|
||||
/// not
|
||||
#[inline]
|
||||
fn map_out_of_gas_err<S>(
|
||||
env_gas_limit: U256,
|
||||
mut env: Env,
|
||||
mut db: &mut CacheDB<State<S>>,
|
||||
) -> EthApiError
|
||||
where
|
||||
S: StateProvider,
|
||||
{
|
||||
let req_gas_limit = env.tx.gas_limit;
|
||||
env.tx.gas_limit = env_gas_limit.try_into().unwrap_or(u64::MAX);
|
||||
let (res, _) = match transact(&mut db, env) {
|
||||
Ok(res) => res,
|
||||
Err(err) => return err,
|
||||
};
|
||||
match res.result {
|
||||
ExecutionResult::Success { .. } => {
|
||||
// transaction succeeded by manually increasing the gas limit to
|
||||
// highest, which means the caller lacks funds to pay for the tx
|
||||
InvalidTransactionError::BasicOutOfGas(U256::from(req_gas_limit)).into()
|
||||
}
|
||||
ExecutionResult::Revert { output, .. } => {
|
||||
// reverted again after bumping the limit
|
||||
InvalidTransactionError::Revert(RevertError::new(output)).into()
|
||||
}
|
||||
ExecutionResult::Halt { reason, .. } => InvalidTransactionError::EvmHalt(reason).into(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user