mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-12 01:41:36 +00:00
core/vm: change state gas calculation
This commit is contained in:
parent
f7f38b0005
commit
23fbc5a923
11 changed files with 440 additions and 292 deletions
|
|
@ -135,6 +135,112 @@ func (j *journal) length() int {
|
|||
return len(j.entries)
|
||||
}
|
||||
|
||||
// computeStateGrowthCost walks journal entries from the given snapshot revision
|
||||
// to the current tip and computes the signed net state growth cost.
|
||||
// It mirrors the EIP-8037 spec's compute_state_growth_cost(snapshot, current).
|
||||
//
|
||||
// Positive cost = state grew (accounts created, slots set, code deployed).
|
||||
// Negative cost = state shrank (accounts removed, slots cleared).
|
||||
func (j *journal) computeStateGrowthCost(revid int, s *StateDB, costPerStateByte uint64) int64 {
|
||||
// Find the journal index for this revision.
|
||||
idx := sort.Search(len(j.validRevisions), func(i int) bool {
|
||||
return j.validRevisions[i].id >= revid
|
||||
})
|
||||
if idx == len(j.validRevisions) || j.validRevisions[idx].id != revid {
|
||||
panic(fmt.Errorf("revision id %v not found for state growth cost", revid))
|
||||
}
|
||||
snapshotIdx := j.validRevisions[idx].journalIndex
|
||||
|
||||
// Track which accounts were created and which (addr, key) slots were
|
||||
// touched since the snapshot. We only care about net effect.
|
||||
type slotKey struct {
|
||||
addr common.Address
|
||||
key common.Hash
|
||||
}
|
||||
// For accounts: track the first createObjectChange. We need to know
|
||||
// if the account existed at snapshot time (it didn't if created after).
|
||||
accountCreated := make(map[common.Address]bool) // addr → was created since snapshot
|
||||
// For storage: track the value at snapshot time (first prevvalue seen).
|
||||
slotSnapshotValue := make(map[slotKey]common.Hash)
|
||||
slotSeen := make(map[slotKey]bool)
|
||||
// For code: track the code length at snapshot time (first prevCode seen).
|
||||
codeSnapshotLen := make(map[common.Address]int)
|
||||
codeSeen := make(map[common.Address]bool)
|
||||
|
||||
for i := snapshotIdx; i < len(j.entries); i++ {
|
||||
switch ch := j.entries[i].(type) {
|
||||
case createObjectChange:
|
||||
if !accountCreated[ch.account] {
|
||||
accountCreated[ch.account] = true
|
||||
}
|
||||
case storageChange:
|
||||
sk := slotKey{ch.account, ch.key}
|
||||
if !slotSeen[sk] {
|
||||
slotSeen[sk] = true
|
||||
slotSnapshotValue[sk] = ch.prevvalue
|
||||
}
|
||||
case codeChange:
|
||||
if !codeSeen[ch.account] {
|
||||
codeSeen[ch.account] = true
|
||||
codeSnapshotLen[ch.account] = len(ch.prevCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cost int64
|
||||
|
||||
// Account creation: +112 bytes for each account that was created
|
||||
// and still exists now (not deleted by a revert within this scope).
|
||||
for addr := range accountCreated {
|
||||
// Check if the account currently exists in stateObjects.
|
||||
// If it was created and then the snapshot was reverted past it,
|
||||
// it won't be here. But since we only walk entries that haven't
|
||||
// been reverted, the account should exist.
|
||||
if _, exists := s.stateObjects[addr]; exists {
|
||||
cost += int64(112 * costPerStateByte)
|
||||
}
|
||||
}
|
||||
|
||||
// Storage slots: compare snapshot value vs current value.
|
||||
for sk, snapshotVal := range slotSnapshotValue {
|
||||
obj := s.getStateObject(sk.addr)
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
// Current value is in dirtyStorage (in-tx mutations).
|
||||
currentVal, dirty := obj.dirtyStorage[sk.key]
|
||||
if !dirty {
|
||||
// Not dirty means value didn't change from committed state,
|
||||
// which means the journal changes were reverted. Net effect = 0.
|
||||
continue
|
||||
}
|
||||
snapshotZero := snapshotVal == (common.Hash{})
|
||||
currentZero := currentVal == (common.Hash{})
|
||||
if snapshotZero && !currentZero {
|
||||
cost += int64(32 * costPerStateByte) // new slot
|
||||
} else if !snapshotZero && currentZero {
|
||||
cost -= int64(32 * costPerStateByte) // removed slot
|
||||
}
|
||||
}
|
||||
|
||||
// Code: compare snapshot code length vs current code length.
|
||||
for addr, snapshotLen := range codeSnapshotLen {
|
||||
obj := s.getStateObject(addr)
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
currentLen := len(obj.code)
|
||||
if snapshotLen == 0 && currentLen > 0 {
|
||||
cost += int64(uint64(currentLen) * costPerStateByte)
|
||||
}
|
||||
// Note: code can only be set once per account in practice,
|
||||
// so we don't handle the removal case (code is never cleared
|
||||
// to empty during normal execution).
|
||||
}
|
||||
|
||||
return cost
|
||||
}
|
||||
|
||||
// copy returns a deep-copied journal.
|
||||
func (j *journal) copy() *journal {
|
||||
entries := make([]journalEntry, 0, j.length())
|
||||
|
|
|
|||
|
|
@ -738,6 +738,13 @@ func (s *StateDB) RevertToSnapshot(revid int) {
|
|||
s.journal.revertToSnapshot(revid, s)
|
||||
}
|
||||
|
||||
// ComputeStateGrowthCost returns the signed net state growth cost (in gas)
|
||||
// between the given snapshot revision and the current state.
|
||||
// This implements the EIP-8037 diff-at-return mechanism.
|
||||
func (s *StateDB) ComputeStateGrowthCost(revid int, costPerStateByte uint64) int64 {
|
||||
return s.journal.computeStateGrowthCost(revid, s, costPerStateByte)
|
||||
}
|
||||
|
||||
// GetRefund returns the current value of the refund counter.
|
||||
func (s *StateDB) GetRefund() uint64 {
|
||||
return s.refund
|
||||
|
|
|
|||
|
|
@ -150,6 +150,10 @@ func (s *hookedStateDB) Snapshot() int {
|
|||
return s.inner.Snapshot()
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) ComputeStateGrowthCost(revid int, costPerStateByte uint64) int64 {
|
||||
return s.inner.ComputeStateGrowthCost(revid, costPerStateByte)
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) {
|
||||
s.inner.AddPreimage(hash, bytes)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,9 +74,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
|||
var gas vm.GasCosts
|
||||
if isContractCreation && rules.IsHomestead {
|
||||
if rules.IsAmsterdam {
|
||||
// EIP-8037: account creation is state gas; base tx + CREATE overhead is regular gas.
|
||||
// EIP-8037 diff-at-return: account creation state gas is computed
|
||||
// from the state diff at call return, not charged as intrinsic gas.
|
||||
gas.RegularGas = params.TxGas + params.CreateGasAmsterdam
|
||||
gas.StateGas = params.AccountCreationSize * costPerStateByte
|
||||
} else {
|
||||
gas.RegularGas = params.TxGasContractCreation
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,6 +239,7 @@ func isSystemCall(caller common.Address) bool {
|
|||
// the necessary steps to create accounts and reverses the state in case of an
|
||||
// execution error or failed value transfer.
|
||||
func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, gas GasBudget, value *uint256.Int) (ret []byte, leftOverGas GasBudget, gasUsed GasUsed, err error) {
|
||||
initialStateGas := gas.StateGas // EIP-8037: remember for diff-at-return
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.captureBegin(evm.depth, CALL, caller, addr, input, gas, value.ToBig())
|
||||
|
|
@ -305,6 +306,12 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
gasUsed = contract.GasUsed
|
||||
}
|
||||
}
|
||||
// EIP-8037 diff-at-return: charge state gas based on actual state diff.
|
||||
if err == nil {
|
||||
if stateErr := evm.chargeStateGasAtReturn(snapshot, initialStateGas, &gas, &gasUsed); stateErr != nil {
|
||||
err = stateErr
|
||||
}
|
||||
}
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
// above we revert to the snapshot and consume any gas remaining. Additionally,
|
||||
// when we're in homestead this also counts for code storage gas errors.
|
||||
|
|
@ -330,6 +337,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
// CallCode differs from Call in the sense that it executes the given address'
|
||||
// code with the caller as context.
|
||||
func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byte, gas GasBudget, value *uint256.Int) (ret []byte, leftOverGas GasBudget, gasUsed GasUsed, err error) {
|
||||
initialStateGas := gas.StateGas // EIP-8037: remember for diff-at-return
|
||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.captureBegin(evm.depth, CALLCODE, caller, addr, input, gas, value.ToBig())
|
||||
|
|
@ -365,6 +373,12 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
gas = contract.Gas
|
||||
gasUsed = contract.GasUsed
|
||||
}
|
||||
// EIP-8037 diff-at-return: charge state gas based on actual state diff.
|
||||
if err == nil {
|
||||
if stateErr := evm.chargeStateGasAtReturn(snapshot, initialStateGas, &gas, &gasUsed); stateErr != nil {
|
||||
err = stateErr
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
isRevert := err == ErrExecutionReverted
|
||||
|
|
@ -385,6 +399,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
// DelegateCall differs from CallCode in the sense that it executes the given address'
|
||||
// code with the caller as context and the caller is set to the caller of the caller.
|
||||
func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas GasBudget, value *uint256.Int) (ret []byte, leftOverGas GasBudget, gasUsed GasUsed, err error) {
|
||||
initialStateGas := gas.StateGas // EIP-8037: remember for diff-at-return
|
||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||
if evm.Config.Tracer != nil {
|
||||
// DELEGATECALL inherits value from parent call
|
||||
|
|
@ -415,6 +430,12 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
|||
gas = contract.Gas
|
||||
gasUsed = contract.GasUsed
|
||||
}
|
||||
// EIP-8037 diff-at-return: charge state gas based on actual state diff.
|
||||
if err == nil {
|
||||
if stateErr := evm.chargeStateGasAtReturn(snapshot, initialStateGas, &gas, &gasUsed); stateErr != nil {
|
||||
err = stateErr
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
isRevert := err == ErrExecutionReverted
|
||||
|
|
@ -434,6 +455,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
|||
// Opcodes that attempt to perform such modifications will result in exceptions
|
||||
// instead of performing the modifications.
|
||||
func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []byte, gas GasBudget) (ret []byte, leftOverGas GasBudget, gasUsed GasUsed, err error) {
|
||||
initialStateGas := gas.StateGas // EIP-8037: remember for diff-at-return
|
||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.captureBegin(evm.depth, STATICCALL, caller, addr, input, gas, nil)
|
||||
|
|
@ -464,6 +486,13 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
|||
gas = contract.Gas
|
||||
gasUsed = contract.GasUsed
|
||||
}
|
||||
// EIP-8037 diff-at-return: charge state gas based on actual state diff.
|
||||
// (StaticCall shouldn't create state, but charge anyway for correctness.)
|
||||
if err == nil {
|
||||
if stateErr := evm.chargeStateGasAtReturn(snapshot, initialStateGas, &gas, &gasUsed); stateErr != nil {
|
||||
err = stateErr
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
isRevert := err == ErrExecutionReverted
|
||||
|
|
@ -480,6 +509,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
|||
|
||||
// create creates a new contract using code as deployment code.
|
||||
func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas GasBudget, used GasUsed, err error) {
|
||||
initialStateGas := gas.StateGas // EIP-8037: remember for diff-at-return
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
var nonce uint64
|
||||
|
|
@ -580,6 +610,12 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
|||
contract.IsDeployment = true
|
||||
|
||||
ret, err = evm.initNewContract(contract, address)
|
||||
// EIP-8037 diff-at-return: charge state gas based on actual state diff.
|
||||
if err == nil {
|
||||
if stateErr := evm.chargeStateGasAtReturn(snapshot, initialStateGas, &contract.Gas, &contract.GasUsed); stateErr != nil {
|
||||
err = stateErr
|
||||
}
|
||||
}
|
||||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
|
|
@ -608,21 +644,16 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
|||
}
|
||||
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
// Check max code size BEFORE charging gas so over-max code
|
||||
// does not consume state gas (which would inflate tx_state).
|
||||
// Check max code size BEFORE charging gas.
|
||||
if err := CheckMaxCodeSize(&evm.chainRules, uint64(len(ret))); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
// EIP-8037: Charge regular gas (keccak256 hash) first, then state gas
|
||||
// (code storage). Regular-before-state prevents reservoir inflation.
|
||||
// EIP-8037: Charge regular gas (keccak256 hash) only.
|
||||
// Code deposit state gas is computed from the state diff at call return.
|
||||
regularGas := GasCosts{RegularGas: toWordSize(uint64(len(ret))) * params.Keccak256WordGas}
|
||||
if !contract.UseGas(regularGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
||||
return ret, ErrCodeStoreOutOfGas
|
||||
}
|
||||
stateGas := GasCosts{StateGas: uint64(len(ret)) * evm.Context.CostPerGasByte}
|
||||
if !contract.UseGas(stateGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
||||
return ret, ErrCodeStoreOutOfGas
|
||||
}
|
||||
} else if evm.chainRules.IsEIP4762 {
|
||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas.RegularGas)
|
||||
contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||
|
|
@ -693,6 +724,47 @@ func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash {
|
|||
return evm.StateDB.GetCodeHash(addr)
|
||||
}
|
||||
|
||||
// chargeStateGasAtReturn implements EIP-8037 diff-at-return: after a successful
|
||||
// call, compute the net state growth cost from the journal diff and charge it
|
||||
// from the gas budget (state gas reservoir first, then spillover to regular gas).
|
||||
// initialStateGas is the reservoir value at call entry, needed to compute how
|
||||
// much inner calls already consumed.
|
||||
// Returns an error if the call cannot afford the state gas, in which case the
|
||||
// caller should revert the state to the snapshot.
|
||||
func (evm *EVM) chargeStateGasAtReturn(snapshot int, initialStateGas uint64, gas *GasBudget, gasUsed *GasUsed) error {
|
||||
if !evm.chainRules.IsAmsterdam {
|
||||
return nil
|
||||
}
|
||||
growthCost := evm.StateDB.ComputeStateGrowthCost(snapshot, evm.Context.CostPerGasByte)
|
||||
|
||||
// Inner calls already deducted from reservoir.
|
||||
alreadyPaid := int64(initialStateGas) - int64(gas.StateGas)
|
||||
thisCallCost := growthCost - alreadyPaid
|
||||
|
||||
if thisCallCost <= 0 {
|
||||
// Negative cost = state shrank relative to what was paid, credit the reservoir.
|
||||
if thisCallCost < 0 {
|
||||
gas.StateGas += uint64(-thisCallCost)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
cost := uint64(thisCallCost)
|
||||
if gas.StateGas >= cost {
|
||||
gas.StateGas -= cost
|
||||
gasUsed.StateGas += cost
|
||||
} else if gas.StateGas+gas.RegularGas >= cost {
|
||||
// Spillover: reservoir exhausted, take remainder from regular gas.
|
||||
remainder := cost - gas.StateGas
|
||||
gasUsed.StateGas += gas.StateGas
|
||||
gas.StateGas = 0
|
||||
gas.RegularGas -= remainder
|
||||
gasUsed.RegularGas += remainder
|
||||
} else {
|
||||
return ErrOutOfGas
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChainConfig returns the environment's chain configuration
|
||||
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
|
||||
|
||||
|
|
|
|||
|
|
@ -64,26 +64,26 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
|||
// EXTCODECOPY (stack position 3)
|
||||
// RETURNDATACOPY (stack position 2)
|
||||
func memoryCopierGas(stackpos int) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// Gas for expanding the memory
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
// And gas for copying data, charged per word at param.CopyGas
|
||||
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, words); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,9 +95,9 @@ var (
|
|||
gasReturnDataCopy = memoryCopierGas(2)
|
||||
)
|
||||
|
||||
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
var (
|
||||
y, x = stack.Back(1), stack.Back(0)
|
||||
|
|
@ -114,12 +114,12 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
// 3. From a non-zero to a non-zero (CHANGE)
|
||||
switch {
|
||||
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
|
||||
return GasCosts{RegularGas: params.SstoreSetGas}, nil
|
||||
return params.SstoreSetGas, nil
|
||||
case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
|
||||
evm.StateDB.AddRefund(params.SstoreRefundGas)
|
||||
return GasCosts{RegularGas: params.SstoreClearGas}, nil
|
||||
return params.SstoreClearGas, nil
|
||||
default: // non 0 => non 0 (or 0 => 0)
|
||||
return GasCosts{RegularGas: params.SstoreResetGas}, nil
|
||||
return params.SstoreResetGas, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,16 +139,16 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
// (2.2.2.2.) Otherwise, add 4800 gas to refund counter.
|
||||
value := common.Hash(y.Bytes32())
|
||||
if current == value { // noop (1)
|
||||
return GasCosts{RegularGas: params.NetSstoreNoopGas}, nil
|
||||
return params.NetSstoreNoopGas, nil
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return GasCosts{RegularGas: params.NetSstoreInitGas}, nil
|
||||
return params.NetSstoreInitGas, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
|
||||
}
|
||||
return GasCosts{RegularGas: params.NetSstoreCleanGas}, nil // write existing slot (2.1.2)
|
||||
return params.NetSstoreCleanGas, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
|
|
@ -164,7 +164,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
evm.StateDB.AddRefund(params.NetSstoreResetRefund)
|
||||
}
|
||||
}
|
||||
return GasCosts{RegularGas: params.NetSstoreDirtyGas}, nil
|
||||
return params.NetSstoreDirtyGas, nil
|
||||
}
|
||||
|
||||
// Here come the EIP2200 rules:
|
||||
|
|
@ -182,13 +182,13 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
// (2.2.2.) If original value equals new value (this storage slot is reset):
|
||||
// (2.2.2.1.) If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
|
||||
// (2.2.2.2.) Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
|
||||
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
|
||||
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
|
||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
|
|
@ -198,16 +198,16 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
value := common.Hash(y.Bytes32())
|
||||
|
||||
if current == value { // noop (1)
|
||||
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil
|
||||
return params.SloadGasEIP2200, nil
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return GasCosts{RegularGas: params.SstoreSetGasEIP2200}, nil
|
||||
return params.SstoreSetGasEIP2200, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
}
|
||||
return GasCosts{RegularGas: params.SstoreResetGasEIP2200}, nil // write existing slot (2.1.2)
|
||||
return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
|
|
@ -223,66 +223,62 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||
}
|
||||
}
|
||||
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil // dirty update (2.2)
|
||||
return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
}
|
||||
|
||||
func makeGasLog(n uint64) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
var memorySizeGas uint64
|
||||
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
|
||||
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
// pureMemoryGascost is used by several operations, which aside from their
|
||||
// static cost have a dynamic cost which is solely based on the memory
|
||||
// expansion
|
||||
func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return memoryGasCost(mem, memorySize)
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -293,81 +289,81 @@ var (
|
|||
gasMStore = pureMemoryGascost
|
||||
)
|
||||
|
||||
func gasCreate(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasCreate(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
return pureMemoryGascost(evm, contract, stack, mem, memorySize)
|
||||
}
|
||||
|
||||
func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
// Since size <= the protocol-defined maximum initcode size limit, these multiplication cannot overflow
|
||||
moreGas := params.InitCodeWordGas * ((size + 31) / 32)
|
||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
// Since size <= the protocol-defined maximum initcode size limit, these multiplication cannot overflow
|
||||
moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
|
||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
|
||||
var (
|
||||
|
|
@ -375,12 +371,12 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
|
|||
overflow bool
|
||||
)
|
||||
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
|
||||
var (
|
||||
|
|
@ -388,9 +384,9 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
|
|||
overflow bool
|
||||
)
|
||||
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -401,20 +397,20 @@ var (
|
|||
)
|
||||
|
||||
func makeCallVariantGasCost(intrinsicFunc intrinsicGasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
intrinsic, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsic, stack.Back(0))
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
gas, overflow := math.SafeAdd(intrinsic, evm.callGasTemp)
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -515,16 +511,16 @@ func gasStaticCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
|||
return memoryGasCost(mem, memorySize)
|
||||
}
|
||||
|
||||
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
var gas uint64
|
||||
// EIP150 homestead gas reprice fork:
|
||||
if evm.chainRules.IsEIP150 {
|
||||
gas = params.SelfdestructGasEIP150
|
||||
if gas > contract.Gas.RegularGas {
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
var address = common.Address(stack.Back(0).Bytes20())
|
||||
|
|
@ -541,116 +537,99 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
if !evm.StateDB.HasSelfDestructed(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
|
||||
words := (size + 31) / 32
|
||||
wordGas := params.InitCodeWordGas * words
|
||||
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
|
||||
// EIP-8037 diff-at-return: account creation state gas is computed
|
||||
// from the state diff at call return, not charged here.
|
||||
return gas + wordGas, nil
|
||||
}
|
||||
|
||||
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if err := CheckMaxInitCodeSize(&evm.chainRules, size); err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
// Since size <= MaxInitCodeSizeAmsterdam, these multiplications cannot overflow
|
||||
words := (size + 31) / 32
|
||||
// CREATE2 charges both InitCodeWordGas (EIP-3860) and Keccak256WordGas (for address hashing).
|
||||
wordGas := (params.InitCodeWordGas + params.Keccak256WordGas) * words
|
||||
stateGas := params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||
return GasCosts{RegularGas: gas + wordGas, StateGas: stateGas}, nil
|
||||
// EIP-8037 diff-at-return: account creation state gas is computed
|
||||
// from the state diff at call return, not charged here.
|
||||
return gas + wordGas, nil
|
||||
}
|
||||
|
||||
// gasCall8037 is the stateful gas calculator for CALL in Amsterdam (EIP-8037).
|
||||
// It only returns the state-dependent gas (account creation as state gas).
|
||||
// Memory gas, transfer gas, and callGas are handled by gasCallStateless and
|
||||
// makeCallVariantGasCall.
|
||||
func gasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
gas GasCosts
|
||||
transfersValue = !stack.Back(2).IsZero()
|
||||
address = common.Address(stack.Back(1).Bytes20())
|
||||
)
|
||||
if evm.chainRules.IsEIP158 {
|
||||
if transfersValue && evm.StateDB.Empty(address) {
|
||||
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||
}
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||
}
|
||||
return gas, nil
|
||||
// With diff-at-return, account creation state gas is computed from the state
|
||||
// diff at call return, not charged here.
|
||||
func gasCall8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func gasSelfdestruct8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasSelfdestruct8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
var (
|
||||
gas GasCosts
|
||||
gas uint64
|
||||
address = common.Address(stack.peek().Bytes20())
|
||||
)
|
||||
if !evm.StateDB.AddressInAccessList(address) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(address)
|
||||
gas.RegularGas = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
// Check we have enough regular gas before we add the address to the BAL
|
||||
if contract.Gas.RegularGas < gas.RegularGas {
|
||||
return gas, nil
|
||||
}
|
||||
// if empty and transfers value
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerGasByte
|
||||
gas = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
// EIP-8037 diff-at-return: account creation state gas is computed
|
||||
// from the state diff at call return, not charged here.
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
|
||||
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
|
||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
y, x = stack.Back(1), stack.peek()
|
||||
slot = common.Hash(x.Bytes32())
|
||||
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
|
||||
cost GasCosts
|
||||
cost uint64
|
||||
)
|
||||
// Check slot presence in the access list
|
||||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||
cost = GasCosts{RegularGas: params.ColdSloadCostEIP2929}
|
||||
cost = params.ColdSloadCostEIP2929
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
}
|
||||
|
|
@ -659,24 +638,20 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
|||
if current == value { // noop (1)
|
||||
// EIP 2200 original clause:
|
||||
// return params.SloadGasEIP2200, nil
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // SLOAD_GAS
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
// EIP-8037: Return both regular and state gas. The interpreter
|
||||
// charges regular gas before state gas, preventing reservoir
|
||||
// inflation when the regular charge OOGs.
|
||||
return GasCosts{
|
||||
RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929,
|
||||
StateGas: params.StorageCreationSize * evm.Context.CostPerGasByte,
|
||||
}, nil
|
||||
// EIP-8037 diff-at-return: storage creation state gas is computed
|
||||
// from the state diff at call return, not charged here.
|
||||
return cost + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP3529)
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929}, nil // write existing slot (2.1.2)
|
||||
return cost + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929, nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
|
|
@ -689,7 +664,9 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
|||
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
|
||||
// EIP 2200 Original clause:
|
||||
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
|
||||
evm.StateDB.AddRefund(params.StorageCreationSize*evm.Context.CostPerGasByte + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929 - params.WarmStorageReadCostEIP2929)
|
||||
// EIP-8037 diff-at-return: state gas is handled by the diff,
|
||||
// so refund only covers the regular gas overpayment.
|
||||
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929 - params.WarmStorageReadCostEIP2929)
|
||||
} else { // reset to original existing slot (2.2.2.2)
|
||||
// EIP 2200 Original clause:
|
||||
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||
|
|
@ -701,5 +678,5 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
|||
}
|
||||
// EIP-2200 original clause:
|
||||
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // dirty update (2.2)
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,10 @@ type StateDB interface {
|
|||
RevertToSnapshot(int)
|
||||
Snapshot() int
|
||||
|
||||
// ComputeStateGrowthCost returns the signed net state growth cost (in gas)
|
||||
// between the given snapshot revision and the current state (EIP-8037).
|
||||
ComputeStateGrowthCost(revid int, costPerStateByte uint64) int64
|
||||
|
||||
AddLog(*types.Log)
|
||||
EmitLogsForBurnAccounts()
|
||||
AddPreimage(common.Hash, []byte)
|
||||
|
|
|
|||
|
|
@ -219,32 +219,17 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
|||
}
|
||||
// Consume the gas and return an error if not enough gas is available.
|
||||
// cost is explicitly set so that the capture state defer method can get the proper cost
|
||||
var dynamicCost GasCosts
|
||||
var dynamicCost uint64
|
||||
dynamicCost, err = operation.dynamicGas(evm, contract, stack, mem, memorySize)
|
||||
cost += dynamicCost.RegularGas // for tracing
|
||||
cost += dynamicCost // for tracing
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
|
||||
}
|
||||
// for tracing: this gas consumption event is emitted below in the debug section.
|
||||
if evm.chainRules.IsAmsterdam && dynamicCost.StateGas > 0 {
|
||||
// EIP-8037: charge regular gas before state gas.
|
||||
if contract.Gas.RegularGas < dynamicCost.RegularGas {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
contract.GasUsed.RegularGas += dynamicCost.RegularGas
|
||||
contract.Gas.RegularGas -= dynamicCost.RegularGas
|
||||
stateOnly := GasCosts{StateGas: dynamicCost.StateGas}
|
||||
if contract.Gas.Underflow(stateOnly) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
contract.GasUsed.Add(stateOnly)
|
||||
contract.Gas.Sub(stateOnly)
|
||||
} else if contract.Gas.Underflow(dynamicCost) {
|
||||
if contract.Gas.RegularGas < dynamicCost {
|
||||
return nil, ErrOutOfGas
|
||||
} else {
|
||||
contract.GasUsed.Add(dynamicCost)
|
||||
contract.Gas.Sub(dynamicCost)
|
||||
}
|
||||
contract.Gas.RegularGas -= dynamicCost
|
||||
contract.GasUsed.RegularGas += dynamicCost
|
||||
}
|
||||
|
||||
// Do tracing before potential memory expansion
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
type (
|
||||
executionFunc func(pc *uint64, evm *EVM, callContext *ScopeContext) ([]byte, error)
|
||||
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (GasCosts, error) // last parameter is the requested memory size as a uint64
|
||||
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
||||
intrinsicGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
||||
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
|
||||
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
||||
|
|
|
|||
|
|
@ -27,24 +27,24 @@ import (
|
|||
)
|
||||
|
||||
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
|
||||
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
|
||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
y, x = stack.Back(1), stack.peek()
|
||||
slot = common.Hash(x.Bytes32())
|
||||
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
|
||||
cost = GasCosts{RegularGas: 0}
|
||||
cost uint64
|
||||
)
|
||||
// Check slot presence in the access list
|
||||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||
cost = GasCosts{RegularGas: params.ColdSloadCostEIP2929}
|
||||
cost = params.ColdSloadCostEIP2929
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
}
|
||||
|
|
@ -53,18 +53,18 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
if current == value { // noop (1)
|
||||
// EIP 2200 original clause:
|
||||
// return params.SloadGasEIP2200, nil
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // SLOAD_GAS
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
||||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.SstoreSetGasEIP2200}, nil
|
||||
return cost + params.SstoreSetGasEIP2200, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(clearingRefund)
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return GasCosts{RegularGas: cost.RegularGas + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929)}, nil // write existing slot (2.1.2)
|
||||
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
|
|
@ -89,7 +89,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
}
|
||||
// EIP-2200 original clause:
|
||||
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
return GasCosts{RegularGas: cost.RegularGas + params.WarmStorageReadCostEIP2929}, nil // dirty update (2.2)
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
// whose storage is being read) is not yet in accessed_storage_keys,
|
||||
// charge 2100 gas and add the pair to accessed_storage_keys.
|
||||
// If the pair is already in accessed_storage_keys, charge 100 gas.
|
||||
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
loc := stack.peek()
|
||||
slot := common.Hash(loc.Bytes32())
|
||||
// Check slot presence in the access list
|
||||
|
|
@ -106,9 +106,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
// If he does afford it, we can skip checking the same thing later on, during execution
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
return GasCosts{RegularGas: params.ColdSloadCostEIP2929}, nil
|
||||
return params.ColdSloadCostEIP2929, nil
|
||||
}
|
||||
return GasCosts{RegularGas: params.WarmStorageReadCostEIP2929}, nil
|
||||
return params.WarmStorageReadCostEIP2929, nil
|
||||
}
|
||||
|
||||
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
|
||||
|
|
@ -116,11 +116,11 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
// > If the target is not in accessed_addresses,
|
||||
// > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
|
||||
// > Otherwise, charge WARM_STORAGE_READ_COST gas.
|
||||
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// memory expansion first (dynamic part of pre-2929 implementation)
|
||||
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
|
|
@ -128,10 +128,9 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
|||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
var overflow bool
|
||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
|
@ -143,20 +142,20 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
|||
// - extcodehash,
|
||||
// - extcodesize,
|
||||
// - (ext) balance
|
||||
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The warm storage read cost is already charged as constantGas
|
||||
return GasCosts{RegularGas: params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929}, nil
|
||||
return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
|
||||
}
|
||||
return GasCosts{RegularGas: 0}, nil
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
addr := common.Address(stack.Back(addressPosition).Bytes20())
|
||||
// Check slot presence in the access list
|
||||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
||||
|
|
@ -168,7 +167,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
|||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// gas for call
|
||||
if !contract.UseGas(GasCosts{RegularGas: coldCost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
// Now call the old calculator, which takes into account
|
||||
|
|
@ -187,8 +186,8 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
|||
contract.Gas.RegularGas += coldCost
|
||||
|
||||
var overflow bool
|
||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, coldCost); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
|
@ -224,13 +223,13 @@ var (
|
|||
|
||||
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529
|
||||
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
gas uint64
|
||||
address = common.Address(stack.peek().Bytes20())
|
||||
)
|
||||
if evm.readOnly {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
if !evm.StateDB.AddressInAccessList(address) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
|
|
@ -240,11 +239,11 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||||
// it can effectively prevent accessing the states in the following steps
|
||||
if contract.Gas.RegularGas < gas {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
if gas > contract.Gas.RegularGas {
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
// if empty and transfers value
|
||||
|
|
@ -254,7 +253,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
}
|
||||
return GasCosts{RegularGas: gas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
return gasFunc
|
||||
}
|
||||
|
|
@ -268,30 +267,30 @@ var (
|
|||
innerGasCallEIP8037 = makeCallVariantGasCallEIP8037(gasCallIntrinsic8037, gasCall8037)
|
||||
)
|
||||
|
||||
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// Return early if this call attempts to transfer value in a static context.
|
||||
// Although it's checked in `gasCall`, EIP-7702 loads the target's code before
|
||||
// to determine if it is resolving a delegation. This could incorrectly record
|
||||
// the target in the block access list (BAL) if the call later fails.
|
||||
transfersValue := !stack.Back(2).IsZero()
|
||||
if evm.readOnly && transfersValue {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
return innerGasCallEIP7702(evm, contract, stack, mem, memorySize)
|
||||
}
|
||||
|
||||
func gasCallEIP8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasCallEIP8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// Same write-protection guard as gasCallEIP7702: check before any gas
|
||||
// charging to avoid incorrectly recording in the access list.
|
||||
transfersValue := !stack.Back(2).IsZero()
|
||||
if evm.readOnly && transfersValue {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
return 0, ErrWriteProtection
|
||||
}
|
||||
return innerGasCallEIP8037(evm, contract, stack, mem, memorySize)
|
||||
}
|
||||
|
||||
func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
eip2929Cost uint64
|
||||
eip7702Cost uint64
|
||||
|
|
@ -310,7 +309,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
|||
// Charge the remaining difference here already, to correctly calculate
|
||||
// available gas for call
|
||||
if !contract.UseGas(GasCosts{RegularGas: eip2929Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -321,13 +320,13 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
|||
// - create new account
|
||||
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||||
// it can effectively prevent accessing the states in the following steps.
|
||||
// It's an essential safeguard before any stateful check.
|
||||
if contract.Gas.RegularGas < intrinsicCost {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
|
||||
// Check if code is a delegation and if so, charge for resolution.
|
||||
|
|
@ -339,14 +338,14 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
|||
eip7702Cost = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
if !contract.UseGas(GasCosts{RegularGas: eip7702Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
// Calculate the gas budget for the nested call. The costs defined by
|
||||
// EIP-2929 and EIP-7702 have already been applied.
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsicCost, stack.Back(0))
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
// Temporarily add the gas charge back to the contract and return value. By
|
||||
// adding it to the return, it will be charged outside of this function, as
|
||||
|
|
@ -365,15 +364,15 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
|||
totalCost uint64
|
||||
)
|
||||
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: totalCost}, nil
|
||||
return totalCost, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -382,7 +381,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
|||
// intrinsicFunc computes the regular gas (memory + transfer, no new account creation).
|
||||
// stateGasFunc computes the state gas (new account creation as state gas).
|
||||
func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
eip2929Cost uint64
|
||||
eip7702Cost uint64
|
||||
|
|
@ -393,21 +392,21 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
|
|||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
eip2929Cost = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||
if !contract.UseGas(GasCosts{RegularGas: eip2929Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
||||
// Compute intrinsic cost (memory + transfer, no new account creation).
|
||||
intrinsicCost, err := intrinsicFunc(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Charge intrinsic cost directly (regular gas). This must happen
|
||||
// BEFORE state gas to prevent reservoir inflation, and also serves
|
||||
// as the OOG guard before stateful operations.
|
||||
if !contract.UseGas(GasCosts{RegularGas: intrinsicCost}, evm.Config.Tracer, tracing.GasChangeCallOpCode) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
|
||||
// EIP-7702 delegation check.
|
||||
|
|
@ -419,28 +418,21 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
|
|||
eip7702Cost = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
if !contract.UseGas(GasCosts{RegularGas: eip7702Cost}, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
||||
// Compute and charge state gas (new account creation) AFTER regular gas.
|
||||
stateGas, err := stateGasFunc(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
if stateGas.StateGas > 0 {
|
||||
stateGasCost := GasCosts{StateGas: stateGas.StateGas}
|
||||
if contract.Gas.Underflow(stateGasCost) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
contract.GasUsed.Add(stateGasCost)
|
||||
contract.Gas.Sub(stateGasCost)
|
||||
// EIP-8037 diff-at-return: state gas (new account creation) is
|
||||
// computed from the state diff at call return, not charged here.
|
||||
// We still call stateGasFunc for any side-effects/error checks.
|
||||
if _, err = stateGasFunc(evm, contract, stack, mem, memorySize); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Calculate the gas budget for the nested call (63/64 rule).
|
||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, 0, stack.Back(0))
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Temporarily undo direct regular charges for tracer reporting.
|
||||
|
|
@ -454,14 +446,14 @@ func makeCallVariantGasCallEIP8037(intrinsicFunc intrinsicGasFunc, stateGasFunc
|
|||
totalCost uint64
|
||||
)
|
||||
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if totalCost, overflow = math.SafeAdd(totalCost, intrinsicCost); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: totalCost}, nil
|
||||
return totalCost, nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,40 +24,40 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas.RegularGas, true)}, nil
|
||||
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas.RegularGas, true), nil
|
||||
}
|
||||
|
||||
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas.RegularGas, true)}, nil
|
||||
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas.RegularGas, true), nil
|
||||
}
|
||||
|
||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
address := stack.peek().Bytes20()
|
||||
return GasCosts{RegularGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true)}, nil
|
||||
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true), nil
|
||||
}
|
||||
|
||||
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
address := stack.peek().Bytes20()
|
||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||
return GasCosts{}, nil
|
||||
return 0, nil
|
||||
}
|
||||
return GasCosts{RegularGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true)}, nil
|
||||
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true), nil
|
||||
}
|
||||
|
||||
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
address := stack.peek().Bytes20()
|
||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||
return GasCosts{}, nil
|
||||
return 0, nil
|
||||
}
|
||||
return GasCosts{RegularGas: evm.AccessEvents.CodeHashGas(address, false, contract.Gas.RegularGas, true)}, nil
|
||||
return evm.AccessEvents.CodeHashGas(address, false, contract.Gas.RegularGas, true), nil
|
||||
}
|
||||
|
||||
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
target = common.Address(stack.Back(1).Bytes20())
|
||||
witnessGas GasCosts
|
||||
witnessGas uint64
|
||||
_, isPrecompile = evm.precompile(target)
|
||||
isSystemContract = target == params.HistoryStorageAddress
|
||||
)
|
||||
|
|
@ -67,36 +67,37 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) ga
|
|||
if withTransferCosts && !stack.Back(2).IsZero() {
|
||||
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas.RegularGas)
|
||||
if wantedValueTransferWitnessGas > contract.Gas.RegularGas {
|
||||
return GasCosts{RegularGas: wantedValueTransferWitnessGas}, nil
|
||||
return wantedValueTransferWitnessGas, nil
|
||||
}
|
||||
witnessGas = GasCosts{RegularGas: wantedValueTransferWitnessGas}
|
||||
witnessGas = wantedValueTransferWitnessGas
|
||||
} else if isPrecompile || isSystemContract {
|
||||
witnessGas = GasCosts{RegularGas: params.WarmStorageReadCostEIP2929}
|
||||
witnessGas = params.WarmStorageReadCostEIP2929
|
||||
} else {
|
||||
// The charging for the value transfer is done BEFORE subtracting
|
||||
// the 1/64th gas, as this is considered part of the CALL instruction.
|
||||
// (so before we get to this point)
|
||||
// But the message call is part of the subcall, for which only 63/64th
|
||||
// of the gas should be available.
|
||||
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas.RegularGas-witnessGas.RegularGas)
|
||||
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas.RegularGas-witnessGas)
|
||||
var overflow bool
|
||||
if witnessGas.RegularGas, overflow = math.SafeAdd(witnessGas.RegularGas, wantedMessageCallWitnessGas); overflow {
|
||||
return GasCosts{RegularGas: 0}, ErrGasUintOverflow
|
||||
if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if witnessGas.RegularGas > contract.Gas.RegularGas {
|
||||
if witnessGas > contract.Gas.RegularGas {
|
||||
return witnessGas, nil
|
||||
}
|
||||
}
|
||||
|
||||
contract.Gas.Sub(witnessGas)
|
||||
witnessGasCost := GasCosts{RegularGas: witnessGas}
|
||||
contract.Gas.Sub(witnessGasCost)
|
||||
// if the operation fails, adds witness gas to the gas before returning the error
|
||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
contract.Gas.Add(witnessGas) // restore witness gas so that it can be charged at the callsite
|
||||
contract.Gas.Add(witnessGasCost) // restore witness gas so that it can be charged at the callsite
|
||||
var overflow bool
|
||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, witnessGas.RegularGas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas.RegularGas}, err
|
||||
return gas, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,20 +108,20 @@ var (
|
|||
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false)
|
||||
)
|
||||
|
||||
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
beneficiaryAddr := common.Address(stack.peek().Bytes20())
|
||||
if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile {
|
||||
return GasCosts{}, nil
|
||||
return 0, nil
|
||||
}
|
||||
if contract.IsSystemCall {
|
||||
return GasCosts{}, nil
|
||||
return 0, nil
|
||||
}
|
||||
contractAddr := contract.Address()
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas.RegularGas, false)
|
||||
if wanted > contract.Gas.RegularGas {
|
||||
return GasCosts{RegularGas: wanted}, nil
|
||||
return wanted, nil
|
||||
}
|
||||
statelessGas := GasCosts{RegularGas: wanted}
|
||||
statelessGas := wanted
|
||||
balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0
|
||||
_, isPrecompile := evm.precompile(beneficiaryAddr)
|
||||
isSystemContract := beneficiaryAddr == params.HistoryStorageAddress
|
||||
|
|
@ -130,39 +131,39 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
|||
}
|
||||
|
||||
if contractAddr != beneficiaryAddr {
|
||||
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas.RegularGas-statelessGas.RegularGas, false)
|
||||
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas {
|
||||
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil
|
||||
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas.RegularGas-statelessGas, false)
|
||||
if wanted > contract.Gas.RegularGas-statelessGas {
|
||||
return statelessGas + wanted, nil
|
||||
}
|
||||
statelessGas.RegularGas += wanted
|
||||
statelessGas += wanted
|
||||
}
|
||||
// Charge write costs if it transfers value
|
||||
if !balanceIsZero {
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas.RegularGas-statelessGas.RegularGas, false)
|
||||
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas {
|
||||
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil
|
||||
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas.RegularGas-statelessGas, false)
|
||||
if wanted > contract.Gas.RegularGas-statelessGas {
|
||||
return statelessGas + wanted, nil
|
||||
}
|
||||
statelessGas.RegularGas += wanted
|
||||
statelessGas += wanted
|
||||
|
||||
if contractAddr != beneficiaryAddr {
|
||||
if evm.StateDB.Exist(beneficiaryAddr) {
|
||||
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas.RegularGas, false)
|
||||
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas, false)
|
||||
} else {
|
||||
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas.RegularGas)
|
||||
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas)
|
||||
}
|
||||
if wanted > contract.Gas.RegularGas-statelessGas.RegularGas {
|
||||
return GasCosts{RegularGas: statelessGas.RegularGas + wanted}, nil
|
||||
if wanted > contract.Gas.RegularGas-statelessGas {
|
||||
return statelessGas + wanted, nil
|
||||
}
|
||||
statelessGas.RegularGas += wanted
|
||||
statelessGas += wanted
|
||||
}
|
||||
}
|
||||
return statelessGas, nil
|
||||
}
|
||||
|
||||
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := gasCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
if !contract.IsDeployment && !contract.IsSystemCall {
|
||||
var (
|
||||
|
|
@ -175,31 +176,31 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
|||
}
|
||||
|
||||
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
||||
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas.RegularGas-gas.RegularGas)
|
||||
gas.RegularGas += wanted
|
||||
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas.RegularGas-gas)
|
||||
gas += wanted
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// memory expansion first (dynamic part of pre-2929 implementation)
|
||||
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
return 0, err
|
||||
}
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
_, isPrecompile := evm.precompile(addr)
|
||||
if isPrecompile || addr == params.HistoryStorageAddress {
|
||||
var overflow bool
|
||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, params.WarmStorageReadCostEIP2929); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas.RegularGas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas.RegularGas-gas.RegularGas, true)
|
||||
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas.RegularGas-gas, true)
|
||||
var overflow bool
|
||||
if gas.RegularGas, overflow = math.SafeAdd(gas.RegularGas, wgas); overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return GasCosts{RegularGas: gas.RegularGas}, nil
|
||||
return gas, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue