core: last fixes

This commit is contained in:
MariusVanDerWijden 2026-04-28 22:33:01 +02:00
parent 48d7bf08d7
commit f71f4da77a
7 changed files with 110 additions and 7 deletions

View file

@ -241,6 +241,19 @@ func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *
return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, evm)
}
// systemCallGasBudget returns the gas budget for system calls. Pre-Amsterdam
// the budget is 30M regular gas. Post-Amsterdam (EIP-8037), an additional
// state-gas reservoir of `STATE_BYTES_PER_STORAGE_SET × CPSB × SYSTEM_MAX_SSTORES_PER_CALL`
// is provided to cover the expected new SSTOREs in system contracts.
func systemCallGasBudget(evm *vm.EVM) vm.GasBudget {
const regular = 30_000_000
if evm.ChainConfig().IsAmsterdam(evm.Context.BlockNumber, evm.Context.Time) {
stateGas := params.StorageCreationSize * evm.Context.CostPerStateByte * params.SystemMaxSstoresPerCall
return vm.NewGasBudget(regular, stateGas)
}
return vm.NewGasBudgetReg(regular)
}
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
// contract. This method is exported to be used in tests.
func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
@ -261,7 +274,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
}
evm.SetTxContext(NewEVMTxContext(msg))
evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress)
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudgetReg(30_000_000), common.U2560)
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, systemCallGasBudget(evm), common.U2560)
if evm.StateDB.AccessEvents() != nil {
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
}
@ -288,7 +301,7 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
}
evm.SetTxContext(NewEVMTxContext(msg))
evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress)
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudgetReg(30_000_000), common.U2560)
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, systemCallGasBudget(evm), common.U2560)
if err != nil {
panic(err)
}
@ -327,7 +340,7 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
}
evm.SetTxContext(NewEVMTxContext(msg))
evm.StateDB.AddAddressToAccessList(addr)
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudgetReg(30_000_000), common.U2560)
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, systemCallGasBudget(evm), common.U2560)
if evm.StateDB.AccessEvents() != nil {
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
}

View file

@ -769,10 +769,10 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
return 0, err
}
// If the account already exists in state, refund the new account cost
// charged in the intrinsic calculation.
// If the account is not empty (per EIP-161: non-zero nonce, balance, or
// code) refund the new account cost charged in the intrinsic calculation.
var refund uint64
if st.state.Exist(authority) {
if !st.state.Empty(authority) {
if rules.IsAmsterdam {
// EIP-8037: refund account creation state gas to the reservoir
refund = params.AccountCreationSize * st.evm.Context.CostPerStateByte

View file

@ -44,6 +44,7 @@ var activators = map[int]func(*JumpTable){
7939: enable7939,
8024: enable8024,
7843: enable7843,
8037: enable8037,
}
// EnableEIP enables the given EIP on the config.
@ -169,6 +170,13 @@ func enable3529(jt *JumpTable) {
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529
}
// enable8037 enables EIP-8037 SSTORE repricing: the regular-gas portion of
// new slot creation and same-tx 0→X→0 reset is reduced; the state-gas
// portion is charged/refunded at frame-end via the journal.
func enable8037(jt *JumpTable) {
jt[SSTORE].dynamicGas = gasSStoreEIP8037
}
// enable3198 applies EIP-3198 (BASEFEE Opcode)
// - Adds an opcode that returns the current block's base fee.
func enable3198(jt *JumpTable) {

View file

@ -660,7 +660,15 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
}
if !evm.chainRules.IsEIP4762 {
createDataGas := uint64(len(ret)) * params.CreateDataGas
var createDataGas uint64
if evm.chainRules.IsAmsterdam {
// EIP-8037: regular gas portion is the keccak hashing cost
// (6 × ⌈L/32⌉). The state-gas portion (L × CPSB) is charged
// at frame end via the journal's codeChange walker.
createDataGas = ((uint64(len(ret)) + 31) / 32) * params.Keccak256WordGas
} else {
createDataGas = uint64(len(ret)) * params.CreateDataGas
}
if !contract.UseGas(GasCosts{RegularGas: createDataGas}, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
return ret, ErrCodeStoreOutOfGas
}

View file

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

View file

@ -222,8 +222,80 @@ var (
// gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
// gasSStoreEIP8037 implements gas cost for SSTORE under EIP-8037.
// New slot creation (orig=0, current=0, value!=0) is repriced from
// SstoreSetGas (20,000) to SstoreUpdateGas - ColdSloadCost (2,900); the
// state-gas portion (32 × CPSB) is charged at frame-end via the journal.
// Likewise the same-tx 0→X→0 reset refund is reduced from 19,900 to
// SstoreUpdateGas - ColdSloadCost - WarmStorageReadCost (2,800); the
// state-gas refund is also handled at frame-end.
gasSStoreEIP8037 = makeGasSStoreFuncAmsterdam(params.SstoreClearsScheduleRefundEIP3529)
)
// makeGasSStoreFuncAmsterdam returns the EIP-8037 SSTORE gas function. It is
// identical to makeGasSStoreFunc except that the regular-gas portion of new
// slot creation and same-tx 0→X→0 reset is reduced (the state-gas portion is
// charged/refunded at frame-end via the journal).
func makeGasSStoreFuncAmsterdam(clearingRefund uint64) gasFunc {
return func(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 (
y, x = stack.Back(1), stack.peek()
slot = common.Hash(x.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
cost = uint64(0)
)
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
cost = params.ColdSloadCostEIP2929
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
}
value := common.Hash(y.Bytes32())
// EIP-8037: regular-gas portion of new slot creation is the storage
// update cost minus cold sload (2,900). State-gas portion is at
// frame-end.
sstoreNewSlotRegularGas := params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929
if current == value { // noop
return GasCosts{RegularGas: cost + params.WarmStorageReadCostEIP2929}, nil
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return GasCosts{RegularGas: cost + sstoreNewSlotRegularGas}, nil
}
if value == (common.Hash{}) { // delete pre-existing slot
evm.StateDB.AddRefund(clearingRefund)
}
return GasCosts{RegularGas: cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929)}, nil
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(clearingRefund)
} else if value == (common.Hash{}) { // delete dirty (2.2.1.2)
evm.StateDB.AddRefund(clearingRefund)
}
}
if original == value {
if original == (common.Hash{}) { // 0→X→0: reset to original-zero
// EIP-8037: regular-gas refund is reduced because the
// original SET cost was already reduced to
// sstoreNewSlotRegularGas. State-gas refund (32 × CPSB)
// is applied at frame-end.
evm.StateDB.AddRefund(sstoreNewSlotRegularGas - params.WarmStorageReadCostEIP2929)
} else { // reset to original existing slot
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
}
}
return GasCosts{RegularGas: cost + params.WarmStorageReadCostEIP2929}, nil
}
}
// 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) {

View file

@ -193,6 +193,7 @@ const (
AccountCreationSize = 112
StorageCreationSize = 32
AuthorizationCreationSize = 23
SystemMaxSstoresPerCall = 16 // EIP-8037: upper bound on new SSTOREs per system call
GasBlockAccessListItem = 2000 // EIP-7928: gas cost per BAL item for gas limit check
)