fix: continue bisect on intrinsic gas error (#2048)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Dan Cline
2023-03-31 05:18:50 -04:00
committed by GitHub
parent ec7a565ef3
commit cda66470d1

View File

@@ -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(),
}
}