Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions crates/handler/src/pre_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,28 +146,31 @@ pub fn validate_against_state_and_deduct_caller<

let max_balance_spending = tx.max_balance_spending()?;

let mut new_balance = caller_account.info.balance;

// Check if account has enough balance for `gas_limit * max_fee`` and value transfer.
// Transfer will be done inside `*_inner` functions.
if is_balance_check_disabled {
// Make sure the caller's balance is at least the value of the transaction.
new_balance = caller_account.info.balance.max(tx.value());
} else if max_balance_spending > caller_account.info.balance {
if max_balance_spending > caller_account.info.balance && !is_balance_check_disabled {
return Err(InvalidTransaction::LackOfFundForMaxFee {
fee: Box::new(max_balance_spending),
balance: Box::new(caller_account.info.balance),
}
.into());
} else {
let effective_balance_spending = tx
.effective_balance_spending(basefee, blob_price)
.expect("effective balance is always smaller than max balance so it can't overflow");
}

// subtracting max balance spending with value that is going to be deducted later in the call.
let gas_balance_spending = effective_balance_spending - tx.value();
let effective_balance_spending = tx
.effective_balance_spending(basefee, blob_price)
.expect("effective balance is always smaller than max balance so it can't overflow");

new_balance = new_balance.saturating_sub(gas_balance_spending);
// subtracting max balance spending with value that is going to be deducted later in the call.
let gas_balance_spending = effective_balance_spending - tx.value();

let mut new_balance = caller_account
.info
.balance
.saturating_sub(gas_balance_spending);

if is_balance_check_disabled {
// Make sure the caller's balance is at least the value of the transaction.
new_balance = new_balance.max(tx.value());
}

let old_balance = caller_account.info.balance;
Expand Down
39 changes: 20 additions & 19 deletions crates/op-revm/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,35 +174,36 @@ where

// Check if account has enough balance for `gas_limit * max_fee`` and value transfer.
// Transfer will be done inside `*_inner` functions.
if is_balance_check_disabled {
// Make sure the caller's balance is at least the value of the transaction.
// this is not consensus critical, and it is used in testing.
new_balance = caller_account.info.balance.max(tx.value());
} else if !is_deposit && max_balance_spending > new_balance {
if !is_deposit && max_balance_spending > new_balance && !is_balance_check_disabled {
// skip max balance check for deposit transactions.
// this check for deposit was skipped previously in `validate_tx_against_state` function
return Err(InvalidTransaction::LackOfFundForMaxFee {
fee: Box::new(max_balance_spending),
balance: Box::new(new_balance),
}
.into());
} else {
let effective_balance_spending =
tx.effective_balance_spending(basefee, blob_price).expect(
"effective balance is always smaller than max balance so it can't overflow",
);
}

// subtracting max balance spending with value that is going to be deducted later in the call.
let gas_balance_spending = effective_balance_spending - tx.value();
let effective_balance_spending = tx
.effective_balance_spending(basefee, blob_price)
.expect("effective balance is always smaller than max balance so it can't overflow");

// If the transaction is not a deposit transaction, subtract the L1 data fee from the
// caller's balance directly after minting the requested amount of ETH.
// Additionally deduct the operator fee from the caller's account.
//
// In case of deposit additional cost will be zero.
let op_gas_balance_spending = gas_balance_spending.saturating_add(additional_cost);
// subtracting max balance spending with value that is going to be deducted later in the call.
let gas_balance_spending = effective_balance_spending - tx.value();

new_balance = new_balance.saturating_sub(op_gas_balance_spending);
// If the transaction is not a deposit transaction, subtract the L1 data fee from the
// caller's balance directly after minting the requested amount of ETH.
// Additionally deduct the operator fee from the caller's account.
//
// In case of deposit additional cost will be zero.
let op_gas_balance_spending = gas_balance_spending.saturating_add(additional_cost);

new_balance = new_balance.saturating_sub(op_gas_balance_spending);

if is_balance_check_disabled {
// Make sure the caller's balance is at least the value of the transaction.
// this is not consensus critical, and it is used in testing.
new_balance = new_balance.max(tx.value());
}

// Touch account so we know it is changed.
Expand Down
49 changes: 49 additions & 0 deletions crates/revm/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,52 @@ fn test_frame_stack_index() {
assert_eq!(evm.frame_stack.index(), None);
compare_or_save_testdata("test_frame_stack_index.json", result1);
}

#[test]
#[cfg(feature = "optional_balance_check")]
fn test_disable_balance_check() {
use database::BENCH_CALLER_BALANCE;

const RETURN_CALLER_BALANCE_BYTECODE: &[u8] = &[
opcode::CALLER,
opcode::BALANCE,
opcode::PUSH1,
0x00,
opcode::MSTORE,
opcode::PUSH1,
0x20,
opcode::PUSH1,
0x00,
opcode::RETURN,
];

let mut evm = Context::mainnet()
.modify_cfg_chained(|cfg| cfg.disable_balance_check = true)
.with_db(BenchmarkDB::new_bytecode(Bytecode::new_legacy(
RETURN_CALLER_BALANCE_BYTECODE.into(),
)))
.build_mainnet();

// Construct tx so that effective cost is more than caller balance.
let gas_price = 1;
let gas_limit = 100_000;
// Make sure value doesn't consume all balance since we want to validate that all effective
// cost is deducted.
let tx_value = BENCH_CALLER_BALANCE - U256::from(1);

let result = evm
.transact_one(
TxEnv::builder_for_bench()
.gas_price(gas_price)
.gas_limit(gas_limit)
.value(tx_value)
.build_fill(),
)
.unwrap();

assert!(result.is_success());

let returned_balance = U256::from_be_slice(result.output().unwrap().as_ref());
let expected_balance = U256::ZERO;
assert_eq!(returned_balance, expected_balance);
}
Loading