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) 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. // copy returns a deep-copied journal.
func (j *journal) copy() *journal { func (j *journal) copy() *journal {
entries := make([]journalEntry, 0, j.length()) entries := make([]journalEntry, 0, j.length())

View file

@ -738,6 +738,13 @@ func (s *StateDB) RevertToSnapshot(revid int) {
s.journal.revertToSnapshot(revid, s) 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. // GetRefund returns the current value of the refund counter.
func (s *StateDB) GetRefund() uint64 { func (s *StateDB) GetRefund() uint64 {
return s.refund return s.refund

View file

@ -150,6 +150,10 @@ func (s *hookedStateDB) Snapshot() int {
return s.inner.Snapshot() 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) { func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) {
s.inner.AddPreimage(hash, bytes) 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 var gas vm.GasCosts
if isContractCreation && rules.IsHomestead { if isContractCreation && rules.IsHomestead {
if rules.IsAmsterdam { 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.RegularGas = params.TxGas + params.CreateGasAmsterdam
gas.StateGas = params.AccountCreationSize * costPerStateByte
} else { } else {
gas.RegularGas = params.TxGasContractCreation 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 // the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer. // 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) { 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 // Capture the tracer start/end events in debug mode
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALL, caller, addr, input, gas, value.ToBig()) 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 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 // 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, // 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. // 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' // CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context. // 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) { 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 // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALLCODE, caller, addr, input, gas, value.ToBig()) 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 gas = contract.Gas
gasUsed = contract.GasUsed 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 { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
isRevert := err == ErrExecutionReverted 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' // 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. // 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) { 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 // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
// DELEGATECALL inherits value from parent call // DELEGATECALL inherits value from parent call
@ -415,6 +430,12 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
gas = contract.Gas gas = contract.Gas
gasUsed = contract.GasUsed 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 { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
isRevert := err == ErrExecutionReverted 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 // Opcodes that attempt to perform such modifications will result in exceptions
// instead of performing the modifications. // 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) { 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 // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, STATICCALL, caller, addr, input, gas, 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 gas = contract.Gas
gasUsed = contract.GasUsed 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 { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
isRevert := err == ErrExecutionReverted 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. // 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) { 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 // Depth check execution. Fail if we're trying to execute above the
// limit. // limit.
var nonce uint64 var nonce uint64
@ -580,6 +610,12 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
contract.IsDeployment = true contract.IsDeployment = true
ret, err = evm.initNewContract(contract, address) 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) { if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
@ -608,21 +644,16 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
} }
if evm.chainRules.IsAmsterdam { if evm.chainRules.IsAmsterdam {
// Check max code size BEFORE charging gas so over-max code // Check max code size BEFORE charging gas.
// does not consume state gas (which would inflate tx_state).
if err := CheckMaxCodeSize(&evm.chainRules, uint64(len(ret))); err != nil { if err := CheckMaxCodeSize(&evm.chainRules, uint64(len(ret))); err != nil {
return ret, err return ret, err
} }
// EIP-8037: Charge regular gas (keccak256 hash) first, then state gas // EIP-8037: Charge regular gas (keccak256 hash) only.
// (code storage). Regular-before-state prevents reservoir inflation. // Code deposit state gas is computed from the state diff at call return.
regularGas := GasCosts{RegularGas: toWordSize(uint64(len(ret))) * params.Keccak256WordGas} regularGas := GasCosts{RegularGas: toWordSize(uint64(len(ret))) * params.Keccak256WordGas}
if !contract.UseGas(regularGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) { if !contract.UseGas(regularGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
return ret, ErrCodeStoreOutOfGas 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 { } else if evm.chainRules.IsEIP4762 {
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas.RegularGas) 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) 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) 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 // ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } 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) // EXTCODECOPY (stack position 3)
// RETURNDATACOPY (stack position 2) // RETURNDATACOPY (stack position 2)
func memoryCopierGas(stackpos int) gasFunc { 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 for expanding the memory
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
// And gas for copying data, charged per word at param.CopyGas // And gas for copying data, charged per word at param.CopyGas
words, overflow := stack.Back(stackpos).Uint64WithOverflow() words, overflow := stack.Back(stackpos).Uint64WithOverflow()
if overflow { if overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow { if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if gas, overflow = math.SafeAdd(gas, words); overflow { 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) 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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
var ( var (
y, x = stack.Back(1), stack.Back(0) 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) // 3. From a non-zero to a non-zero (CHANGE)
switch { switch {
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0 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 case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
evm.StateDB.AddRefund(params.SstoreRefundGas) evm.StateDB.AddRefund(params.SstoreRefundGas)
return GasCosts{RegularGas: params.SstoreClearGas}, nil return params.SstoreClearGas, nil
default: // non 0 => non 0 (or 0 => 0) 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. // (2.2.2.2.) Otherwise, add 4800 gas to refund counter.
value := common.Hash(y.Bytes32()) value := common.Hash(y.Bytes32())
if current == value { // noop (1) if current == value { // noop (1)
return GasCosts{RegularGas: params.NetSstoreNoopGas}, nil return params.NetSstoreNoopGas, nil
} }
if original == current { if original == current {
if original == (common.Hash{}) { // create slot (2.1.1) 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) if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.NetSstoreClearRefund) 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 original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1) 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) evm.StateDB.AddRefund(params.NetSstoreResetRefund)
} }
} }
return GasCosts{RegularGas: params.NetSstoreDirtyGas}, nil return params.NetSstoreDirtyGas, nil
} }
// Here come the EIP2200 rules: // 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.) 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.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. // (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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
// If we fail the minimum gas availability invariant, fail (0) // If we fail the minimum gas availability invariant, fail (0)
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 { 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 // Gas sentry honoured, do the actual gas calculation based on the stored value
var ( var (
@ -198,16 +198,16 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
value := common.Hash(y.Bytes32()) value := common.Hash(y.Bytes32())
if current == value { // noop (1) if current == value { // noop (1)
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil return params.SloadGasEIP2200, nil
} }
if original == current { if original == current {
if original == (common.Hash{}) { // create slot (2.1.1) 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) if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200) 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 original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1) 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) 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 { 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() requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
if overflow { if overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow { 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 { if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
var memorySizeGas uint64 var memorySizeGas uint64
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow { if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow { 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) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
wordGas, overflow := stack.Back(1).Uint64WithOverflow() wordGas, overflow := stack.Back(1).Uint64WithOverflow()
if overflow { if overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow { if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if gas, overflow = math.SafeAdd(gas, wordGas); overflow { 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 // pureMemoryGascost is used by several operations, which aside from their
// static cost have a dynamic cost which is solely based on the memory // static cost have a dynamic cost which is solely based on the memory
// expansion // expansion
func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize) return memoryGasCost(mem, memorySize)
if err != nil {
return GasCosts{}, err
}
return GasCosts{RegularGas: gas}, nil
} }
var ( var (
@ -293,81 +289,81 @@ var (
gasMStore = pureMemoryGascost 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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
return pureMemoryGascost(evm, contract, stack, mem, memorySize) 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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
wordGas, overflow := stack.Back(2).Uint64WithOverflow() wordGas, overflow := stack.Back(2).Uint64WithOverflow()
if overflow { if overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow { if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if gas, overflow = math.SafeAdd(gas, wordGas); overflow { 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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
size, overflow := stack.Back(2).Uint64WithOverflow() size, overflow := stack.Back(2).Uint64WithOverflow()
if overflow { if overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil { 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 // Since size <= the protocol-defined maximum initcode size limit, these multiplication cannot overflow
moreGas := params.InitCodeWordGas * ((size + 31) / 32) moreGas := params.InitCodeWordGas * ((size + 31) / 32)
if gas, overflow = math.SafeAdd(gas, moreGas); overflow { 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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
size, overflow := stack.Back(2).Uint64WithOverflow() size, overflow := stack.Back(2).Uint64WithOverflow()
if overflow { if overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil { 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 // Since size <= the protocol-defined maximum initcode size limit, these multiplication cannot overflow
moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32) moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
if gas, overflow = math.SafeAdd(gas, moreGas); overflow { 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) expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
var ( var (
@ -375,12 +371,12 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
overflow bool overflow bool
) )
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow { 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) expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
var ( var (
@ -388,9 +384,9 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
overflow bool overflow bool
) )
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow { if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
return GasCosts{RegularGas: gas}, nil return gas, nil
} }
var ( var (
@ -401,20 +397,20 @@ var (
) )
func makeCallVariantGasCost(intrinsicFunc intrinsicGasFunc) gasFunc { 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) intrinsic, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsic, stack.Back(0)) evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsic, stack.Back(0))
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
gas, overflow := math.SafeAdd(intrinsic, evm.callGasTemp) gas, overflow := math.SafeAdd(intrinsic, evm.callGasTemp)
if overflow { 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) 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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
var gas uint64 var gas uint64
// EIP150 homestead gas reprice fork: // EIP150 homestead gas reprice fork:
if evm.chainRules.IsEIP150 { if evm.chainRules.IsEIP150 {
gas = params.SelfdestructGasEIP150 gas = params.SelfdestructGasEIP150
if gas > contract.Gas.RegularGas { if gas > contract.Gas.RegularGas {
return GasCosts{RegularGas: gas}, nil return gas, nil
} }
var address = common.Address(stack.Back(0).Bytes20()) 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()) { if !evm.StateDB.HasSelfDestructed(contract.Address()) {
evm.StateDB.AddRefund(params.SelfdestructRefundGas) 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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
size, overflow := stack.Back(2).Uint64WithOverflow() size, overflow := stack.Back(2).Uint64WithOverflow()
if overflow { if overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil { if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
return GasCosts{}, err return 0, err
} }
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow // Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
words := (size + 31) / 32 words := (size + 31) / 32
wordGas := params.InitCodeWordGas * words wordGas := params.InitCodeWordGas * words
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte // EIP-8037 diff-at-return: account creation state gas is computed
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil // 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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
size, overflow := stack.Back(2).Uint64WithOverflow() size, overflow := stack.Back(2).Uint64WithOverflow()
if overflow { if overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil { if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
return GasCosts{}, err return 0, err
} }
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow // Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
words := (size + 31) / 32 words := (size + 31) / 32
// CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas (for address hashing). // CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas (for address hashing).
wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte // EIP-8037 diff-at-return: account creation state gas is computed
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil // 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). // gasCall8037 is the stateful gas calculator for CALL in Amsterdam (EIP-8037).
// It only returns the state-dependent gas (account creation as state gas). // With diff-at-return, account creation state gas is computed from the state
// Memory gas, transfer gas, and callGas are handled by gasCallStateless and // diff at call return, not charged here.
// makeCallVariantGasCall. func gasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { return 0, nil
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) (uint64, error) {
if evm.readOnly { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
var ( var (
gas GasCosts gas uint64
address = common.Address(stack.peek().Bytes20()) address = common.Address(stack.peek().Bytes20())
) )
if !evm.StateDB.AddressInAccessList(address) { if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back // If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(address) evm.StateDB.AddAddressToAccessList(address)
gas.RegularGas = params.ColdAccountAccessCostEIP2929 gas = 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
} }
// EIP-8037 diff-at-return: account creation state gas is computed
// from the state diff at call return, not charged here.
return gas, nil 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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
// If we fail the minimum gas availability invariant, fail (0) // If we fail the minimum gas availability invariant, fail (0)
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 { 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 // Gas sentry honoured, do the actual gas calculation based on the stored value
var ( var (
y, x = stack.Back(1), stack.peek() y, x = stack.Back(1), stack.peek()
slot = common.Hash(x.Bytes32()) slot = common.Hash(x.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot) current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
cost GasCosts cost uint64
) )
// Check slot presence in the access list // Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { 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 // If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddSlotToAccessList(contract.Address(), slot) 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) if current == value { // noop (1)
// EIP 2200 original clause: // EIP 2200 original clause:
// return params.SloadGasEIP2200, nil // 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 == current {
if original == (common.Hash{}) { // create slot (2.1.1) if original == (common.Hash{}) { // create slot (2.1.1)
// EIP-8037: Return both regular and state gas. The interpreter // EIP-8037 diff-at-return: storage creation state gas is computed
// charges regular gas before state gas, preventing reservoir // from the state diff at call return, not charged here.
// inflation when the regular charge OOGs. return cost + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929, nil
return GasCosts{
RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929,
StateGas: params.StorageCreationSize * evm.Context.CostPerGasByte,
}, nil
} }
if value == (common.Hash{}) { // delete slot (2.1.2b) if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP3529) evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP3529)
} }
// EIP-2200 original clause: // EIP-2200 original clause:
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) // 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 original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1) 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) if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
// EIP 2200 Original clause: // EIP 2200 Original clause:
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) //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) } else { // reset to original existing slot (2.2.2.2)
// EIP 2200 Original clause: // EIP 2200 Original clause:
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) // 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: // EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2) //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) RevertToSnapshot(int)
Snapshot() 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) AddLog(*types.Log)
EmitLogsForBurnAccounts() EmitLogsForBurnAccounts()
AddPreimage(common.Hash, []byte) 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. // 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 // 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) dynamicCost, err = operation.dynamicGas(evm, contract, stack, mem, memorySize)
cost += dynamicCost.RegularGas // for tracing cost += dynamicCost // for tracing
if err != nil { if err != nil {
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
} }
// for tracing: this gas consumption event is emitted below in the debug section. if contract.Gas.RegularGas < dynamicCost {
if evm.chainRules.IsAmsterdam && dynamicCost.StateGas > 0 {
// EIP-8037: charge regular gas before state gas.
if contract.Gas.RegularGas < dynamicCost.RegularGas {
return nil, ErrOutOfGas return nil, ErrOutOfGas
} }
contract.GasUsed.RegularGas += dynamicCost.RegularGas contract.Gas.RegularGas -= dynamicCost
contract.Gas.RegularGas -= dynamicCost.RegularGas contract.GasUsed.RegularGas += dynamicCost
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) {
return nil, ErrOutOfGas
} else {
contract.GasUsed.Add(dynamicCost)
contract.Gas.Sub(dynamicCost)
}
} }
// Do tracing before potential memory expansion // Do tracing before potential memory expansion

View file

@ -24,7 +24,7 @@ import (
type ( type (
executionFunc func(pc *uint64, evm *EVM, callContext *ScopeContext) ([]byte, error) 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 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 returns the required size, and whether the operation overflowed a uint64
memorySizeFunc func(*Stack) (size uint64, overflow bool) memorySizeFunc func(*Stack) (size uint64, overflow bool)

View file

@ -27,24 +27,24 @@ import (
) )
func makeGasSStoreFunc(clearingRefund uint64) gasFunc { 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 { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
// If we fail the minimum gas availability invariant, fail (0) // If we fail the minimum gas availability invariant, fail (0)
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 { 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 // Gas sentry honoured, do the actual gas calculation based on the stored value
var ( var (
y, x = stack.Back(1), stack.peek() y, x = stack.Back(1), stack.peek()
slot = common.Hash(x.Bytes32()) slot = common.Hash(x.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot) current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
cost = GasCosts{RegularGas: 0} cost uint64
) )
// Check slot presence in the access list // Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { 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 // If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddSlotToAccessList(contract.Address(), slot) evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
} }
@ -53,18 +53,18 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
if current == value { // noop (1) if current == value { // noop (1)
// EIP 2200 original clause: // EIP 2200 original clause:
// return params.SloadGasEIP2200, nil // 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 == current {
if original == (common.Hash{}) { // create slot (2.1.1) 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) if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(clearingRefund) evm.StateDB.AddRefund(clearingRefund)
} }
// EIP-2200 original clause: // EIP-2200 original clause:
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) // 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 original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1) if current == (common.Hash{}) { // recreate slot (2.2.1.1)
@ -89,7 +89,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
} }
// EIP-2200 original clause: // EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2) //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, // whose storage is being read) is not yet in accessed_storage_keys,
// charge 2100 gas and add the pair to 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. // 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() loc := stack.peek()
slot := common.Hash(loc.Bytes32()) slot := common.Hash(loc.Bytes32())
// Check slot presence in the access list // 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 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 // If he does afford it, we can skip checking the same thing later on, during execution
evm.StateDB.AddSlotToAccessList(contract.Address(), slot) 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 // 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, // > If the target is not in accessed_addresses,
// > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses. // > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
// > Otherwise, charge WARM_STORAGE_READ_COST gas. // > 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) // memory expansion first (dynamic part of pre-2929 implementation)
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize) gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
addr := common.Address(stack.peek().Bytes20()) addr := common.Address(stack.peek().Bytes20())
// Check slot presence in the access list // 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) evm.StateDB.AddAddressToAccessList(addr)
var overflow bool var overflow bool
// We charge (cold-warm), since 'warm' is already charged as constantGas // We charge (cold-warm), since 'warm' is already charged as constantGas
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow { if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
return gas, nil
} }
return gas, nil return gas, nil
} }
@ -143,20 +142,20 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
// - extcodehash, // - extcodehash,
// - extcodesize, // - extcodesize,
// - (ext) balance // - (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()) addr := common.Address(stack.peek().Bytes20())
// Check slot presence in the access list // Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) { if !evm.StateDB.AddressInAccessList(addr) {
// If the caller cannot afford the cost, this change will be rolled back // If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(addr)
// The warm storage read cost is already charged as constantGas // 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 { 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()) addr := common.Address(stack.Back(addressPosition).Bytes20())
// Check slot presence in the access list // Check slot presence in the access list
warmAccess := evm.StateDB.AddressInAccessList(addr) 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 // Charge the remaining difference here already, to correctly calculate available
// gas for call // gas for call
if !contract.UseGas(GasCosts{RegularGas: coldCost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { 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 // Now call the old calculator, which takes into account
@ -187,8 +186,8 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
contract.Gas.RegularGas += coldCost contract.Gas.RegularGas += coldCost
var overflow bool var overflow bool
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, coldCost); overflow { if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
return gas, nil return gas, nil
} }
@ -224,13 +223,13 @@ var (
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529 // makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { 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 ( var (
gas uint64 gas uint64
address = common.Address(stack.peek().Bytes20()) address = common.Address(stack.peek().Bytes20())
) )
if evm.readOnly { if evm.readOnly {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
if !evm.StateDB.AddressInAccessList(address) { if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back // If the caller cannot afford the cost, this change will be rolled back
@ -240,11 +239,11 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
// Terminate the gas measurement if the leftover gas is not sufficient, // Terminate the gas measurement if the leftover gas is not sufficient,
// it can effectively prevent accessing the states in the following steps // it can effectively prevent accessing the states in the following steps
if contract.Gas.RegularGas < gas { if contract.Gas.RegularGas < gas {
return GasCosts{}, ErrOutOfGas return 0, ErrOutOfGas
} }
} }
if gas > contract.Gas.RegularGas { if gas > contract.Gas.RegularGas {
return GasCosts{RegularGas: gas}, nil return gas, nil
} }
// if empty and transfers value // if empty and transfers value
@ -254,7 +253,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) { if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
evm.StateDB.AddRefund(params.SelfdestructRefundGas) evm.StateDB.AddRefund(params.SelfdestructRefundGas)
} }
return GasCosts{RegularGas: gas}, nil return gas, nil
} }
return gasFunc return gasFunc
} }
@ -268,30 +267,30 @@ var (
innerGasCallEIP8037 = makeCallVariantGasCallEIP8037(gasCallIntrinsic8037, gasCall8037) 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. // 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 // 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 // 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. // the target in the block access list (BAL) if the call later fails.
transfersValue := !stack.Back(2).IsZero() transfersValue := !stack.Back(2).IsZero()
if evm.readOnly && transfersValue { if evm.readOnly && transfersValue {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
return innerGasCallEIP7702(evm, contract, stack, mem, memorySize) 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 // Same write-protection guard as gasCallEIP7702: check before any gas
// charging to avoid incorrectly recording in the access list. // charging to avoid incorrectly recording in the access list.
transfersValue := !stack.Back(2).IsZero() transfersValue := !stack.Back(2).IsZero()
if evm.readOnly && transfersValue { if evm.readOnly && transfersValue {
return GasCosts{}, ErrWriteProtection return 0, ErrWriteProtection
} }
return innerGasCallEIP8037(evm, contract, stack, mem, memorySize) return innerGasCallEIP8037(evm, contract, stack, mem, memorySize)
} }
func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc { 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 ( var (
eip2929Cost uint64 eip2929Cost uint64
eip7702Cost uint64 eip7702Cost uint64
@ -310,7 +309,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
// Charge the remaining difference here already, to correctly calculate // Charge the remaining difference here already, to correctly calculate
// available gas for call // available gas for call
if !contract.UseGas(GasCosts{RegularGas: eip2929Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { 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 // - create new account
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize) intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
// Terminate the gas measurement if the leftover gas is not sufficient, // Terminate the gas measurement if the leftover gas is not sufficient,
// it can effectively prevent accessing the states in the following steps. // it can effectively prevent accessing the states in the following steps.
// It's an essential safeguard before any stateful check. // It's an essential safeguard before any stateful check.
if contract.Gas.RegularGas < intrinsicCost { if contract.Gas.RegularGas < intrinsicCost {
return GasCosts{}, ErrOutOfGas return 0, ErrOutOfGas
} }
// Check if code is a delegation and if so, charge for resolution. // Check if code is a delegation and if so, charge for resolution.
@ -339,14 +338,14 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
eip7702Cost = params.ColdAccountAccessCostEIP2929 eip7702Cost = params.ColdAccountAccessCostEIP2929
} }
if !contract.UseGas(GasCosts{RegularGas: eip7702Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { 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 // Calculate the gas budget for the nested call. The costs defined by
// EIP-2929 and EIP-7702 have already been applied. // EIP-2929 and EIP-7702 have already been applied.
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsicCost, stack.Back(0)) evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsicCost, stack.Back(0))
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
// 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
@ -365,15 +364,15 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
totalCost uint64 totalCost uint64
) )
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow { if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow { if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow { 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). // intrinsicFunc computes the regular gas (memory + transfer, no new account creation).
// stateGasFunc computes the state gas (new account creation as state gas). // stateGasFunc computes the state gas (new account creation as state gas).
func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc gasFunc) gasFunc { 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 ( var (
eip2929Cost uint64 eip2929Cost uint64
eip7702Cost uint64 eip7702Cost uint64
@ -393,21 +392,21 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(addr)
eip2929Cost = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 eip2929Cost = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
if !contract.UseGas(GasCosts{RegularGas: eip2929Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { 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). // Compute intrinsic cost (memory + transfer, no new account creation).
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize) intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
// Charge intrinsic cost directly (regular gas). This must happen // Charge intrinsic cost directly (regular gas). This must happen
// BEFORE state gas to prevent reservoir inflation, and also serves // BEFORE state gas to prevent reservoir inflation, and also serves
// as the OOG guard before stateful operations. // as the OOG guard before stateful operations.
if !contract.UseGas(GasCosts{RegularGas: intrinsicCost}, evm.Config.Tracer, tracing.GasChangeCallOpCode) { if !contract.UseGas(GasCosts{RegularGas: intrinsicCost}, evm.Config.Tracer, tracing.GasChangeCallOpCode) {
return GasCosts{}, ErrOutOfGas return 0, ErrOutOfGas
} }
// EIP-7702 delegation check. // EIP-7702 delegation check.
@ -419,28 +418,21 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
eip7702Cost = params.ColdAccountAccessCostEIP2929 eip7702Cost = params.ColdAccountAccessCostEIP2929
} }
if !contract.UseGas(GasCosts{RegularGas: eip7702Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { 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. // EIP-8037 diff-at-return: state gas (new account creation) is
stateGas, err := stateGasFunc(evm, contract, stack, mem, memorySize) // computed from the state diff at call return, not charged here.
if err != nil { // We still call stateGasFunc for any side-effects/error checks.
return GasCosts{}, err if _, err = stateGasFunc(evm, contract, stack, mem, memorySize); err != nil {
} return 0, 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)
} }
// Calculate the gas budget for the nested call (63/64 rule). // 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)) evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, 0, stack.Back(0))
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
// Temporarily undo direct regular charges for tracer reporting. // Temporarily undo direct regular charges for tracer reporting.
@ -454,14 +446,14 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
totalCost uint64 totalCost uint64
) )
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow { if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow { if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow { 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" "github.com/ethereum/go-ethereum/params"
) )
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) { func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas.RegularGas, true)}, nil 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) { func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas.RegularGas, true)}, nil 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() 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() address := stack.peek().Bytes20()
if _, isPrecompile := evm.precompile(address); isPrecompile { 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() address := stack.peek().Bytes20()
if _, isPrecompile := evm.precompile(address); isPrecompile { 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 { 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 ( var (
target = common.Address(stack.Back(1).Bytes20()) target = common.Address(stack.Back(1).Bytes20())
witnessGas GasCosts witnessGas uint64
_, isPrecompile = evm.precompile(target) _, isPrecompile = evm.precompile(target)
isSystemContract = target == params.HistoryStorageAddress isSystemContract = target == params.HistoryStorageAddress
) )
@ -67,36 +67,37 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) ga
if withTransferCosts && !stack.Back(2).IsZero() { if withTransferCosts && !stack.Back(2).IsZero() {
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas.RegularGas) wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas.RegularGas)
if wantedValueTransferWitnessGas > 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 { } else if isPrecompile || isSystemContract {
witnessGas = GasCosts{RegularGas: params.WarmStorageReadCostEIP2929} witnessGas = params.WarmStorageReadCostEIP2929
} else { } else {
// The charging for the value transfer is done BEFORE subtracting // The charging for the value transfer is done BEFORE subtracting
// the 1/64th gas, as this is considered part of the CALL instruction. // the 1/64th gas, as this is considered part of the CALL instruction.
// (so before we get to this point) // (so before we get to this point)
// But the message call is part of the subcall, for which only 63/64th // But the message call is part of the subcall, for which only 63/64th
// of the gas should be available. // 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 var overflow bool
if witnessGas.RegularGas, overflow = math.SafeAdd(witnessGas.RegularGas, wantedMessageCallWitnessGas); overflow { if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
return GasCosts{RegularGas: 0}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if witnessGas.RegularGas > contract.Gas.RegularGas { if witnessGas > contract.Gas.RegularGas {
return witnessGas, nil 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 // if the operation fails, adds witness gas to the gas before returning the error
gas, err := oldCalculator(evm, contract, stack, mem, memorySize) 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 var overflow bool
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, witnessGas.RegularGas); overflow { if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
return GasCosts{RegularGas: gas.RegularGas}, err return gas, err
} }
} }
@ -107,20 +108,20 @@ var (
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false) 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()) beneficiaryAddr := common.Address(stack.peek().Bytes20())
if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile { if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile {
return GasCosts{}, nil return 0, nil
} }
if contract.IsSystemCall { if contract.IsSystemCall {
return GasCosts{}, nil return 0, nil
} }
contractAddr := contract.Address() contractAddr := contract.Address()
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas.RegularGas, false) wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas.RegularGas, false)
if wanted > contract.Gas.RegularGas { 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 balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0
_, isPrecompile := evm.precompile(beneficiaryAddr) _, isPrecompile := evm.precompile(beneficiaryAddr)
isSystemContract := beneficiaryAddr == params.HistoryStorageAddress isSystemContract := beneficiaryAddr == params.HistoryStorageAddress
@ -130,39 +131,39 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
} }
if contractAddr != beneficiaryAddr { if contractAddr != beneficiaryAddr {
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas.RegularGas-statelessGas.RegularGas, false) wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas.RegularGas-statelessGas, false)
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas { if wanted > contract.Gas.RegularGas-statelessGas {
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil return statelessGas + wanted, nil
} }
statelessGas.RegularGas += wanted statelessGas += wanted
} }
// Charge write costs if it transfers value // Charge write costs if it transfers value
if !balanceIsZero { if !balanceIsZero {
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas.RegularGas-statelessGas.RegularGas, false) wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas.RegularGas-statelessGas, false)
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas { if wanted > contract.Gas.RegularGas-statelessGas {
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil return statelessGas + wanted, nil
} }
statelessGas.RegularGas += wanted statelessGas += wanted
if contractAddr != beneficiaryAddr { if contractAddr != beneficiaryAddr {
if evm.StateDB.Exist(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 { } 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 { if wanted > contract.Gas.RegularGas-statelessGas {
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil return statelessGas + wanted, nil
} }
statelessGas.RegularGas += wanted statelessGas += wanted
} }
} }
return statelessGas, nil 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) gas, err := gasCodeCopy(evm, contract, stack, mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
if !contract.IsDeployment && !contract.IsSystemCall { if !contract.IsDeployment && !contract.IsSystemCall {
var ( var (
@ -175,31 +176,31 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
} }
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64()) _, 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) _, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas.RegularGas-gas)
gas.RegularGas += wanted gas += wanted
} }
return gas, nil 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) // memory expansion first (dynamic part of pre-2929 implementation)
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize) gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
if err != nil { if err != nil {
return GasCosts{}, err return 0, err
} }
addr := common.Address(stack.peek().Bytes20()) addr := common.Address(stack.peek().Bytes20())
_, isPrecompile := evm.precompile(addr) _, isPrecompile := evm.precompile(addr)
if isPrecompile || addr == params.HistoryStorageAddress { if isPrecompile || addr == params.HistoryStorageAddress {
var overflow bool var overflow bool
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, params.WarmStorageReadCostEIP2929); overflow { if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow {
return GasCosts{}, ErrGasUintOverflow 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 var overflow bool
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, wgas); overflow { if gas, overflow = math.SafeAdd(gas, wgas); overflow {
return GasCosts{}, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
return GasCosts{RegularGas: gas.RegularGas}, nil return gas, nil
} }