mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-08 07:58:40 +00:00
445 lines
20 KiB
Go
445 lines
20 KiB
Go
// Copyright 2020 The go-ethereum Authors
|
||
// This file is part of the go-ethereum library.
|
||
//
|
||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||
// it under the terms of the GNU Lesser General Public License as published by
|
||
// the Free Software Foundation, either version 3 of the License, or
|
||
// (at your option) any later version.
|
||
//
|
||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
// GNU Lesser General Public License for more details.
|
||
//
|
||
// You should have received a copy of the GNU Lesser General Public License
|
||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
package vm
|
||
|
||
import (
|
||
"errors"
|
||
|
||
"github.com/ethereum/go-ethereum/common"
|
||
"github.com/ethereum/go-ethereum/common/math"
|
||
"github.com/ethereum/go-ethereum/core/tracing"
|
||
"github.com/ethereum/go-ethereum/core/types"
|
||
"github.com/ethereum/go-ethereum/params"
|
||
)
|
||
|
||
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||
if evm.readOnly {
|
||
return GasCosts{}, ErrWriteProtection
|
||
}
|
||
// If we fail the minimum gas availability invariant, fail (0)
|
||
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
|
||
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
|
||
}
|
||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||
var (
|
||
y, x = stack.Back(1), stack.peek()
|
||
slot = common.Hash(x.Bytes32())
|
||
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
|
||
cost = uint64(0)
|
||
)
|
||
// Check slot presence in the access list
|
||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||
cost = params.ColdSloadCostEIP2929
|
||
// If the caller cannot afford the cost, this change will be rolled back
|
||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||
}
|
||
value := common.Hash(y.Bytes32())
|
||
|
||
if current == value { // noop (1)
|
||
// EIP 2200 original clause:
|
||
// return params.SloadGasEIP2200, nil
|
||
return GasCosts{RegularGas: cost + params.WarmStorageReadCostEIP2929}, nil // SLOAD_GAS
|
||
}
|
||
if original == current {
|
||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||
return GasCosts{RegularGas: cost + params.SstoreSetGasEIP2200}, nil
|
||
}
|
||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||
evm.StateDB.AddRefund(clearingRefund)
|
||
}
|
||
// EIP-2200 original clause:
|
||
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||
return GasCosts{RegularGas: cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929)}, nil // write existing slot (2.1.2)
|
||
}
|
||
if original != (common.Hash{}) {
|
||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||
evm.StateDB.SubRefund(clearingRefund)
|
||
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
|
||
evm.StateDB.AddRefund(clearingRefund)
|
||
}
|
||
}
|
||
if original == value {
|
||
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
|
||
// EIP 2200 Original clause:
|
||
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
|
||
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
|
||
} else { // reset to original existing slot (2.2.2.2)
|
||
// EIP 2200 Original clause:
|
||
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
|
||
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
|
||
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
|
||
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
|
||
}
|
||
}
|
||
// EIP-2200 original clause:
|
||
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||
return GasCosts{RegularGas: cost + params.WarmStorageReadCostEIP2929}, nil // dirty update (2.2)
|
||
}
|
||
}
|
||
|
||
// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
|
||
// For SLOAD, if the (address, storage_key) pair (where address is the address of the contract
|
||
// whose storage is being read) is not yet in accessed_storage_keys,
|
||
// charge 2100 gas and add the pair to accessed_storage_keys.
|
||
// If the pair is already in accessed_storage_keys, charge 100 gas.
|
||
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||
loc := stack.peek()
|
||
slot := common.Hash(loc.Bytes32())
|
||
// Check slot presence in the access list
|
||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||
// If the caller cannot afford the cost, this change will be rolled back
|
||
// If he does afford it, we can skip checking the same thing later on, during execution
|
||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||
return GasCosts{RegularGas: params.ColdSloadCostEIP2929}, nil
|
||
}
|
||
return GasCosts{RegularGas: params.WarmStorageReadCostEIP2929}, nil
|
||
}
|
||
|
||
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
|
||
// EIP spec:
|
||
// > If the target is not in accessed_addresses,
|
||
// > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
|
||
// > Otherwise, charge WARM_STORAGE_READ_COST gas.
|
||
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||
// memory expansion first (dynamic part of pre-2929 implementation)
|
||
gasCost, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||
if err != nil {
|
||
return GasCosts{}, err
|
||
}
|
||
gas := gasCost.RegularGas
|
||
addr := common.Address(stack.peek().Bytes20())
|
||
// Check slot presence in the access list
|
||
if !evm.StateDB.AddressInAccessList(addr) {
|
||
evm.StateDB.AddAddressToAccessList(addr)
|
||
var overflow bool
|
||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||
return GasCosts{}, ErrGasUintOverflow
|
||
}
|
||
return GasCosts{RegularGas: gas}, nil
|
||
}
|
||
return GasCosts{RegularGas: gas}, nil
|
||
}
|
||
|
||
// gasEip2929AccountCheck checks whether the first stack item (as address) is present in the access list.
|
||
// If it is, this method returns '0', otherwise 'cold-warm' gas, presuming that the opcode using it
|
||
// is also using 'warm' as constant factor.
|
||
// This method is used by:
|
||
// - extcodehash,
|
||
// - extcodesize,
|
||
// - (ext) balance
|
||
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||
addr := common.Address(stack.peek().Bytes20())
|
||
// Check slot presence in the access list
|
||
if !evm.StateDB.AddressInAccessList(addr) {
|
||
// If the caller cannot afford the cost, this change will be rolled back
|
||
evm.StateDB.AddAddressToAccessList(addr)
|
||
// The warm storage read cost is already charged as constantGas
|
||
return GasCosts{RegularGas: params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929}, nil
|
||
}
|
||
return GasCosts{}, nil
|
||
}
|
||
|
||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
|
||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||
addr := common.Address(stack.Back(addressPosition).Bytes20())
|
||
// Check slot presence in the access list
|
||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
||
// the cost to charge for cold access, if any, is Cold - Warm
|
||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||
if !warmAccess {
|
||
evm.StateDB.AddAddressToAccessList(addr)
|
||
// Charge the remaining difference here already, to correctly calculate available
|
||
// gas for call
|
||
if !contract.UseGas(GasCosts{RegularGas: coldCost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||
return GasCosts{}, ErrOutOfGas
|
||
}
|
||
}
|
||
// Now call the old calculator, which takes into account
|
||
// - create new account
|
||
// - transfer value
|
||
// - memory expansion
|
||
// - 63/64ths rule
|
||
gasCost, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||
if warmAccess || err != nil {
|
||
return gasCost, err
|
||
}
|
||
// In case of a cold access, we temporarily add the cold charge back, and also
|
||
// add it to the returned gas. By adding it to the return, it will be charged
|
||
// outside of this function, as part of the dynamic gas, and that will make it
|
||
// also become correctly reported to tracers.
|
||
contract.Gas.RegularGas += coldCost
|
||
// Also undo the RegularGasUsed bookkeeping so coldCost isn't
|
||
// double-counted (it will be re-added by the dynamicGas
|
||
// mechanism via the returned cost).
|
||
contract.Gas.RegularGasUsed -= coldCost
|
||
|
||
gas := gasCost.RegularGas
|
||
var overflow bool
|
||
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
|
||
return GasCosts{}, ErrGasUintOverflow
|
||
}
|
||
return GasCosts{RegularGas: gas}, nil
|
||
}
|
||
}
|
||
|
||
var (
|
||
gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall, 1)
|
||
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall, 1)
|
||
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall, 1)
|
||
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode, 1)
|
||
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
||
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
|
||
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
||
|
||
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
|
||
//
|
||
// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
|
||
// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
|
||
// Additionally, modify the parameters defined in EIP 2200 as follows:
|
||
//
|
||
// Parameter Old value New value
|
||
// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
|
||
// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
|
||
//
|
||
//The other parameters defined in EIP 2200 are unchanged.
|
||
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
|
||
gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200)
|
||
|
||
// gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529
|
||
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
|
||
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
|
||
|
||
// gasSStoreEIP8037 implements gas cost for SSTORE under EIP-8037.
|
||
// New slot creation (orig=0, current=0, value!=0) is repriced from
|
||
// SstoreSetGas (20,000) to SstoreUpdateGas - ColdSloadCost (2,900); the
|
||
// state-gas portion (32 × CPSB) is charged at frame-end via the journal.
|
||
// Likewise the same-tx 0→X→0 reset refund is reduced from 19,900 to
|
||
// SstoreUpdateGas - ColdSloadCost - WarmStorageReadCost (2,800); the
|
||
// state-gas refund is also handled at frame-end.
|
||
gasSStoreEIP8037 = makeGasSStoreFuncAmsterdam(params.SstoreClearsScheduleRefundEIP3529)
|
||
)
|
||
|
||
// makeGasSStoreFuncAmsterdam returns the EIP-8037 SSTORE gas function. It is
|
||
// identical to makeGasSStoreFunc except that the regular-gas portion of new
|
||
// slot creation and same-tx 0→X→0 reset is reduced (the state-gas portion is
|
||
// charged/refunded at frame-end via the journal).
|
||
func makeGasSStoreFuncAmsterdam(clearingRefund uint64) gasFunc {
|
||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||
if evm.readOnly {
|
||
return GasCosts{}, ErrWriteProtection
|
||
}
|
||
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
|
||
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
|
||
}
|
||
var (
|
||
y, x = stack.Back(1), stack.peek()
|
||
slot = common.Hash(x.Bytes32())
|
||
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
|
||
cost = uint64(0)
|
||
)
|
||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||
cost = params.ColdSloadCostEIP2929
|
||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||
}
|
||
value := common.Hash(y.Bytes32())
|
||
|
||
// EIP-8037: regular-gas portion of new slot creation is the storage
|
||
// update cost minus cold sload (2,900). State-gas portion is at
|
||
// frame-end.
|
||
sstoreNewSlotRegularGas := params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929
|
||
|
||
if current == value { // noop
|
||
return GasCosts{RegularGas: cost + params.WarmStorageReadCostEIP2929}, nil
|
||
}
|
||
if original == current {
|
||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||
return GasCosts{RegularGas: cost + sstoreNewSlotRegularGas}, nil
|
||
}
|
||
if value == (common.Hash{}) { // delete pre-existing slot
|
||
evm.StateDB.AddRefund(clearingRefund)
|
||
}
|
||
return GasCosts{RegularGas: cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929)}, nil
|
||
}
|
||
if original != (common.Hash{}) {
|
||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||
evm.StateDB.SubRefund(clearingRefund)
|
||
} else if value == (common.Hash{}) { // delete dirty (2.2.1.2)
|
||
evm.StateDB.AddRefund(clearingRefund)
|
||
}
|
||
}
|
||
if original == value {
|
||
if original == (common.Hash{}) { // 0→X→0: reset to original-zero
|
||
// EIP-8037: regular-gas refund is reduced because the
|
||
// original SET cost was already reduced to
|
||
// sstoreNewSlotRegularGas. State-gas refund (32 × CPSB)
|
||
// is applied at frame-end.
|
||
evm.StateDB.AddRefund(sstoreNewSlotRegularGas - params.WarmStorageReadCostEIP2929)
|
||
} else { // reset to original existing slot
|
||
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
|
||
}
|
||
}
|
||
return GasCosts{RegularGas: cost + params.WarmStorageReadCostEIP2929}, nil
|
||
}
|
||
}
|
||
|
||
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529
|
||
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||
var (
|
||
gas uint64
|
||
address = common.Address(stack.peek().Bytes20())
|
||
)
|
||
if evm.readOnly {
|
||
return GasCosts{}, ErrWriteProtection
|
||
}
|
||
if !evm.StateDB.AddressInAccessList(address) {
|
||
// If the caller cannot afford the cost, this change will be rolled back
|
||
evm.StateDB.AddAddressToAccessList(address)
|
||
gas = params.ColdAccountAccessCostEIP2929
|
||
|
||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||
// it can effectively prevent accessing the states in the following steps
|
||
if contract.Gas.RegularGas < gas {
|
||
return GasCosts{}, ErrOutOfGas
|
||
}
|
||
}
|
||
// if empty and transfers value
|
||
// EIP-8037: under Amsterdam the regular-gas portion of
|
||
// CreateBySelfdestructGas (25,000) is removed; account creation
|
||
// is charged via state gas at frame end.
|
||
if !evm.chainRules.IsAmsterdam {
|
||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||
gas += params.CreateBySelfdestructGas
|
||
}
|
||
}
|
||
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
|
||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||
}
|
||
return GasCosts{RegularGas: gas}, nil
|
||
}
|
||
return gasFunc
|
||
}
|
||
|
||
var (
|
||
innerGasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCallIntrinsic)
|
||
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCallIntrinsic)
|
||
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic)
|
||
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic)
|
||
)
|
||
|
||
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||
// Return early if this call attempts to transfer value in a static context.
|
||
// Although it's checked in `gasCall`, EIP-7702 loads the target's code before
|
||
// to determine if it is resolving a delegation. This could incorrectly record
|
||
// the target in the block access list (BAL) if the call later fails.
|
||
transfersValue := !stack.Back(2).IsZero()
|
||
if evm.readOnly && transfersValue {
|
||
return GasCosts{}, ErrWriteProtection
|
||
}
|
||
return innerGasCallEIP7702(evm, contract, stack, mem, memorySize)
|
||
}
|
||
|
||
func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||
var (
|
||
eip2929Cost uint64
|
||
eip7702Cost uint64
|
||
addr = common.Address(stack.Back(1).Bytes20())
|
||
)
|
||
// Perform EIP-2929 checks (stateless), checking address presence
|
||
// in the accessList and charge the cold access accordingly.
|
||
if !evm.StateDB.AddressInAccessList(addr) {
|
||
evm.StateDB.AddAddressToAccessList(addr)
|
||
|
||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form
|
||
// of a constant cost, so the cost to charge for cold access, if any,
|
||
// is Cold - Warm
|
||
eip2929Cost = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||
|
||
// Charge the remaining difference here already, to correctly calculate
|
||
// available gas for call
|
||
if !contract.UseGas(GasCosts{RegularGas: eip2929Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||
return GasCosts{}, ErrOutOfGas
|
||
}
|
||
}
|
||
|
||
// Perform the intrinsic cost calculation including:
|
||
//
|
||
// - transfer value
|
||
// - memory expansion
|
||
// - create new account
|
||
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||
if err != nil {
|
||
return GasCosts{}, err
|
||
}
|
||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||
// it can effectively prevent accessing the states in the following steps.
|
||
// It's an essential safeguard before any stateful check.
|
||
if contract.Gas.RegularGas < intrinsicCost.RegularGas {
|
||
return GasCosts{}, ErrOutOfGas
|
||
}
|
||
|
||
// Check if code is a delegation and if so, charge for resolution.
|
||
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||
if evm.StateDB.AddressInAccessList(target) {
|
||
eip7702Cost = params.WarmStorageReadCostEIP2929
|
||
} else {
|
||
evm.StateDB.AddAddressToAccessList(target)
|
||
eip7702Cost = params.ColdAccountAccessCostEIP2929
|
||
}
|
||
if !contract.UseGas(GasCosts{RegularGas: eip7702Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||
return GasCosts{}, ErrOutOfGas
|
||
}
|
||
}
|
||
// Calculate the gas budget for the nested call. The costs defined by
|
||
// EIP-2929 and EIP-7702 have already been applied.
|
||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsicCost.RegularGas, stack.Back(0))
|
||
if err != nil {
|
||
return GasCosts{}, err
|
||
}
|
||
// Temporarily add the gas charge back to the contract and return value. By
|
||
// adding it to the return, it will be charged outside of this function, as
|
||
// part of the dynamic gas. This will ensure it is correctly reported to
|
||
// tracers.
|
||
contract.Gas.RegularGas += eip2929Cost + eip7702Cost
|
||
// Also undo the RegularGasUsed bookkeeping so eip2929/7702 costs aren't
|
||
// double-counted (they will be re-added by the dynamicGas mechanism via
|
||
// the returned cost).
|
||
contract.Gas.RegularGasUsed -= eip2929Cost + eip7702Cost
|
||
|
||
// Aggregate the gas costs from all components, including EIP-2929, EIP-7702,
|
||
// the CALL opcode itself, and the cost incurred by nested calls.
|
||
var (
|
||
overflow bool
|
||
totalCost uint64
|
||
)
|
||
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
|
||
return GasCosts{}, ErrGasUintOverflow
|
||
}
|
||
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost.RegularGas); overflow {
|
||
return GasCosts{}, ErrGasUintOverflow
|
||
}
|
||
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
|
||
return GasCosts{}, ErrGasUintOverflow
|
||
}
|
||
return GasCosts{RegularGas: totalCost}, nil
|
||
}
|
||
}
|