From 964a117ea71b496b7774a33e61695ff8077dfebc Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 5 Mar 2026 17:46:14 +0100 Subject: [PATCH] core: fixed issues from devnet --- core/state_transition.go | 5 ++++- core/vm/evm.go | 31 +++++++++++++++---------------- core/vm/gascosts.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 41fb7a4be8..0092eaef51 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -781,7 +781,10 @@ func (st *stateTransition) blockGasUsed(intrinsicGas, execGasStart vm.GasCosts) totalExecUsed := (execGasStart.RegularGas + execGasStart.StateGas) - (st.gasRemaining.RegularGas + st.gasRemaining.StateGas) execStateUsed := st.gasRemaining.TotalStateGasCharged - execRegularUsed := totalExecUsed - execStateUsed + // Exclude state gas that was charged from regular gas but then reverted. + // This gas was consumed from the regular pool but was for state operations + // that didn't persist, so it shouldn't count in the regular dimension. + execRegularUsed := totalExecUsed - execStateUsed - st.gasRemaining.RevertedStateGasSpill txRegular := intrinsicGas.RegularGas + execRegularUsed txState := intrinsicGas.StateGas + execStateUsed - st.stateGasRefund diff --git a/core/vm/evm.go b/core/vm/evm.go index b52415a46f..77f17dd0d4 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -316,7 +316,8 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g // when we're in homestead this also counts for code storage gas errors. if err != nil { evm.StateDB.RevertToSnapshot(snapshot) - if err != ErrExecutionReverted { + isRevert := err == ErrExecutionReverted + if !isRevert { if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution) } @@ -325,8 +326,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g // State changes are rolled back on any error, so state gas charges // from the reverted execution should not count toward block accounting. // Also restore the state gas reservoir since state creation was undone. - gas.TotalStateGasCharged = savedTotalStateGas - gas.StateGas = savedStateGas + gas.RevertStateGas(savedTotalStateGas, savedStateGas, isRevert) // TODO: consider clearing up unused snapshots: //} else { // evm.StateDB.DiscardSnapshot(snapshot) @@ -381,14 +381,14 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) - if err != ErrExecutionReverted { + isRevert := err == ErrExecutionReverted + if !isRevert { if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution) } gas.RegularGas = 0 } - gas.TotalStateGasCharged = savedTotalStateGas - gas.StateGas = savedStateGas + gas.RevertStateGas(savedTotalStateGas, savedStateGas, isRevert) } return ret, gas, err } @@ -433,14 +433,14 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) - if err != ErrExecutionReverted { + isRevert := err == ErrExecutionReverted + if !isRevert { if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution) } gas.RegularGas = 0 } - gas.TotalStateGasCharged = savedTotalStateGas - gas.StateGas = savedStateGas + gas.RevertStateGas(savedTotalStateGas, savedStateGas, isRevert) } return ret, gas, err } @@ -496,15 +496,14 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) - if err != ErrExecutionReverted { + isRevert := err == ErrExecutionReverted + if !isRevert { if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution) } - gas.RegularGas = 0 } - gas.TotalStateGasCharged = savedTotalStateGas - gas.StateGas = savedStateGas + gas.RevertStateGas(savedTotalStateGas, savedStateGas, isRevert) } return ret, gas, err } @@ -615,14 +614,14 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasCosts, value * ret, err = evm.initNewContract(contract, address) if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) - if err != ErrExecutionReverted { + isRevert := err == ErrExecutionReverted + if !isRevert { contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution) } // State changes are rolled back, so state gas charges from the // reverted execution should not count toward block accounting. // Also restore the state gas reservoir since state creation was undone. - contract.Gas.TotalStateGasCharged = savedTotalStateGas - contract.Gas.StateGas = savedStateGas + contract.Gas.RevertStateGas(savedTotalStateGas, savedStateGas, isRevert) } return ret, address, contract.Gas, err } diff --git a/core/vm/gascosts.go b/core/vm/gascosts.go index 2f8953bdca..d996735df6 100644 --- a/core/vm/gascosts.go +++ b/core/vm/gascosts.go @@ -11,6 +11,13 @@ type GasCosts struct { // This is needed for EIP-8037 block gas accounting where the state gas // dimension counts ALL state creation charges, not just reservoir consumption. TotalStateGasCharged uint64 + + // RevertedStateGasSpill tracks state gas that was charged from regular gas + // (spilled) during execution of a call that subsequently reverted. When a + // call fails, its state changes are undone, but the regular gas was already + // consumed. Block gas accounting must exclude this amount from the regular + // gas dimension since it was for state operations that didn't persist. + RevertedStateGasSpill uint64 } func (g GasCosts) Max() uint64 { @@ -52,6 +59,28 @@ func (g *GasCosts) Add(b GasCosts) { g.RegularGas += b.RegularGas g.StateGas += b.StateGas g.TotalStateGasCharged += b.TotalStateGasCharged + g.RevertedStateGasSpill += b.RevertedStateGasSpill +} + +// RevertStateGas handles state gas accounting when a call reverts (EIP-8037). +// It computes how much state gas was charged from regular gas (spill) during the +// call, and either returns it for REVERT errors or tracks it for non-REVERT errors. +func (g *GasCosts) RevertStateGas(savedTotalStateGas, savedStateGas uint64, isRevert bool) { + chargedDuringCall := g.TotalStateGasCharged - savedTotalStateGas + fromReservoir := savedStateGas - g.StateGas + spilledFromRegular := chargedDuringCall - fromReservoir + + if isRevert { + // REVERT: return the spilled state gas to regular gas since the caller + // keeps unused gas and state operations were undone. + g.RegularGas += spilledFromRegular + } else { + // Non-REVERT: regular gas is zeroed, but block accounting must exclude + // the spill from the regular gas dimension. + g.RevertedStateGasSpill += spilledFromRegular + } + g.TotalStateGasCharged = savedTotalStateGas + g.StateGas = savedStateGas } func (g GasCosts) String() string {