diff --git a/core/vm/eips.go b/core/vm/eips.go index 8dcceaa9cc..80163279d3 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -606,7 +606,6 @@ func enable8037(jt *JumpTable) { jt[CREATE].dynamicGas = gasCreateEip8037 jt[CREATE2].constantGas = params.CreateGasAmsterdam jt[CREATE2].dynamicGas = gasCreate2Eip8037 - jt[CALL].dynamicGas = makeCallVariantGasCall(gasCall8037, gasCallStateless) jt[SELFDESTRUCT].dynamicGas = gasSelfdestruct8037 jt[SSTORE].dynamicGas = gasSStore8037 } diff --git a/core/vm/gas.go b/core/vm/gas.go index 5fe589bce6..dcb20893c5 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -49,6 +49,5 @@ func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (u if !callCost.IsUint64() { return 0, ErrGasUintOverflow } - return callCost.Uint64(), nil } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 399c1ed595..3ed8d29f35 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -18,7 +18,6 @@ package vm import ( "errors" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "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 } -func gasCallStateless(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { - var ( - gas uint64 - transfersValue = !stack.Back(2).IsZero() - ) +var ( + gasCall = makeCallVariantGasCost(gasCallIntrinsic) + gasCallCode = makeCallVariantGasCost(gasCallCodeIntrinsic) + gasDelegateCall = makeCallVariantGasCost(gasDelegateCallIntrinsic) + gasStaticCall = makeCallVariantGasCost(gasStaticCallIntrinsic) +) - if transfersValue { - if evm.readOnly { - return GasCosts{}, ErrWriteProtection - } else if !evm.chainRules.IsEIP4762 { - gas += params.CallValueTransferGas +func makeCallVariantGasCost(intrinsicFunc gasFunc) gasFunc { + return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { + intrinsic, err := intrinsicFunc(evm, contract, stack, mem, memorySize) + if err != nil { + 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 ( gas uint64 transfersValue = !stack.Back(2).IsZero() @@ -413,48 +411,54 @@ func gasCallStateful(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me if evm.readOnly && transfersValue { 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 transfersValue && evm.StateDB.Empty(address) { - gas += params.CallNewAccountGas + stateGas = accountCreationCost } } else if !evm.StateDB.Exist(address) { - gas += params.CallNewAccountGas + stateGas = accountCreationCost } - return GasCosts{RegularGas: gas}, 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 + if evm.chainRules.IsAmsterdam { + return GasCosts{RegularGas: gas, StateGas: stateGas}, nil } - stateful, err := gasCallStateful(evm, contract, stack, mem, memorySize) - if err != nil { - return GasCosts{}, err - } - - gas, overflow := math.SafeAdd(stateless.RegularGas, stateful.RegularGas) + gas, overflow = math.SafeAdd(gas, stateGas) if overflow { 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 } -func gasCallCodeStateful(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) { +func gasCallCodeIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { memoryGas, err := memoryGasCost(mem, memorySize) if err != nil { return GasCosts{}, err @@ -475,50 +479,7 @@ func gasCallCodeStateless(evm *EVM, contract *Contract, stack *Stack, mem *Memor return GasCosts{RegularGas: gas}, nil } -func gasCallCode(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) { +func gasDelegateCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { gas, err := memoryGasCost(mem, memorySize) if err != nil { return GasCosts{}, err @@ -526,7 +487,7 @@ func gasDelegateCallStateless(evm *EVM, contract *Contract, stack *Stack, mem *M 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) if err != nil { return GasCosts{}, err @@ -534,27 +495,6 @@ func gasStaticCallStateless(evm *EVM, contract *Contract, stack *Stack, mem *Mem 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) { if evm.readOnly { 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 } -// 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) { if evm.readOnly { return GasCosts{}, ErrWriteProtection diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 2c48db4061..60ac3fe3b3 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -18,7 +18,6 @@ package vm import ( "errors" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "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 } +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 ( // TODO: we can use the same functions already defined above for the 7702 gas handlers - gasCallEIP2929 = makeCallVariantGasCall(gasCallStateless, gasCallStateful) - gasDelegateCallEIP2929 = makeCallVariantGasCall(gasDelegateCallStateless, gasDelegateCallStateful) - gasStaticCallEIP2929 = makeCallVariantGasCall(gasStaticCallStateless, gasStaticCallStateful) - gasCallCodeEIP2929 = makeCallVariantGasCall(gasCallCodeStateless, gasCallCodeStateful) + 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) @@ -222,76 +260,71 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { } var ( - gasCallEIP7702 = makeCallVariantGasCall(gasCallStateful, gasCallStateless) - gasDelegateCallEIP7702 = makeCallVariantGasCall(gasDelegateCallStateful, gasDelegateCallStateless) - gasStaticCallEIP7702 = makeCallVariantGasCall(gasStaticCallStateful, gasStaticCallStateless) - gasCallCodeEIP7702 = makeCallVariantGasCall(gasCallCodeStateful, gasCallCodeStateless) + innerGasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCallIntrinsic) + gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCallIntrinsic) + gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic) + 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) { var ( - eip150BaseGas GasCosts // gas used for memory expansion, transfer costs -> input to the 63/64 bounding - eip7702Gas uint64 - eip2929Gas uint64 - addr = common.Address(stack.Back(1).Bytes20()) - overflow bool - err error + eip2929Gas uint64 + eip7702Gas uint64 + addr = common.Address(stack.Back(1).Bytes20()) ) - - // Check slot presence in the access list - if evm.chainRules.IsEIP2929 && !evm.StateDB.AddressInAccessList(addr) { + // 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 - coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 - // Charge the remaining difference here already, to correctly calculate available - // gas for call - if !contract.UseGas(GasCosts{RegularGas: coldCost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { + + // 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 + eip2929Gas = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 + + // 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 } - 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 { return GasCosts{}, err } - - // 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 { + if contract.Gas.RegularGas < intrinsicCost.RegularGas { return GasCosts{}, ErrOutOfGas } - oldStateful, err := oldCalculatorStateful(evm, contract, stack, mem, memorySize) - if err != nil { - return oldStateful, err - } - - // this should cause BAL test failures if uncommented - baseCost, overflow := math.SafeAdd(eip150BaseGas.RegularGas, oldStateful.RegularGas) - if overflow { - return GasCosts{}, ErrGasUintOverflow - } else if contract.Gas.RegularGas < baseCost { - 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 - } + // 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 // tiny 1/64 remainder after child gas allocation. var stateGasCharged uint64 - if evm.chainRules.IsAmsterdam && oldStateful.StateGas > 0 { - stateGasCharged = oldStateful.StateGas + if evm.chainRules.IsAmsterdam && intrinsicCost.StateGas > 0 { + stateGasCharged = intrinsicCost.StateGas stateGasCost := GasCosts{StateGas: stateGasCharged} if contract.Gas.Underflow(stateGasCost) { return GasCosts{}, ErrOutOfGas @@ -311,15 +344,14 @@ func makeCallVariantGasCall(oldCalculatorStateful, oldCalculatorStateless gasFun 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 { return GasCosts{}, err } - // 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 - // we fail at the very end? - + var overflow bool // 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 @@ -338,24 +370,22 @@ func makeCallVariantGasCall(oldCalculatorStateful, oldCalculatorStateless gasFun } 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 - totalCost, overflow = math.SafeAdd(eip2929Gas, eip7702Gas) - if overflow { + if totalCost, overflow = math.SafeAdd(eip2929Gas, eip7702Gas); overflow { return GasCosts{}, ErrGasUintOverflow } - totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp) - if overflow { + if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost.RegularGas); overflow { return GasCosts{}, ErrGasUintOverflow } - totalCost, overflow = math.SafeAdd(totalCost, eip150BaseGas.RegularGas) - if overflow { + if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow { return GasCosts{}, ErrGasUintOverflow } - // If state gas was already charged directly (Amsterdam), don't include // it in the returned cost — it would be double-charged by the // interpreter's UseGas/Sub which increments TotalStateGasCharged again. - returnedStateGas := oldStateful.StateGas + returnedStateGas := intrinsicCost.StateGas if stateGasCharged > 0 { returnedStateGas = 0 }