core/vm: introduce eip-8037 gas metering for SSTORE

This commit is contained in:
Gary Rong 2026-04-30 22:16:40 +08:00
parent 01036bed83
commit 93ffef90cc
4 changed files with 62 additions and 9 deletions

View file

@ -43,6 +43,7 @@ var activators = map[int]func(*JumpTable){
7939: enable7939, 7939: enable7939,
8024: enable8024, 8024: enable8024,
7843: enable7843, 7843: enable7843,
8037: enable8037,
} }
// EnableEIP enables the given EIP on the config. // EnableEIP enables the given EIP on the config.
@ -590,3 +591,9 @@ func enable7843(jt *JumpTable) {
maxStack: maxStack(0, 1), maxStack: maxStack(0, 1),
} }
} }
// enable8037 applies EIP-8037: State Creation Gas Cost Increase
func enable8037(jt *JumpTable) {
jt[SLOAD].constantGas = params.SloadGasEIP2200
jt[SSTORE].dynamicGas = gasSStoreEIP8037
}

View file

@ -226,6 +226,52 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil // dirty update (2.2) return GasCosts{RegularGas: params.SloadGasEIP2200}, nil // dirty update (2.2)
} }
// EIP-8037 constants. EIP-8037 only diverges from EIP-2200 on slots whose
// tx-original is zero AND on writes that cross the zero/non-zero boundary
// (i.e. a state creation or its in-tx undo). Everything else stays on the
// EIP-2200 schedule.
const (
stateBytesPerSlotEIP8037 = 64 // bytes attributed to one slot's state
costPerStateByteEIP8037 = 1157 // gas per state-creation byte
sstoreBaseGasEIP8037 = 2900 // regular gas baseline for the EIP-8037 transitions
stateGasEIP8037 = stateBytesPerSlotEIP8037 * costPerStateByteEIP8037
)
// gasSStoreEIP8037 prices SSTORE under EIP-8037.
//
// Two specific (original, current, value) shapes get the new pricing:
// - O == 0, C == 0, V != 0 — creating fresh state in this tx.
// Charge stateGasEIP8037 of state gas and sstoreBaseGasEIP8037 of regular gas.
// - O == 0, C != 0, V == 0 — undoing the in-tx creation.
// Refund stateGasEIP8037 of state gas (negative StateGas in the cost
// vector) and pay sstoreBaseGasEIP8037 of regular gas.
//
// Every other shape — including O != 0 of any kind, and O == 0 cases that
// don't cross zero — defers to gasSStoreEIP2200 unchanged.
func gasSStoreEIP8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
if evm.readOnly {
return GasCosts{}, ErrWriteProtection
}
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
}
var (
zero = common.Hash{}
y, x = stack.back(1), stack.back(0)
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
value = common.Hash(y.Bytes32())
)
if original == zero {
switch {
case current == zero && value != zero: // creation
return GasCosts{RegularGas: sstoreBaseGasEIP8037, StateGas: stateGasEIP8037}, nil
case current != zero && value == zero: // undo creation
return GasCosts{RegularGas: sstoreBaseGasEIP8037, StateGas: -stateGasEIP8037}, nil
}
}
return gasSStoreEIP2200(evm, contract, stack, mem, memorySize)
}
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) (GasCosts, error) {
requestedSize, overflow := stack.back(1).Uint64WithOverflow() requestedSize, overflow := stack.back(1).Uint64WithOverflow()

View file

@ -18,17 +18,16 @@ package vm
import "fmt" import "fmt"
// GasCosts denotes a vector of gas costs in the // GasCosts denotes a vector of gas costs in the multidimensional metering
// multidimensional metering paradigm. It represents the cost // paradigm. It represents the cost charged by an individual operation.
// charged by an individual operation. //
// StateGas is signed so an operation can express a state-gas refund
// (negative cost) — e.g. an SSTORE that undoes an in-tx state creation.
// Such a refund flows back into the StateGas reservoir of the GasBudget,
// not into RegularGas.
type GasCosts struct { type GasCosts struct {
RegularGas uint64 RegularGas uint64
StateGas uint64 StateGas int64
}
// Sum returns the total gas (regular + state).
func (g GasCosts) Sum() uint64 {
return g.RegularGas + g.StateGas
} }
// String returns a visual representation of the gas vector. // String returns a visual representation of the gas vector.

View file

@ -97,6 +97,7 @@ func newAmsterdamInstructionSet() JumpTable {
instructionSet := newOsakaInstructionSet() instructionSet := newOsakaInstructionSet()
enable7843(&instructionSet) // EIP-7843 (SLOTNUM opcode) enable7843(&instructionSet) // EIP-7843 (SLOTNUM opcode)
enable8024(&instructionSet) // EIP-8024 (Backward compatible SWAPN, DUPN, EXCHANGE) enable8024(&instructionSet) // EIP-8024 (Backward compatible SWAPN, DUPN, EXCHANGE)
enable8037(&instructionSet) // EIP-8037 (State Creation Gas Cost Increase)
return validate(instructionSet) return validate(instructionSet)
} }