mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 18:30:45 +00:00
core/vm: integrate Gary's call gas cost rework
This commit is contained in:
parent
e5d453c086
commit
97d643a677
4 changed files with 162 additions and 214 deletions
|
|
@ -606,7 +606,6 @@ func enable8037(jt *JumpTable) {
|
||||||
jt[CREATE].dynamicGas = gasCreateEip8037
|
jt[CREATE].dynamicGas = gasCreateEip8037
|
||||||
jt[CREATE2].constantGas = params.CreateGasAmsterdam
|
jt[CREATE2].constantGas = params.CreateGasAmsterdam
|
||||||
jt[CREATE2].dynamicGas = gasCreate2Eip8037
|
jt[CREATE2].dynamicGas = gasCreate2Eip8037
|
||||||
jt[CALL].dynamicGas = makeCallVariantGasCall(gasCall8037, gasCallStateless)
|
|
||||||
jt[SELFDESTRUCT].dynamicGas = gasSelfdestruct8037
|
jt[SELFDESTRUCT].dynamicGas = gasSelfdestruct8037
|
||||||
jt[SSTORE].dynamicGas = gasSStore8037
|
jt[SSTORE].dynamicGas = gasSStore8037
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,5 @@ func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (u
|
||||||
if !callCost.IsUint64() {
|
if !callCost.IsUint64() {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
return callCost.Uint64(), nil
|
return callCost.Uint64(), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ 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/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -378,33 +377,32 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
|
||||||
return GasCosts{RegularGas: gas}, nil
|
return GasCosts{RegularGas: gas}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasCallStateless(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
var (
|
||||||
var (
|
gasCall = makeCallVariantGasCost(gasCallIntrinsic)
|
||||||
gas uint64
|
gasCallCode = makeCallVariantGasCost(gasCallCodeIntrinsic)
|
||||||
transfersValue = !stack.Back(2).IsZero()
|
gasDelegateCall = makeCallVariantGasCost(gasDelegateCallIntrinsic)
|
||||||
)
|
gasStaticCall = makeCallVariantGasCost(gasStaticCallIntrinsic)
|
||||||
|
)
|
||||||
|
|
||||||
if transfersValue {
|
func makeCallVariantGasCost(intrinsicFunc gasFunc) gasFunc {
|
||||||
if evm.readOnly {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
return GasCosts{}, ErrWriteProtection
|
intrinsic, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||||||
} else if !evm.chainRules.IsEIP4762 {
|
if err != nil {
|
||||||
gas += params.CallValueTransferGas
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsic.RegularGas, stack.Back(0))
|
||||||
|
if err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
gas, overflow := math.SafeAdd(intrinsic.RegularGas, evm.callGasTemp)
|
||||||
|
if overflow {
|
||||||
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
return GasCosts{RegularGas: gas}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryGas, err := memoryGasCost(mem, memorySize)
|
|
||||||
if err != nil {
|
|
||||||
return GasCosts{}, err
|
|
||||||
}
|
|
||||||
var overflow bool
|
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
|
|
||||||
return GasCosts{RegularGas: gas}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasCallStateful(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
var (
|
var (
|
||||||
gas uint64
|
gas uint64
|
||||||
transfersValue = !stack.Back(2).IsZero()
|
transfersValue = !stack.Back(2).IsZero()
|
||||||
|
|
@ -413,48 +411,54 @@ func gasCallStateful(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||||
if evm.readOnly && transfersValue {
|
if evm.readOnly && transfersValue {
|
||||||
return GasCosts{}, ErrWriteProtection
|
return GasCosts{}, ErrWriteProtection
|
||||||
}
|
}
|
||||||
|
// Stateless check
|
||||||
|
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||||
|
if err != nil {
|
||||||
|
return GasCosts{}, err
|
||||||
|
}
|
||||||
|
var transferGas uint64
|
||||||
|
if transfersValue && !evm.chainRules.IsEIP4762 {
|
||||||
|
transferGas = params.CallValueTransferGas
|
||||||
|
}
|
||||||
|
var overflow bool
|
||||||
|
if gas, overflow = math.SafeAdd(memoryGas, transferGas); overflow {
|
||||||
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
// Stateful check
|
||||||
|
var (
|
||||||
|
stateGas uint64
|
||||||
|
accountCreationCost uint64
|
||||||
|
)
|
||||||
|
if evm.chainRules.IsAmsterdam {
|
||||||
|
accountCreationCost = params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||||
|
} else {
|
||||||
|
accountCreationCost = params.CallNewAccountGas
|
||||||
|
}
|
||||||
if evm.chainRules.IsEIP158 {
|
if evm.chainRules.IsEIP158 {
|
||||||
if transfersValue && evm.StateDB.Empty(address) {
|
if transfersValue && evm.StateDB.Empty(address) {
|
||||||
gas += params.CallNewAccountGas
|
stateGas = accountCreationCost
|
||||||
}
|
}
|
||||||
} else if !evm.StateDB.Exist(address) {
|
} else if !evm.StateDB.Exist(address) {
|
||||||
gas += params.CallNewAccountGas
|
stateGas = accountCreationCost
|
||||||
}
|
}
|
||||||
|
|
||||||
return GasCosts{RegularGas: gas}, nil
|
if evm.chainRules.IsAmsterdam {
|
||||||
}
|
return GasCosts{RegularGas: gas, StateGas: stateGas}, nil
|
||||||
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
|
||||||
stateless, err := gasCallStateless(evm, contract, stack, mem, memorySize)
|
|
||||||
if err != nil {
|
|
||||||
return GasCosts{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stateful, err := gasCallStateful(evm, contract, stack, mem, memorySize)
|
gas, overflow = math.SafeAdd(gas, stateGas)
|
||||||
if err != nil {
|
|
||||||
return GasCosts{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
gas, overflow := math.SafeAdd(stateless.RegularGas, stateful.RegularGas)
|
|
||||||
if overflow {
|
if overflow {
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, gas, stack.Back(0))
|
|
||||||
if err != nil {
|
|
||||||
return GasCosts{}, err
|
|
||||||
}
|
|
||||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
return GasCosts{RegularGas: gas}, nil
|
return GasCosts{RegularGas: gas}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasCallCodeStateful(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasCallCodeIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
return GasCosts{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasCallCodeStateless(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
|
||||||
memoryGas, err := memoryGasCost(mem, memorySize)
|
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
|
|
@ -475,50 +479,7 @@ func gasCallCodeStateless(evm *EVM, contract *Contract, stack *Stack, mem *Memor
|
||||||
return GasCosts{RegularGas: gas}, nil
|
return GasCosts{RegularGas: gas}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasDelegateCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
var overflow bool
|
|
||||||
gas, err := gasCallCodeStateless(evm, contract, stack, mem, memorySize)
|
|
||||||
if err != nil {
|
|
||||||
return GasCosts{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, gas.RegularGas, stack.Back(0))
|
|
||||||
if err != nil {
|
|
||||||
return GasCosts{}, err
|
|
||||||
}
|
|
||||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, evm.callGasTemp); overflow {
|
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
gas GasCosts
|
|
||||||
)
|
|
||||||
|
|
||||||
gas, err = gasDelegateCallStateless(evm, contract, stack, mem, memorySize)
|
|
||||||
if err != nil {
|
|
||||||
return GasCosts{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, gas.RegularGas, stack.Back(0))
|
|
||||||
if err != nil {
|
|
||||||
return GasCosts{}, err
|
|
||||||
}
|
|
||||||
var overflow bool
|
|
||||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, evm.callGasTemp); overflow {
|
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasDelegateCallStateful(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
|
||||||
return GasCosts{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasDelegateCallStateless(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
|
||||||
gas, err := memoryGasCost(mem, memorySize)
|
gas, err := memoryGasCost(mem, memorySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
|
|
@ -526,7 +487,7 @@ func gasDelegateCallStateless(evm *EVM, contract *Contract, stack *Stack, mem *M
|
||||||
return GasCosts{RegularGas: gas}, nil
|
return GasCosts{RegularGas: gas}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasStaticCallStateless(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasStaticCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
gas, err := memoryGasCost(mem, memorySize)
|
gas, err := memoryGasCost(mem, memorySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
|
|
@ -534,27 +495,6 @@ func gasStaticCallStateless(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
return GasCosts{RegularGas: gas}, nil
|
return GasCosts{RegularGas: gas}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasStaticCallStateful(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
|
||||||
return GasCosts{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
|
||||||
gas, err := gasStaticCallStateless(evm, contract, stack, mem, memorySize)
|
|
||||||
if err != nil {
|
|
||||||
return GasCosts{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, gas.RegularGas, stack.Back(0))
|
|
||||||
if err != nil {
|
|
||||||
return GasCosts{}, err
|
|
||||||
}
|
|
||||||
var overflow bool
|
|
||||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, evm.callGasTemp); overflow {
|
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
if evm.readOnly {
|
if evm.readOnly {
|
||||||
return GasCosts{}, ErrWriteProtection
|
return GasCosts{}, ErrWriteProtection
|
||||||
|
|
@ -628,26 +568,6 @@ func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
|
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// gasCall8037 is the stateful gas calculator for CALL in Amsterdam (EIP-8037).
|
|
||||||
// It only returns the state-dependent gas (account creation as state gas).
|
|
||||||
// Memory gas, transfer gas, and callGas are handled by gasCallStateless and
|
|
||||||
// makeCallVariantGasCall.
|
|
||||||
func gasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
|
||||||
var (
|
|
||||||
gas GasCosts
|
|
||||||
transfersValue = !stack.Back(2).IsZero()
|
|
||||||
address = common.Address(stack.Back(1).Bytes20())
|
|
||||||
)
|
|
||||||
if evm.chainRules.IsEIP158 {
|
|
||||||
if transfersValue && evm.StateDB.Empty(address) {
|
|
||||||
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerGasByte
|
|
||||||
}
|
|
||||||
} else if !evm.StateDB.Exist(address) {
|
|
||||||
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerGasByte
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasSelfdestruct8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasSelfdestruct8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
if evm.readOnly {
|
if evm.readOnly {
|
||||||
return GasCosts{}, ErrWriteProtection
|
return GasCosts{}, ErrWriteProtection
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ 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"
|
||||||
|
|
@ -155,12 +154,51 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
return GasCosts{RegularGas: 0}, nil
|
return GasCosts{RegularGas: 0}, 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
|
||||||
|
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||||
|
if warmAccess || err != nil {
|
||||||
|
return gas, 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
|
||||||
|
|
||||||
|
var overflow bool
|
||||||
|
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, coldCost); overflow {
|
||||||
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
return gas, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// TODO: we can use the same functions already defined above for the 7702 gas handlers
|
// TODO: we can use the same functions already defined above for the 7702 gas handlers
|
||||||
gasCallEIP2929 = makeCallVariantGasCall(gasCallStateless, gasCallStateful)
|
gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall, 1)
|
||||||
gasDelegateCallEIP2929 = makeCallVariantGasCall(gasDelegateCallStateless, gasDelegateCallStateful)
|
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall, 1)
|
||||||
gasStaticCallEIP2929 = makeCallVariantGasCall(gasStaticCallStateless, gasStaticCallStateful)
|
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall, 1)
|
||||||
gasCallCodeEIP2929 = makeCallVariantGasCall(gasCallCodeStateless, gasCallCodeStateful)
|
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode, 1)
|
||||||
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)
|
||||||
|
|
@ -222,76 +260,71 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gasCallEIP7702 = makeCallVariantGasCall(gasCallStateful, gasCallStateless)
|
innerGasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCallIntrinsic)
|
||||||
gasDelegateCallEIP7702 = makeCallVariantGasCall(gasDelegateCallStateful, gasDelegateCallStateless)
|
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCallIntrinsic)
|
||||||
gasStaticCallEIP7702 = makeCallVariantGasCall(gasStaticCallStateful, gasStaticCallStateless)
|
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic)
|
||||||
gasCallCodeEIP7702 = makeCallVariantGasCall(gasCallCodeStateful, gasCallCodeStateless)
|
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic)
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeCallVariantGasCall(oldCalculatorStateful, oldCalculatorStateless gasFunc) gasFunc {
|
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) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
var (
|
var (
|
||||||
eip150BaseGas GasCosts // gas used for memory expansion, transfer costs -> input to the 63/64 bounding
|
eip2929Gas uint64
|
||||||
eip7702Gas uint64
|
eip7702Gas uint64
|
||||||
eip2929Gas uint64
|
addr = common.Address(stack.Back(1).Bytes20())
|
||||||
addr = common.Address(stack.Back(1).Bytes20())
|
|
||||||
overflow bool
|
|
||||||
err error
|
|
||||||
)
|
)
|
||||||
|
// Perform EIP-2929 checks (stateless), checking address presence
|
||||||
// Check slot presence in the access list
|
// in the accessList and charge the cold access accordingly.
|
||||||
if evm.chainRules.IsEIP2929 && !evm.StateDB.AddressInAccessList(addr) {
|
if !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 cost to charge for cold access, if any, is Cold - Warm
|
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form
|
||||||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
// of a constant cost, so the cost to charge for cold access, if any,
|
||||||
// Charge the remaining difference here already, to correctly calculate available
|
// is Cold - Warm
|
||||||
// gas for call
|
eip2929Gas = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||||
if !contract.UseGas(GasCosts{RegularGas: coldCost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
|
||||||
|
// Charge the remaining difference here already, to correctly calculate
|
||||||
|
// available gas for call
|
||||||
|
if !contract.UseGas(GasCosts{RegularGas: eip2929Gas}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
return GasCosts{}, ErrOutOfGas
|
return GasCosts{}, ErrOutOfGas
|
||||||
}
|
}
|
||||||
eip2929Gas = coldCost
|
|
||||||
}
|
}
|
||||||
eip150BaseGas, err = oldCalculatorStateless(evm, contract, stack, mem, memorySize)
|
|
||||||
|
// Perform the intrinsic cost calculation including:
|
||||||
|
//
|
||||||
|
// - transfer value
|
||||||
|
// - memory expansion
|
||||||
|
// - create new account
|
||||||
|
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
|
if contract.Gas.RegularGas < intrinsicCost.RegularGas {
|
||||||
// ensure the portion of the call cost which doesn't depend on state lookups
|
|
||||||
// is covered by the provided gas
|
|
||||||
if contract.Gas.RegularGas < eip150BaseGas.RegularGas {
|
|
||||||
return GasCosts{}, ErrOutOfGas
|
return GasCosts{}, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
oldStateful, err := oldCalculatorStateful(evm, contract, stack, mem, memorySize)
|
// Check if code is a delegation and if so, charge for resolution.
|
||||||
if err != nil {
|
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||||
return oldStateful, err
|
if evm.StateDB.AddressInAccessList(target) {
|
||||||
}
|
eip7702Gas = params.WarmStorageReadCostEIP2929
|
||||||
|
} else {
|
||||||
// this should cause BAL test failures if uncommented
|
evm.StateDB.AddAddressToAccessList(target)
|
||||||
baseCost, overflow := math.SafeAdd(eip150BaseGas.RegularGas, oldStateful.RegularGas)
|
eip7702Gas = params.ColdAccountAccessCostEIP2929
|
||||||
if overflow {
|
}
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
if !contract.UseGas(GasCosts{RegularGas: eip7702Gas}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
} else if contract.Gas.RegularGas < baseCost {
|
return GasCosts{}, ErrOutOfGas
|
||||||
return GasCosts{}, ErrOutOfGas
|
|
||||||
}
|
|
||||||
|
|
||||||
if eip150BaseGas.RegularGas, overflow = math.SafeAdd(eip150BaseGas.RegularGas, oldStateful.RegularGas); overflow {
|
|
||||||
return GasCosts{}, ErrOutOfGas
|
|
||||||
}
|
|
||||||
|
|
||||||
if evm.chainRules.IsPrague {
|
|
||||||
// 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) {
|
|
||||||
eip7702Gas = params.WarmStorageReadCostEIP2929
|
|
||||||
} else {
|
|
||||||
evm.StateDB.AddAddressToAccessList(target)
|
|
||||||
eip7702Gas = params.ColdAccountAccessCostEIP2929
|
|
||||||
}
|
|
||||||
if !contract.UseGas(GasCosts{RegularGas: eip7702Gas}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
|
||||||
return GasCosts{}, ErrOutOfGas
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,8 +334,8 @@ func makeCallVariantGasCall(oldCalculatorStateful, oldCalculatorStateless gasFun
|
||||||
// the Underflow check in UseGas will fail when the spillover exceeds the
|
// the Underflow check in UseGas will fail when the spillover exceeds the
|
||||||
// tiny 1/64 remainder after child gas allocation.
|
// tiny 1/64 remainder after child gas allocation.
|
||||||
var stateGasCharged uint64
|
var stateGasCharged uint64
|
||||||
if evm.chainRules.IsAmsterdam && oldStateful.StateGas > 0 {
|
if evm.chainRules.IsAmsterdam && intrinsicCost.StateGas > 0 {
|
||||||
stateGasCharged = oldStateful.StateGas
|
stateGasCharged = intrinsicCost.StateGas
|
||||||
stateGasCost := GasCosts{StateGas: stateGasCharged}
|
stateGasCost := GasCosts{StateGas: stateGasCharged}
|
||||||
if contract.Gas.Underflow(stateGasCost) {
|
if contract.Gas.Underflow(stateGasCost) {
|
||||||
return GasCosts{}, ErrOutOfGas
|
return GasCosts{}, ErrOutOfGas
|
||||||
|
|
@ -311,15 +344,14 @@ func makeCallVariantGasCall(oldCalculatorStateful, oldCalculatorStateless gasFun
|
||||||
contract.Gas.Sub(stateGasCost)
|
contract.Gas.Sub(stateGasCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, eip150BaseGas.RegularGas, stack.Back(0))
|
// 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 {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: it's not clear what happens if there is enough gas to cover the stateless component
|
var overflow bool
|
||||||
// but not enough to cover the whole call: do all the state reads happen in this case, and
|
|
||||||
// we fail at the very end?
|
|
||||||
|
|
||||||
// Temporarily add the gas charge back to the contract and return value. By
|
// 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
|
// 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
|
// part of the dynamic gas. This will ensure it is correctly reported to
|
||||||
|
|
@ -338,24 +370,22 @@ func makeCallVariantGasCall(oldCalculatorStateful, oldCalculatorStateless gasFun
|
||||||
}
|
}
|
||||||
contract.GasUsed.RegularGasUsed -= eip7702Gas
|
contract.GasUsed.RegularGasUsed -= eip7702Gas
|
||||||
|
|
||||||
|
// Aggregate the gas costs from all components, including EIP-2929, EIP-7702,
|
||||||
|
// the CALL opcode itself, and the cost incurred by nested calls.
|
||||||
var totalCost uint64
|
var totalCost uint64
|
||||||
totalCost, overflow = math.SafeAdd(eip2929Gas, eip7702Gas)
|
if totalCost, overflow = math.SafeAdd(eip2929Gas, eip7702Gas); overflow {
|
||||||
if overflow {
|
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp)
|
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost.RegularGas); overflow {
|
||||||
if overflow {
|
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
totalCost, overflow = math.SafeAdd(totalCost, eip150BaseGas.RegularGas)
|
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
|
||||||
if overflow {
|
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
// If state gas was already charged directly (Amsterdam), don't include
|
// If state gas was already charged directly (Amsterdam), don't include
|
||||||
// it in the returned cost — it would be double-charged by the
|
// it in the returned cost — it would be double-charged by the
|
||||||
// interpreter's UseGas/Sub which increments TotalStateGasCharged again.
|
// interpreter's UseGas/Sub which increments TotalStateGasCharged again.
|
||||||
returnedStateGas := oldStateful.StateGas
|
returnedStateGas := intrinsicCost.StateGas
|
||||||
if stateGasCharged > 0 {
|
if stateGasCharged > 0 {
|
||||||
returnedStateGas = 0
|
returnedStateGas = 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue