mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-13 11:36:37 +00:00
core/vm: refactor calls gas calculation to exit early if there isn't enough gas to cover cost component that doesn't depend on state lookups
This commit is contained in:
parent
54d9e4ff40
commit
18fa37c3b5
2 changed files with 32 additions and 109 deletions
|
|
@ -430,7 +430,6 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
||||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
|
@ -27,9 +28,6 @@ import (
|
||||||
|
|
||||||
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
if evm.readOnly {
|
|
||||||
return 0, ErrWriteProtection
|
|
||||||
}
|
|
||||||
// If we fail the minimum gas availability invariant, fail (0)
|
// If we fail the minimum gas availability invariant, fail (0)
|
||||||
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
||||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||||
|
|
@ -154,80 +152,12 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCallVariantGasCallEIP2929(oldCalculatorStateful, oldCalculatorStateless gasFunc, addressPosition int) gasFunc {
|
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, 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
|
|
||||||
var (
|
|
||||||
eip2929Cost uint64
|
|
||||||
overflow bool
|
|
||||||
)
|
|
||||||
if !warmAccess {
|
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
|
||||||
// Charge the remaining difference here already, to correctly calculate available
|
|
||||||
// gas for call
|
|
||||||
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
|
||||||
return 0, ErrOutOfGas
|
|
||||||
}
|
|
||||||
eip2929Cost = coldCost
|
|
||||||
}
|
|
||||||
// Now call the old calculator, which takes into account
|
|
||||||
// - create new account
|
|
||||||
// - transfer value
|
|
||||||
// - memory expansion
|
|
||||||
// - 63/64ths rule
|
|
||||||
|
|
||||||
eip150BaseGas, err := oldCalculatorStateless(evm, contract, stack, mem, memorySize)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure the portion of the call cost which doesn't depend on state lookups
|
|
||||||
// is covered by the provided gas
|
|
||||||
if contract.Gas < eip150BaseGas {
|
|
||||||
return 0, ErrOutOfGas
|
|
||||||
}
|
|
||||||
|
|
||||||
oldStateful, err := oldCalculatorStateful(evm, contract, stack, mem, memorySize)
|
|
||||||
if err != nil {
|
|
||||||
return oldStateful, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if eip150BaseGas, overflow = math.SafeAdd(eip150BaseGas, oldStateful); overflow {
|
|
||||||
return 0, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, eip150BaseGas, stack.Back(0))
|
|
||||||
if err != nil {
|
|
||||||
return 0, 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 += eip2929Cost
|
|
||||||
var total uint64
|
|
||||||
if total, overflow = math.SafeAdd(evm.callGasTemp, eip150BaseGas); overflow {
|
|
||||||
return 0, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
if total, overflow = math.SafeAdd(total, eip2929Cost); overflow {
|
|
||||||
return 0, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
|
|
||||||
return total, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCallStateless, gasCallStateful, 1)
|
// TODO: we can use the same functions already defined above for the 7702 gas handlers
|
||||||
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCallStateless, gasDelegateCallStateful, 1)
|
gasCallEIP2929 = makeCallVariantGasCall(gasCallStateless, gasCallStateful)
|
||||||
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCallStateless, gasStaticCallStateful, 1)
|
gasDelegateCallEIP2929 = makeCallVariantGasCall(gasDelegateCallStateless, gasDelegateCallStateful)
|
||||||
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCodeStateless, gasCallCodeStateful, 1)
|
gasStaticCallEIP2929 = makeCallVariantGasCall(gasStaticCallStateless, gasStaticCallStateful)
|
||||||
|
gasCallCodeEIP2929 = makeCallVariantGasCall(gasCallCodeStateless, gasCallCodeStateful)
|
||||||
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
||||||
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
|
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
|
||||||
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
||||||
|
|
@ -258,17 +188,11 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||||
gas uint64
|
gas uint64
|
||||||
address = common.Address(stack.peek().Bytes20())
|
address = common.Address(stack.peek().Bytes20())
|
||||||
)
|
)
|
||||||
if evm.readOnly {
|
|
||||||
return 0, ErrWriteProtection
|
|
||||||
}
|
|
||||||
if !evm.StateDB.AddressInAccessList(address) {
|
if !evm.StateDB.AddressInAccessList(address) {
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
evm.StateDB.AddAddressToAccessList(address)
|
evm.StateDB.AddAddressToAccessList(address)
|
||||||
gas = params.ColdAccountAccessCostEIP2929
|
gas = params.ColdAccountAccessCostEIP2929
|
||||||
}
|
}
|
||||||
if contract.Gas < gas {
|
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
// if empty and transfers value
|
// if empty and transfers value
|
||||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||||
gas += params.CreateBySelfdestructGas
|
gas += params.CreateBySelfdestructGas
|
||||||
|
|
@ -282,13 +206,13 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCallStateful, gasCallStateless)
|
gasCallEIP7702 = makeCallVariantGasCall(gasCallStateful, gasCallStateless)
|
||||||
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCallStateful, gasDelegateCallStateless)
|
gasDelegateCallEIP7702 = makeCallVariantGasCall(gasDelegateCallStateful, gasDelegateCallStateless)
|
||||||
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallStateful, gasStaticCallStateless)
|
gasStaticCallEIP7702 = makeCallVariantGasCall(gasStaticCallStateful, gasStaticCallStateless)
|
||||||
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeStateful, gasCallCodeStateless)
|
gasCallCodeEIP7702 = makeCallVariantGasCall(gasCallCodeStateful, gasCallCodeStateless)
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeCallVariantGasCallEIP7702(oldCalculatorStateful, oldCalculatorStateless gasFunc) gasFunc {
|
func makeCallVariantGasCall(oldCalculatorStateful, oldCalculatorStateless gasFunc) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
var (
|
var (
|
||||||
eip150BaseGas uint64 // gas used for memory expansion, transfer costs -> input to the 63/64 bounding
|
eip150BaseGas uint64 // gas used for memory expansion, transfer costs -> input to the 63/64 bounding
|
||||||
|
|
@ -300,7 +224,7 @@ func makeCallVariantGasCallEIP7702(oldCalculatorStateful, oldCalculatorStateless
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if !evm.StateDB.AddressInAccessList(addr) {
|
if evm.chainRules.IsEIP2929 && !evm.StateDB.AddressInAccessList(addr) {
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
// 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
|
// the cost to charge for cold access, if any, is Cold - Warm
|
||||||
|
|
@ -323,27 +247,35 @@ func makeCallVariantGasCallEIP7702(oldCalculatorStateful, oldCalculatorStateless
|
||||||
return 0, ErrOutOfGas
|
return 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
// ^ TODO: I'm not totally sure this is compatible with the 63/64 gas reduction rule
|
|
||||||
|
|
||||||
oldStateful, err := oldCalculatorStateful(evm, contract, stack, mem, memorySize)
|
oldStateful, err := oldCalculatorStateful(evm, contract, stack, mem, memorySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return oldStateful, err
|
return oldStateful, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this should cause BAL test failures if uncommented
|
||||||
|
baseCost, overflow := math.SafeAdd(eip150BaseGas, oldStateful)
|
||||||
|
if overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
} else if contract.Gas < baseCost {
|
||||||
|
return 0, ErrOutOfGas
|
||||||
|
}
|
||||||
|
|
||||||
if eip150BaseGas, overflow = math.SafeAdd(eip150BaseGas, oldStateful); overflow {
|
if eip150BaseGas, overflow = math.SafeAdd(eip150BaseGas, oldStateful); overflow {
|
||||||
return 0, ErrOutOfGas
|
return 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if code is a delegation and if so, charge for resolution.
|
if evm.chainRules.IsPrague {
|
||||||
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
// Check if code is a delegation and if so, charge for resolution.
|
||||||
if evm.StateDB.AddressInAccessList(target) {
|
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||||
eip7702Gas = params.WarmStorageReadCostEIP2929
|
if evm.StateDB.AddressInAccessList(target) {
|
||||||
} else {
|
eip7702Gas = params.WarmStorageReadCostEIP2929
|
||||||
evm.StateDB.AddAddressToAccessList(target)
|
} else {
|
||||||
eip7702Gas = params.ColdAccountAccessCostEIP2929
|
evm.StateDB.AddAddressToAccessList(target)
|
||||||
}
|
eip7702Gas = params.ColdAccountAccessCostEIP2929
|
||||||
if !contract.UseGas(eip7702Gas, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
}
|
||||||
return 0, ErrOutOfGas
|
if !contract.UseGas(eip7702Gas, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
|
return 0, ErrOutOfGas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,13 +284,6 @@ func makeCallVariantGasCallEIP7702(oldCalculatorStateful, oldCalculatorStateless
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this check is probably not necessary (?)
|
|
||||||
/*
|
|
||||||
if contract.Gas < evm.callGasTemp {
|
|
||||||
return 0, ErrOutOfGas
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO: it's not clear what happens if there is enough gas to cover the stateless component
|
// TODO: it's not clear what happens if there is enough gas to cover the stateless component
|
||||||
// but not enough to cover the whole call: do all the state reads happen in this case, and
|
// but not enough to cover the whole call: do all the state reads happen in this case, and
|
||||||
// we fail at the very end?
|
// we fail at the very end?
|
||||||
|
|
@ -385,7 +310,6 @@ func makeCallVariantGasCallEIP7702(oldCalculatorStateful, oldCalculatorStateless
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
totalCost, overflow = math.SafeAdd(totalCost, eip150BaseGas)
|
totalCost, overflow = math.SafeAdd(totalCost, eip150BaseGas)
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue