core: misc fixes (claude)

This commit is contained in:
MariusVanDerWijden 2026-04-29 10:35:01 +02:00
parent 87a4f9c674
commit 42a87ae77a
7 changed files with 88 additions and 12 deletions

View file

@ -5,10 +5,10 @@
# https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0
a3192784375acec7eaec492799d5c5d0c47a2909a3cc40178898e4ecd20cc416 fixtures_develop.tar.gz
# version:spec-tests-bal v5.6.1
# version:spec-tests-bal v8037.0.0
# https://github.com/ethereum/execution-spec-tests/releases
# https://github.com/ethereum/execution-spec-tests/releases/download/bal%40v5.6.1
741530c88f6a48c15184d1504316c02c3a76c2322c410a04b643a85185dc62e9 fixtures_bal.tar.gz
# https://github.com/ethereum/execution-spec-tests/releases/download/snobal-devnet-5%40v8037.0.0
0b0d5b14e9f2b7d3a7037f42703d76c2baf2b20cb550bd6b34ea761b7fd407df fixtures_snobal-devnet-5.tar.gz
# version:golang 1.25.9
# https://go.dev/dl/

View file

@ -455,7 +455,7 @@ func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string
func downloadBALSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string {
ext := ".tar.gz"
base := "fixtures_bal"
base := "fixtures_snobal-devnet-5"
archivePath := filepath.Join(cachedir, base+ext)
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
log.Fatal(err)

View file

@ -287,7 +287,20 @@ func (j *journal) stateChangedBytes(revid int, stateObjects map[common.Address]*
}
var totalBytes int64
for range created {
// Per EIP-8037 spec compute_state_byte_diff: only count +112 for accounts
// that exist at frame exit. Per EIP-161, accounts that end up with
// nonce=0, balance=0, and empty code are considered non-existent and
// pruned — these don't count even though we created the object record
// (e.g. AddBalance(addr, 0) for a zero-value SELFDESTRUCT to a
// non-existent beneficiary).
for addr := range created {
obj := stateObjects[addr]
if obj == nil {
continue
}
if obj.empty() {
continue
}
totalBytes += CostPerAccount
}
for sk, si := range slots {

View file

@ -650,8 +650,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
if rules.IsAmsterdam {
if vmerr == nil {
outerBytes := st.state.StateChangedBytes(outerSnapshot, false)
// Refund state gas for selfdestructed accounts.
outerBytes -= st.state.SelfDestructRefundBytes()
// For contract-creation txs the intrinsic already paid for the
// account creation; subtract it so we don't double-charge.
if contractCreation {
@ -661,6 +659,20 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
alreadyPaid := st.gasRemaining.StateGasUsed
thisCallCost := outerBytes*int64(st.evm.Context.CostPerStateByte) - alreadyPaid
st.gasRemaining.Charge(vm.GasCosts{StateGas: thisCallCost})
// EIP-8037 + EIP-6780: refund state gas for accounts created and
// selfdestructed in the same tx. Per spec interpreter.py:200-201,
// this runs at the top-level after the frame's apply_frame_state_gas:
// reservoir is credited and state_gas_used is decremented. The
// decrement may go negative when account-creation was paid via the
// intrinsic (CREATE tx) rather than execution state-gas; the
// negative offsets the intrinsic at tx-level state-gas accounting.
refundBytes := st.state.SelfDestructRefundBytes()
if refundBytes > 0 {
refundCost := refundBytes * int64(st.evm.Context.CostPerStateByte)
st.gasRemaining.StateGas += uint64(refundCost)
st.gasRemaining.StateGasUsed -= refundCost
}
} else {
// On top-level error, restore state-gas reservoir and reset
// the state-gas-used counter; state changes are reverted, no

View file

@ -346,8 +346,16 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
bytesCharged, alreadyPaid, thisCallCost, gas.CanAfford(stateGasCost))
}
if !gas.CanAfford(stateGasCost) {
// EIP-8037 spec apply_frame_state_gas OOG branch: roll back
// state, set OOG, preserve reservoir + remaining regular gas.
// Per incorporate_child_on_error: child's reservoir + max(0,
// state_gas_used) flows to caller's reservoir; state_gas_used
// is dropped. Convert here so RefundGas does the right thing.
evm.StateDB.RevertToSnapshot(snapshot1)
gas.Exhaust()
if gas.StateGasUsed > 0 {
gas.StateGas += uint64(gas.StateGasUsed)
}
gas.StateGasUsed = 0
return ret, gas, ErrOutOfGas
}
gas.Charge(stateGasCost)
@ -432,7 +440,17 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
bytesCharged, alreadyPaid, thisCallCost, gas.CanAfford(stateGasCost))
}
if !gas.CanAfford(stateGasCost) {
gas.Exhaust()
// EIP-8037 spec apply_frame_state_gas OOG branch: roll back
// state, set OOG, preserve reservoir and remaining regular
// gas. Per incorporate_child_on_error: child's reservoir
// AND max(0, state_gas_used) both flow to the caller's
// reservoir; state_gas_used itself is dropped (not
// propagated as credit). Convert here.
evm.StateDB.RevertToSnapshot(snapshot1)
if gas.StateGasUsed > 0 {
gas.StateGas += uint64(gas.StateGasUsed)
}
gas.StateGasUsed = 0
return ret, gas, ErrOutOfGas
}
gas.Charge(stateGasCost)
@ -512,7 +530,17 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
bytesCharged, alreadyPaid, thisCallCost, gas.CanAfford(stateGasCost))
}
if !gas.CanAfford(stateGasCost) {
gas.Exhaust()
// EIP-8037 spec apply_frame_state_gas OOG branch: roll back
// state, set OOG, preserve reservoir and remaining regular
// gas. Per incorporate_child_on_error: child's reservoir
// AND max(0, state_gas_used) both flow to the caller's
// reservoir; state_gas_used itself is dropped (not
// propagated as credit). Convert here.
evm.StateDB.RevertToSnapshot(snapshot1)
if gas.StateGasUsed > 0 {
gas.StateGas += uint64(gas.StateGasUsed)
}
gas.StateGasUsed = 0
return ret, gas, ErrOutOfGas
}
gas.Charge(stateGasCost)
@ -710,8 +738,15 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
thisCallCost := bytesCharged*int64(evm.Context.CostPerStateByte) - alreadyPaid
stateGasCost := GasCosts{StateGas: thisCallCost}
if !contract.Gas.CanAfford(stateGasCost) {
// EIP-8037 spec: state-gas OOG rolls back state, preserves
// reservoir+regular, converts max(0, state_gas_used) into
// reservoir credit (incorporate_child_on_error semantics),
// then drops state_gas_used.
evm.StateDB.RevertToSnapshot(snapshot1)
contract.Gas.Exhaust()
if contract.Gas.StateGasUsed > 0 {
contract.Gas.StateGas += uint64(contract.Gas.StateGasUsed)
}
contract.Gas.StateGasUsed = 0
return ret, address, contract.Gas, ErrOutOfGas
}
contract.Gas.Charge(stateGasCost)

View file

@ -127,6 +127,12 @@ func (g GasBudget) CanAfford(cost GasCosts) bool {
// Charge deducts the given gas cost from the budget. It returns the
// pre-charge regular gas value and false if the budget does not have
// sufficient gas to cover the cost.
//
// Per EIP-8037 spec apply_frame_state_gas: a negative state-gas cost
// (refund from net state shrinkage) credits only the reservoir, not the
// per-frame state_gas_used tracker. Otherwise the negative would
// propagate to the parent's already_paid and cause the parent's
// frame-end charge to be over-counted.
func (g *GasBudget) Charge(cost GasCosts) (uint64, bool) {
prior := g.RegularGas
if !g.CanAfford(cost) {
@ -134,11 +140,11 @@ func (g *GasBudget) Charge(cost GasCosts) (uint64, bool) {
}
g.RegularGas -= cost.RegularGas
g.RegularGasUsed += cost.RegularGas
g.StateGasUsed += cost.StateGas
if cost.StateGas < 0 {
g.StateGas -= uint64(cost.StateGas)
return prior, true
}
g.StateGasUsed += cost.StateGas
if uint64(cost.StateGas) > g.StateGas {
spillover := uint64(cost.StateGas) - g.StateGas
g.StateGas = 0

View file

@ -17,7 +17,9 @@
package vm
import (
"fmt"
"math"
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing"
@ -771,7 +773,15 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// reset parent's reservoir to zero. Leftover comes back via RefundGas.
childStateGas := scope.Contract.Gas.StateGas
scope.Contract.Gas.StateGas = 0
if os.Getenv("DEBUG_8037") != "" {
fmt.Fprintf(os.Stderr, " opCall depth=%d childStateGas=%d gas=%d to=%v\n",
evm.depth, childStateGas, gas, toAddr)
}
ret, returnGas, err := evm.Call(scope.Contract.Address(), toAddr, args, NewGasBudget(gas, childStateGas), &value)
if os.Getenv("DEBUG_8037") != "" {
fmt.Fprintf(os.Stderr, " opCall depth=%d returnGas=<%d,%d>su=%d err=%v\n",
evm.depth, returnGas.RegularGas, returnGas.StateGas, returnGas.StateGasUsed, err)
}
if err != nil {
temp.Clear()