refactor(rpc): gas allowance calculation (#3304)

This commit is contained in:
Alexey Shekhirin
2023-06-21 21:16:48 +01:00
committed by GitHub
parent 44da381591
commit a4a15ab819
2 changed files with 33 additions and 30 deletions

View File

@ -4,8 +4,8 @@ use crate::{
eth::{
error::{ensure_success, EthApiError, EthResult, RevertError, RpcInvalidTransactionError},
revm_utils::{
build_call_evm_env, cap_tx_gas_limit_with_caller_allowance, get_precompiles, inspect,
transact, EvmOverrides,
build_call_evm_env, caller_gas_allowance, cap_tx_gas_limit_with_caller_allowance,
get_precompiles, inspect, transact, EvmOverrides,
},
EthTransactions,
},
@ -122,19 +122,8 @@ where
}
// check funds of the sender
let gas_price = env.tx.gas_price;
if gas_price > U256::ZERO {
let mut available_funds =
db.basic(env.tx.caller)?.map(|acc| acc.balance).unwrap_or_default();
if env.tx.value > available_funds {
return Err(RpcInvalidTransactionError::InsufficientFunds.into())
}
// subtract transferred value from available funds
// SAFETY: value < available_funds, checked above
available_funds -= env.tx.value;
// amount of gas the sender can afford with the `gas_price`
// SAFETY: gas_price not zero
let allowance = available_funds.checked_div(gas_price).unwrap_or_default();
if env.tx.gas_price > U256::ZERO {
let allowance = caller_gas_allowance(&mut db, &env.tx)?;
if highest_gas_limit > allowance {
// cap the highest gas limit by max gas caller can afford with given gas price

View File

@ -317,31 +317,45 @@ pub(crate) fn create_txn_env(block_env: &BlockEnv, request: CallRequest) -> EthR
}
/// Caps the configured [TxEnv] `gas_limit` with the allowance of the caller.
///
/// Returns an error if the caller has insufficient funds
pub(crate) fn cap_tx_gas_limit_with_caller_allowance<DB>(
mut db: DB,
env: &mut TxEnv,
) -> EthResult<()>
pub(crate) fn cap_tx_gas_limit_with_caller_allowance<DB>(db: DB, env: &mut TxEnv) -> EthResult<()>
where
DB: Database,
EthApiError: From<<DB as Database>::Error>,
{
let mut allowance = db.basic(env.caller)?.map(|acc| acc.balance).unwrap_or_default();
// subtract transferred value
allowance = allowance
.checked_sub(env.value)
.ok_or_else(|| RpcInvalidTransactionError::InsufficientFunds)?;
// cap the gas limit
if let Ok(gas_limit) = allowance.checked_div(env.gas_price).unwrap_or_default().try_into() {
if let Ok(gas_limit) = caller_gas_allowance(db, env)?.try_into() {
env.gas_limit = gas_limit;
}
Ok(())
}
/// Calculates the caller gas allowance.
///
/// `allowance = (account.balance - tx.value) / tx.gas_price`
///
/// Returns an error if the caller has insufficient funds.
/// Caution: This assumes non-zero `env.gas_price`. Otherwise, zero allowance will be returned.
pub(crate) fn caller_gas_allowance<DB>(mut db: DB, env: &TxEnv) -> EthResult<U256>
where
DB: Database,
EthApiError: From<<DB as Database>::Error>,
{
Ok(db
// Get the caller account.
.basic(env.caller)?
// Get the caller balance.
.map(|acc| acc.balance)
.unwrap_or_default()
// Subtract transferred value from the caller balance.
.checked_sub(env.value)
// Return error if the caller has insufficient funds.
.ok_or_else(|| RpcInvalidTransactionError::InsufficientFunds)?
// Calculate the amount of gas the caller can afford with the specified gas price.
.checked_div(env.gas_price)
// This will be 0 if gas price is 0. It is fine, because we check it before.
.unwrap_or_default())
}
/// Helper type for representing the fees of a [CallRequest]
pub(crate) struct CallFees {
/// EIP-1559 priority fee