core/vm: change state gas calculation

This commit is contained in:
MariusVanDerWijden 2026-04-16 17:48:02 +02:00
parent f7f38b0005
commit 23fbc5a923
11 changed files with 440 additions and 292 deletions

View file

@ -135,6 +135,112 @@ func (j *journal) length() int {
return len(j.entries)
}
// computeStateGrowthCost walks journal entries from the given snapshot revision
// to the current tip and computes the signed net state growth cost.
// It mirrors the EIP-8037 spec's compute_state_growth_cost(snapshot, current).
//
// Positive cost = state grew (accounts created, slots set, code deployed).
// Negative cost = state shrank (accounts removed, slots cleared).
func (j *journal) computeStateGrowthCost(revid int, s *StateDB, costPerStateByte uint64) int64 {
// Find the journal index for this revision.
idx := sort.Search(len(j.validRevisions), func(i int) bool {
return j.validRevisions[i].id >= revid
})
if idx == len(j.validRevisions) || j.validRevisions[idx].id != revid {
panic(fmt.Errorf("revision id %v not found for state growth cost", revid))
}
snapshotIdx := j.validRevisions[idx].journalIndex
// Track which accounts were created and which (addr, key) slots were
// touched since the snapshot. We only care about net effect.
type slotKey struct {
addr common.Address
key common.Hash
}
// For accounts: track the first createObjectChange. We need to know
// if the account existed at snapshot time (it didn't if created after).
accountCreated := make(map[common.Address]bool) // addr → was created since snapshot
// For storage: track the value at snapshot time (first prevvalue seen).
slotSnapshotValue := make(map[slotKey]common.Hash)
slotSeen := make(map[slotKey]bool)
// For code: track the code length at snapshot time (first prevCode seen).
codeSnapshotLen := make(map[common.Address]int)
codeSeen := make(map[common.Address]bool)
for i := snapshotIdx; i < len(j.entries); i++ {
switch ch := j.entries[i].(type) {
case createObjectChange:
if !accountCreated[ch.account] {
accountCreated[ch.account] = true
}
case storageChange:
sk := slotKey{ch.account, ch.key}
if !slotSeen[sk] {
slotSeen[sk] = true
slotSnapshotValue[sk] = ch.prevvalue
}
case codeChange:
if !codeSeen[ch.account] {
codeSeen[ch.account] = true
codeSnapshotLen[ch.account] = len(ch.prevCode)
}
}
}
var cost int64
// Account creation: +112 bytes for each account that was created
// and still exists now (not deleted by a revert within this scope).
for addr := range accountCreated {
// Check if the account currently exists in stateObjects.
// If it was created and then the snapshot was reverted past it,
// it won't be here. But since we only walk entries that haven't
// been reverted, the account should exist.
if _, exists := s.stateObjects[addr]; exists {
cost += int64(112 * costPerStateByte)
}
}
// Storage slots: compare snapshot value vs current value.
for sk, snapshotVal := range slotSnapshotValue {
obj := s.getStateObject(sk.addr)
if obj == nil {
continue
}
// Current value is in dirtyStorage (in-tx mutations).
currentVal, dirty := obj.dirtyStorage[sk.key]
if !dirty {
// Not dirty means value didn't change from committed state,
// which means the journal changes were reverted. Net effect = 0.
continue
}
snapshotZero := snapshotVal == (common.Hash{})
currentZero := currentVal == (common.Hash{})
if snapshotZero && !currentZero {
cost += int64(32 * costPerStateByte) // new slot
} else if !snapshotZero && currentZero {
cost -= int64(32 * costPerStateByte) // removed slot
}
}
// Code: compare snapshot code length vs current code length.
for addr, snapshotLen := range codeSnapshotLen {
obj := s.getStateObject(addr)
if obj == nil {
continue
}
currentLen := len(obj.code)
if snapshotLen == 0 && currentLen > 0 {
cost += int64(uint64(currentLen) * costPerStateByte)
}
// Note: code can only be set once per account in practice,
// so we don't handle the removal case (code is never cleared
// to empty during normal execution).
}
return cost
}
// copy returns a deep-copied journal.
func (j *journal) copy() *journal {
entries := make([]journalEntry, 0, j.length())

View file

@ -738,6 +738,13 @@ func (s *StateDB) RevertToSnapshot(revid int) {
s.journal.revertToSnapshot(revid, s)
}
// ComputeStateGrowthCost returns the signed net state growth cost (in gas)
// between the given snapshot revision and the current state.
// This implements the EIP-8037 diff-at-return mechanism.
func (s *StateDB) ComputeStateGrowthCost(revid int, costPerStateByte uint64) int64 {
return s.journal.computeStateGrowthCost(revid, s, costPerStateByte)
}
// GetRefund returns the current value of the refund counter.
func (s *StateDB) GetRefund() uint64 {
return s.refund

View file

@ -150,6 +150,10 @@ func (s *hookedStateDB) Snapshot() int {
return s.inner.Snapshot()
}
func (s *hookedStateDB) ComputeStateGrowthCost(revid int, costPerStateByte uint64) int64 {
return s.inner.ComputeStateGrowthCost(revid, costPerStateByte)
}
func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) {
s.inner.AddPreimage(hash, bytes)
}

View file

@ -74,9 +74,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
var gas vm.GasCosts
if isContractCreation && rules.IsHomestead {
if rules.IsAmsterdam {
// EIP-8037: account creation is state gas; base tx + CREATE overhead is regular gas.
// EIP-8037 diff-at-return: account creation state gas is computed
// from the state diff at call return, not charged as intrinsic gas.
gas.RegularGas = params.TxGas + params.CreateGasAmsterdam
gas.StateGas = params.AccountCreationSize * costPerStateByte
} else {
gas.RegularGas = params.TxGasContractCreation
}

View file

@ -239,6 +239,7 @@ func isSystemCall(caller common.Address) bool {
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, gas GasBudget, value *uint256.Int) (ret []byte, leftOverGas GasBudget, gasUsed GasUsed, err error) {
initialStateGas := gas.StateGas // EIP-8037: remember for diff-at-return
// Capture the tracer start/end events in debug mode
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALL, caller, addr, input, gas, value.ToBig())
@ -305,6 +306,12 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
gasUsed = contract.GasUsed
}
}
// EIP-8037 diff-at-return: charge state gas based on actual state diff.
if err == nil {
if stateErr := evm.chargeStateGasAtReturn(snapshot, initialStateGas, &gas, &gasUsed); stateErr != nil {
err = stateErr
}
}
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally,
// when we're in homestead this also counts for code storage gas errors.
@ -330,6 +337,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
// CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context.
func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byte, gas GasBudget, value *uint256.Int) (ret []byte, leftOverGas GasBudget, gasUsed GasUsed, err error) {
initialStateGas := gas.StateGas // EIP-8037: remember for diff-at-return
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALLCODE, caller, addr, input, gas, value.ToBig())
@ -365,6 +373,12 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
gas = contract.Gas
gasUsed = contract.GasUsed
}
// EIP-8037 diff-at-return: charge state gas based on actual state diff.
if err == nil {
if stateErr := evm.chargeStateGasAtReturn(snapshot, initialStateGas, &gas, &gasUsed); stateErr != nil {
err = stateErr
}
}
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
isRevert := err == ErrExecutionReverted
@ -385,6 +399,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
// DelegateCall differs from CallCode in the sense that it executes the given address'
// code with the caller as context and the caller is set to the caller of the caller.
func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas GasBudget, value *uint256.Int) (ret []byte, leftOverGas GasBudget, gasUsed GasUsed, err error) {
initialStateGas := gas.StateGas // EIP-8037: remember for diff-at-return
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
// DELEGATECALL inherits value from parent call
@ -415,6 +430,12 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
gas = contract.Gas
gasUsed = contract.GasUsed
}
// EIP-8037 diff-at-return: charge state gas based on actual state diff.
if err == nil {
if stateErr := evm.chargeStateGasAtReturn(snapshot, initialStateGas, &gas, &gasUsed); stateErr != nil {
err = stateErr
}
}
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
isRevert := err == ErrExecutionReverted
@ -434,6 +455,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
// Opcodes that attempt to perform such modifications will result in exceptions
// instead of performing the modifications.
func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []byte, gas GasBudget) (ret []byte, leftOverGas GasBudget, gasUsed GasUsed, err error) {
initialStateGas := gas.StateGas // EIP-8037: remember for diff-at-return
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, STATICCALL, caller, addr, input, gas, nil)
@ -464,6 +486,13 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
gas = contract.Gas
gasUsed = contract.GasUsed
}
// EIP-8037 diff-at-return: charge state gas based on actual state diff.
// (StaticCall shouldn't create state, but charge anyway for correctness.)
if err == nil {
if stateErr := evm.chargeStateGasAtReturn(snapshot, initialStateGas, &gas, &gasUsed); stateErr != nil {
err = stateErr
}
}
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
isRevert := err == ErrExecutionReverted
@ -480,6 +509,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas GasBudget, used GasUsed, err error) {
initialStateGas := gas.StateGas // EIP-8037: remember for diff-at-return
// Depth check execution. Fail if we're trying to execute above the
// limit.
var nonce uint64
@ -580,6 +610,12 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
contract.IsDeployment = true
ret, err = evm.initNewContract(contract, address)
// EIP-8037 diff-at-return: charge state gas based on actual state diff.
if err == nil {
if stateErr := evm.chargeStateGasAtReturn(snapshot, initialStateGas, &contract.Gas, &contract.GasUsed); stateErr != nil {
err = stateErr
}
}
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
@ -608,21 +644,16 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
}
if evm.chainRules.IsAmsterdam {
// Check max code size BEFORE charging gas so over-max code
// does not consume state gas (which would inflate tx_state).
// Check max code size BEFORE charging gas.
if err := CheckMaxCodeSize(&evm.chainRules, uint64(len(ret))); err != nil {
return ret, err
}
// EIP-8037: Charge regular gas (keccak256 hash) first, then state gas
// (code storage). Regular-before-state prevents reservoir inflation.
// EIP-8037: Charge regular gas (keccak256 hash) only.
// Code deposit state gas is computed from the state diff at call return.
regularGas := GasCosts{RegularGas: toWordSize(uint64(len(ret))) * params.Keccak256WordGas}
if !contract.UseGas(regularGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
return ret, ErrCodeStoreOutOfGas
}
stateGas := GasCosts{StateGas: uint64(len(ret)) * evm.Context.CostPerGasByte}
if !contract.UseGas(stateGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
return ret, ErrCodeStoreOutOfGas
}
} else if evm.chainRules.IsEIP4762 {
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas.RegularGas)
contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
@ -693,6 +724,47 @@ func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash {
return evm.StateDB.GetCodeHash(addr)
}
// chargeStateGasAtReturn implements EIP-8037 diff-at-return: after a successful
// call, compute the net state growth cost from the journal diff and charge it
// from the gas budget (state gas reservoir first, then spillover to regular gas).
// initialStateGas is the reservoir value at call entry, needed to compute how
// much inner calls already consumed.
// Returns an error if the call cannot afford the state gas, in which case the
// caller should revert the state to the snapshot.
func (evm *EVM) chargeStateGasAtReturn(snapshot int, initialStateGas uint64, gas *GasBudget, gasUsed *GasUsed) error {
if !evm.chainRules.IsAmsterdam {
return nil
}
growthCost := evm.StateDB.ComputeStateGrowthCost(snapshot, evm.Context.CostPerGasByte)
// Inner calls already deducted from reservoir.
alreadyPaid := int64(initialStateGas) - int64(gas.StateGas)
thisCallCost := growthCost - alreadyPaid
if thisCallCost <= 0 {
// Negative cost = state shrank relative to what was paid, credit the reservoir.
if thisCallCost < 0 {
gas.StateGas += uint64(-thisCallCost)
}
return nil
}
cost := uint64(thisCallCost)
if gas.StateGas >= cost {
gas.StateGas -= cost
gasUsed.StateGas += cost
} else if gas.StateGas+gas.RegularGas >= cost {
// Spillover: reservoir exhausted, take remainder from regular gas.
remainder := cost - gas.StateGas
gasUsed.StateGas += gas.StateGas
gas.StateGas = 0
gas.RegularGas -= remainder
gasUsed.RegularGas += remainder
} else {
return ErrOutOfGas
}
return nil
}
// ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }

View file

@ -64,26 +64,26 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
// EXTCODECOPY (stack position 3)
// RETURNDATACOPY (stack position 2)
func memoryCopierGas(stackpos int) 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) (uint64, error) {
// Gas for expanding the memory
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
// And gas for copying data, charged per word at param.CopyGas
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
if overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, words); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
}
@ -95,9 +95,9 @@ var (
gasReturnDataCopy = memoryCopierGas(2)
)
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
var (
y, x = stack.Back(1), stack.Back(0)
@ -114,12 +114,12 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// 3. From a non-zero to a non-zero (CHANGE)
switch {
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
return GasCosts{RegularGas: params.SstoreSetGas}, nil
return params.SstoreSetGas, nil
case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
evm.StateDB.AddRefund(params.SstoreRefundGas)
return GasCosts{RegularGas: params.SstoreClearGas}, nil
return params.SstoreClearGas, nil
default: // non 0 => non 0 (or 0 => 0)
return GasCosts{RegularGas: params.SstoreResetGas}, nil
return params.SstoreResetGas, nil
}
}
@ -139,16 +139,16 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// (2.2.2.2.) Otherwise, add 4800 gas to refund counter.
value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return GasCosts{RegularGas: params.NetSstoreNoopGas}, nil
return params.NetSstoreNoopGas, nil
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return GasCosts{RegularGas: params.NetSstoreInitGas}, nil
return params.NetSstoreInitGas, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
return GasCosts{RegularGas: params.NetSstoreCleanGas}, nil // write existing slot (2.1.2)
return params.NetSstoreCleanGas, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
@ -164,7 +164,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
evm.StateDB.AddRefund(params.NetSstoreResetRefund)
}
}
return GasCosts{RegularGas: params.NetSstoreDirtyGas}, nil
return params.NetSstoreDirtyGas, nil
}
// Here come the EIP2200 rules:
@ -182,13 +182,13 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// (2.2.2.) If original value equals new value (this storage slot is reset):
// (2.2.2.1.) If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
// (2.2.2.2.) Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, 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")
return 0, errors.New("not enough gas for reentrancy sentry")
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
@ -198,16 +198,16 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil
return params.SloadGasEIP2200, nil
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return GasCosts{RegularGas: params.SstoreSetGasEIP2200}, nil
return params.SstoreSetGasEIP2200, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
}
return GasCosts{RegularGas: params.SstoreResetGasEIP2200}, nil // write existing slot (2.1.2)
return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
@ -223,66 +223,62 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
}
}
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil // dirty update (2.2)
return params.SloadGasEIP2200, nil // dirty update (2.2)
}
func makeGasLog(n uint64) 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) (uint64, error) {
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
var memorySizeGas uint64
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
}
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
// pureMemoryGascost is used by several operations, which aside from their
// static cost have a dynamic cost which is solely based on the memory
// expansion
func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return GasCosts{}, err
}
return GasCosts{RegularGas: gas}, nil
func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return memoryGasCost(mem, memorySize)
}
var (
@ -293,81 +289,81 @@ var (
gasMStore = pureMemoryGascost
)
func gasCreate(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasCreate(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
return pureMemoryGascost(evm, contract, stack, mem, memorySize)
}
func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
if overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
size, overflow := stack.Back(2).Uint64WithOverflow()
if overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
return GasCosts{}, err
return 0, err
}
// Since size <= the protocol-defined maximum initcode size limit, these multiplication cannot overflow
moreGas := params.InitCodeWordGas * ((size + 31) / 32)
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
size, overflow := stack.Back(2).Uint64WithOverflow()
if overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
return GasCosts{}, err
return 0, err
}
// Since size <= the protocol-defined maximum initcode size limit, these multiplication cannot overflow
moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
var (
@ -375,12 +371,12 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
overflow bool
)
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
var (
@ -388,9 +384,9 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
overflow bool
)
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
var (
@ -401,20 +397,20 @@ var (
)
func makeCallVariantGasCost(intrinsicFunc intrinsicGasFunc) 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) (uint64, error) {
intrinsic, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsic, stack.Back(0))
if err != nil {
return GasCosts{}, err
return 0, err
}
gas, overflow := math.SafeAdd(intrinsic, evm.callGasTemp)
if overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
}
@ -515,16 +511,16 @@ func gasStaticCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Mem
return memoryGasCost(mem, memorySize)
}
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) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
var gas uint64
// EIP150 homestead gas reprice fork:
if evm.chainRules.IsEIP150 {
gas = params.SelfdestructGasEIP150
if gas > contract.Gas.RegularGas {
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
var address = common.Address(stack.Back(0).Bytes20())
@ -541,116 +537,99 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
if !evm.StateDB.HasSelfDestructed(contract.Address()) {
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
size, overflow := stack.Back(2).Uint64WithOverflow()
if overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
return GasCosts{}, err
return 0, err
}
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
words := (size + 31) / 32
wordGas := params.InitCodeWordGas * words
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
// EIP-8037 diff-at-return: account creation state gas is computed
// from the state diff at call return, not charged here.
return gas + wordGas, nil
}
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
size, overflow := stack.Back(2).Uint64WithOverflow()
if overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
return GasCosts{}, err
return 0, err
}
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
words := (size + 31) / 32
// CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas (for address hashing).
wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
// EIP-8037 diff-at-return: account creation state gas is computed
// from the state diff at call return, not charged here.
return gas + wordGas, 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
// With diff-at-return, account creation state gas is computed from the state
// diff at call return, not charged here.
func gasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return 0, 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) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
var (
gas GasCosts
gas uint64
address = common.Address(stack.peek().Bytes20())
)
if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(address)
gas.RegularGas = params.ColdAccountAccessCostEIP2929
}
// Check we have enough regular gas before we add the address to the BAL
if contract.Gas.RegularGas < gas.RegularGas {
return gas, nil
}
// if empty and transfers value
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerGasByte
gas = params.ColdAccountAccessCostEIP2929
}
// EIP-8037 diff-at-return: account creation state gas is computed
// from the state diff at call return, not charged here.
return gas, nil
}
func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, 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")
return 0, 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 GasCosts
cost uint64
)
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
cost = GasCosts{RegularGas: params.ColdSloadCostEIP2929}
cost = params.ColdSloadCostEIP2929
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
}
@ -659,24 +638,20 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
if current == value { // noop (1)
// EIP 2200 original clause:
// return params.SloadGasEIP2200, nil
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // SLOAD_GAS
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
// EIP-8037: Return both regular and state gas. The interpreter
// charges regular gas before state gas, preventing reservoir
// inflation when the regular charge OOGs.
return GasCosts{
RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929,
StateGas: params.StorageCreationSize * evm.Context.CostPerGasByte,
}, nil
// EIP-8037 diff-at-return: storage creation state gas is computed
// from the state diff at call return, not charged here.
return cost + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP3529)
}
// EIP-2200 original clause:
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
return GasCosts{RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929}, nil // write existing slot (2.1.2)
return 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)
@ -689,7 +664,9 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
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.StorageCreationSize*evm.Context.CostPerGasByte + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929 - params.WarmStorageReadCostEIP2929)
// EIP-8037 diff-at-return: state gas is handled by the diff,
// so refund only covers the regular gas overpayment.
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929 - params.WarmStorageReadCostEIP2929)
} else { // reset to original existing slot (2.2.2.2)
// EIP 2200 Original clause:
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
@ -701,5 +678,5 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
}
// EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2)
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // dirty update (2.2)
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
}

View file

@ -86,6 +86,10 @@ type StateDB interface {
RevertToSnapshot(int)
Snapshot() int
// ComputeStateGrowthCost returns the signed net state growth cost (in gas)
// between the given snapshot revision and the current state (EIP-8037).
ComputeStateGrowthCost(revid int, costPerStateByte uint64) int64
AddLog(*types.Log)
EmitLogsForBurnAccounts()
AddPreimage(common.Hash, []byte)

View file

@ -219,32 +219,17 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
}
// Consume the gas and return an error if not enough gas is available.
// cost is explicitly set so that the capture state defer method can get the proper cost
var dynamicCost GasCosts
var dynamicCost uint64
dynamicCost, err = operation.dynamicGas(evm, contract, stack, mem, memorySize)
cost += dynamicCost.RegularGas // for tracing
cost += dynamicCost // for tracing
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
}
// for tracing: this gas consumption event is emitted below in the debug section.
if evm.chainRules.IsAmsterdam && dynamicCost.StateGas > 0 {
// EIP-8037: charge regular gas before state gas.
if contract.Gas.RegularGas < dynamicCost.RegularGas {
return nil, ErrOutOfGas
}
contract.GasUsed.RegularGas += dynamicCost.RegularGas
contract.Gas.RegularGas -= dynamicCost.RegularGas
stateOnly := GasCosts{StateGas: dynamicCost.StateGas}
if contract.Gas.Underflow(stateOnly) {
return nil, ErrOutOfGas
}
contract.GasUsed.Add(stateOnly)
contract.Gas.Sub(stateOnly)
} else if contract.Gas.Underflow(dynamicCost) {
if contract.Gas.RegularGas < dynamicCost {
return nil, ErrOutOfGas
} else {
contract.GasUsed.Add(dynamicCost)
contract.Gas.Sub(dynamicCost)
}
contract.Gas.RegularGas -= dynamicCost
contract.GasUsed.RegularGas += dynamicCost
}
// Do tracing before potential memory expansion

View file

@ -24,7 +24,7 @@ import (
type (
executionFunc func(pc *uint64, evm *EVM, callContext *ScopeContext) ([]byte, error)
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (GasCosts, error) // last parameter is the requested memory size as a uint64
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
intrinsicGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
memorySizeFunc func(*Stack) (size uint64, overflow bool)

View file

@ -27,24 +27,24 @@ import (
)
func makeGasSStoreFunc(clearingRefund uint64) 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) (uint64, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, 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")
return 0, 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 = GasCosts{RegularGas: 0}
cost uint64
)
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
cost = GasCosts{RegularGas: params.ColdSloadCostEIP2929}
cost = params.ColdSloadCostEIP2929
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
}
@ -53,18 +53,18 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
if current == value { // noop (1)
// EIP 2200 original clause:
// return params.SloadGasEIP2200, nil
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // SLOAD_GAS
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return GasCosts{RegularGas: cost.RegularGas + params.SstoreSetGasEIP2200}, nil
return 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.RegularGas + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929)}, nil // write existing slot (2.1.2)
return 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)
@ -89,7 +89,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
}
// EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2)
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // dirty update (2.2)
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
}
}
@ -98,7 +98,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
// 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) {
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
loc := stack.peek()
slot := common.Hash(loc.Bytes32())
// Check slot presence in the access list
@ -106,9 +106,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
// 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 params.ColdSloadCostEIP2929, nil
}
return GasCosts{RegularGas: params.WarmStorageReadCostEIP2929}, nil
return params.WarmStorageReadCostEIP2929, nil
}
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
@ -116,11 +116,11 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
// > 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) {
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// memory expansion first (dynamic part of pre-2929 implementation)
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
addr := common.Address(stack.peek().Bytes20())
// Check slot presence in the access list
@ -128,10 +128,9 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
evm.StateDB.AddAddressToAccessList(addr)
var overflow bool
// We charge (cold-warm), since 'warm' is already charged as constantGas
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
return GasCosts{}, ErrGasUintOverflow
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
return gas, nil
}
@ -143,20 +142,20 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
// - extcodehash,
// - extcodesize,
// - (ext) balance
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, 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 params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
}
return GasCosts{RegularGas: 0}, nil
return 0, nil
}
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) 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) (uint64, error) {
addr := common.Address(stack.Back(addressPosition).Bytes20())
// Check slot presence in the access list
warmAccess := evm.StateDB.AddressInAccessList(addr)
@ -168,7 +167,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
// 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
return 0, ErrOutOfGas
}
}
// Now call the old calculator, which takes into account
@ -187,8 +186,8 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
contract.Gas.RegularGas += coldCost
var overflow bool
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, coldCost); overflow {
return GasCosts{}, ErrGasUintOverflow
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
@ -224,13 +223,13 @@ var (
// 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) {
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
address = common.Address(stack.peek().Bytes20())
)
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back
@ -240,11 +239,11 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
// 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
return 0, ErrOutOfGas
}
}
if gas > contract.Gas.RegularGas {
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
// if empty and transfers value
@ -254,7 +253,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
}
return GasCosts{RegularGas: gas}, nil
return gas, nil
}
return gasFunc
}
@ -268,30 +267,30 @@ var (
innerGasCallEIP8037 = makeCallVariantGasCallEIP8037(gasCallIntrinsic8037, gasCall8037)
)
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, 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 0, ErrWriteProtection
}
return innerGasCallEIP7702(evm, contract, stack, mem, memorySize)
}
func gasCallEIP8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasCallEIP8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// Same write-protection guard as gasCallEIP7702: check before any gas
// charging to avoid incorrectly recording in the access list.
transfersValue := !stack.Back(2).IsZero()
if evm.readOnly && transfersValue {
return GasCosts{}, ErrWriteProtection
return 0, ErrWriteProtection
}
return innerGasCallEIP8037(evm, contract, stack, mem, memorySize)
}
func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) 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) (uint64, error) {
var (
eip2929Cost uint64
eip7702Cost uint64
@ -310,7 +309,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
// 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
return 0, ErrOutOfGas
}
}
@ -321,13 +320,13 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
// - create new account
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, 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 {
return GasCosts{}, ErrOutOfGas
return 0, ErrOutOfGas
}
// Check if code is a delegation and if so, charge for resolution.
@ -339,14 +338,14 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
eip7702Cost = params.ColdAccountAccessCostEIP2929
}
if !contract.UseGas(GasCosts{RegularGas: eip7702Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas
return 0, 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, stack.Back(0))
if err != nil {
return GasCosts{}, err
return 0, 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
@ -365,15 +364,15 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
totalCost uint64
)
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: totalCost}, nil
return totalCost, nil
}
}
@ -382,7 +381,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
// intrinsicFunc computes the regular gas (memory + transfer, no new account creation).
// stateGasFunc computes the state gas (new account creation as state gas).
func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc 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) (uint64, error) {
var (
eip2929Cost uint64
eip7702Cost uint64
@ -393,21 +392,21 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
evm.StateDB.AddAddressToAccessList(addr)
eip2929Cost = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
if !contract.UseGas(GasCosts{RegularGas: eip2929Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas
return 0, ErrOutOfGas
}
}
// Compute intrinsic cost (memory + transfer, no new account creation).
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
// Charge intrinsic cost directly (regular gas). This must happen
// BEFORE state gas to prevent reservoir inflation, and also serves
// as the OOG guard before stateful operations.
if !contract.UseGas(GasCosts{RegularGas: intrinsicCost}, evm.Config.Tracer, tracing.GasChangeCallOpCode) {
return GasCosts{}, ErrOutOfGas
return 0, ErrOutOfGas
}
// EIP-7702 delegation check.
@ -419,28 +418,21 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
eip7702Cost = params.ColdAccountAccessCostEIP2929
}
if !contract.UseGas(GasCosts{RegularGas: eip7702Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas
return 0, ErrOutOfGas
}
}
// Compute and charge state gas (new account creation) AFTER regular gas.
stateGas, err := stateGasFunc(evm, contract, stack, mem, memorySize)
if err != nil {
return GasCosts{}, err
}
if stateGas.StateGas > 0 {
stateGasCost := GasCosts{StateGas: stateGas.StateGas}
if contract.Gas.Underflow(stateGasCost) {
return GasCosts{}, ErrOutOfGas
}
contract.GasUsed.Add(stateGasCost)
contract.Gas.Sub(stateGasCost)
// EIP-8037 diff-at-return: state gas (new account creation) is
// computed from the state diff at call return, not charged here.
// We still call stateGasFunc for any side-effects/error checks.
if _, err = stateGasFunc(evm, contract, stack, mem, memorySize); err != nil {
return 0, err
}
// Calculate the gas budget for the nested call (63/64 rule).
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, 0, stack.Back(0))
if err != nil {
return GasCosts{}, err
return 0, err
}
// Temporarily undo direct regular charges for tracer reporting.
@ -454,14 +446,14 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
totalCost uint64
)
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
return GasCosts{}, ErrGasUintOverflow
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: totalCost}, nil
return totalCost, nil
}
}

View file

@ -24,40 +24,40 @@ import (
"github.com/ethereum/go-ethereum/params"
)
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas.RegularGas, true)}, nil
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas.RegularGas, true), nil
}
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas.RegularGas, true)}, nil
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas.RegularGas, true), nil
}
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
return GasCosts{RegularGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true)}, nil
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true), nil
}
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
if _, isPrecompile := evm.precompile(address); isPrecompile {
return GasCosts{}, nil
return 0, nil
}
return GasCosts{RegularGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true)}, nil
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true), nil
}
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
if _, isPrecompile := evm.precompile(address); isPrecompile {
return GasCosts{}, nil
return 0, nil
}
return GasCosts{RegularGas: evm.AccessEvents.CodeHashGas(address, false, contract.Gas.RegularGas, true)}, nil
return evm.AccessEvents.CodeHashGas(address, false, contract.Gas.RegularGas, true), nil
}
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) 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) (uint64, error) {
var (
target = common.Address(stack.Back(1).Bytes20())
witnessGas GasCosts
witnessGas uint64
_, isPrecompile = evm.precompile(target)
isSystemContract = target == params.HistoryStorageAddress
)
@ -67,36 +67,37 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) ga
if withTransferCosts && !stack.Back(2).IsZero() {
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas.RegularGas)
if wantedValueTransferWitnessGas > contract.Gas.RegularGas {
return GasCosts{RegularGas: wantedValueTransferWitnessGas}, nil
return wantedValueTransferWitnessGas, nil
}
witnessGas = GasCosts{RegularGas: wantedValueTransferWitnessGas}
witnessGas = wantedValueTransferWitnessGas
} else if isPrecompile || isSystemContract {
witnessGas = GasCosts{RegularGas: params.WarmStorageReadCostEIP2929}
witnessGas = params.WarmStorageReadCostEIP2929
} else {
// The charging for the value transfer is done BEFORE subtracting
// the 1/64th gas, as this is considered part of the CALL instruction.
// (so before we get to this point)
// But the message call is part of the subcall, for which only 63/64th
// of the gas should be available.
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas.RegularGas-witnessGas.RegularGas)
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas.RegularGas-witnessGas)
var overflow bool
if witnessGas.RegularGas, overflow = math.SafeAdd(witnessGas.RegularGas, wantedMessageCallWitnessGas); overflow {
return GasCosts{RegularGas: 0}, ErrGasUintOverflow
if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
return 0, ErrGasUintOverflow
}
if witnessGas.RegularGas > contract.Gas.RegularGas {
if witnessGas > contract.Gas.RegularGas {
return witnessGas, nil
}
}
contract.Gas.Sub(witnessGas)
witnessGasCost := GasCosts{RegularGas: witnessGas}
contract.Gas.Sub(witnessGasCost)
// if the operation fails, adds witness gas to the gas before returning the error
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
contract.Gas.Add(witnessGas) // restore witness gas so that it can be charged at the callsite
contract.Gas.Add(witnessGasCost) // restore witness gas so that it can be charged at the callsite
var overflow bool
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, witnessGas.RegularGas); overflow {
return GasCosts{}, ErrGasUintOverflow
if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas.RegularGas}, err
return gas, err
}
}
@ -107,20 +108,20 @@ var (
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false)
)
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
beneficiaryAddr := common.Address(stack.peek().Bytes20())
if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile {
return GasCosts{}, nil
return 0, nil
}
if contract.IsSystemCall {
return GasCosts{}, nil
return 0, nil
}
contractAddr := contract.Address()
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas.RegularGas, false)
if wanted > contract.Gas.RegularGas {
return GasCosts{RegularGas: wanted}, nil
return wanted, nil
}
statelessGas := GasCosts{RegularGas: wanted}
statelessGas := wanted
balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0
_, isPrecompile := evm.precompile(beneficiaryAddr)
isSystemContract := beneficiaryAddr == params.HistoryStorageAddress
@ -130,39 +131,39 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
}
if contractAddr != beneficiaryAddr {
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas.RegularGas-statelessGas.RegularGas, false)
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas {
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas.RegularGas-statelessGas, false)
if wanted > contract.Gas.RegularGas-statelessGas {
return statelessGas + wanted, nil
}
statelessGas.RegularGas += wanted
statelessGas += wanted
}
// Charge write costs if it transfers value
if !balanceIsZero {
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas.RegularGas-statelessGas.RegularGas, false)
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas {
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas.RegularGas-statelessGas, false)
if wanted > contract.Gas.RegularGas-statelessGas {
return statelessGas + wanted, nil
}
statelessGas.RegularGas += wanted
statelessGas += wanted
if contractAddr != beneficiaryAddr {
if evm.StateDB.Exist(beneficiaryAddr) {
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas.RegularGas, false)
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas, false)
} else {
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas.RegularGas)
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas)
}
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas {
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil
if wanted > contract.Gas.RegularGas-statelessGas {
return statelessGas + wanted, nil
}
statelessGas.RegularGas += wanted
statelessGas += wanted
}
}
return statelessGas, nil
}
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := gasCodeCopy(evm, contract, stack, mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
if !contract.IsDeployment && !contract.IsSystemCall {
var (
@ -175,31 +176,31 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
}
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas.RegularGas-gas.RegularGas)
gas.RegularGas += wanted
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas.RegularGas-gas)
gas += wanted
}
return gas, nil
}
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// memory expansion first (dynamic part of pre-2929 implementation)
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
if err != nil {
return GasCosts{}, err
return 0, err
}
addr := common.Address(stack.peek().Bytes20())
_, isPrecompile := evm.precompile(addr)
if isPrecompile || addr == params.HistoryStorageAddress {
var overflow bool
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, params.WarmStorageReadCostEIP2929); overflow {
return GasCosts{}, ErrGasUintOverflow
if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow {
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas.RegularGas}, nil
return gas, nil
}
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas.RegularGas-gas.RegularGas, true)
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas.RegularGas-gas, true)
var overflow bool
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, wgas); overflow {
return GasCosts{}, ErrGasUintOverflow
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
return 0, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas.RegularGas}, nil
return gas, nil
}