go-ethereum/core/vm/gascosts.go
2026-03-05 19:29:57 +01:00

92 lines
3 KiB
Go

package vm
import "fmt"
type GasCosts struct {
RegularGas uint64
StateGas uint64
// TotalStateGasCharged tracks the cumulative state gas charged during
// execution, including gas that spilled from the reservoir to regular gas.
// 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 {
return max(g.RegularGas, g.StateGas)
}
func (g GasCosts) Sum() uint64 {
return g.RegularGas + g.StateGas
}
// Underflow returns true if the operation would underflow.
// When state gas exceeds the reservoir, the excess spills to regular gas.
// The check accounts for regular gas already consumed by b.RegularGas.
func (g GasCosts) Underflow(b GasCosts) bool {
if b.RegularGas > g.RegularGas {
return true
}
if b.StateGas > g.StateGas {
spillover := b.StateGas - g.StateGas
remainingRegular := g.RegularGas - b.RegularGas
if spillover > remainingRegular {
return true
}
}
return false
}
// Sub doesn't check for underflows
func (g *GasCosts) Sub(b GasCosts) {
g.RegularGas -= b.RegularGas
g.TotalStateGasCharged += b.StateGas
if b.StateGas > g.StateGas {
diff := b.StateGas - g.StateGas
g.StateGas = 0
g.RegularGas -= diff
} else {
g.StateGas -= b.StateGas
}
}
// Add doesn't check for overflows
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 {
return fmt.Sprintf("<%v,%v>", g.RegularGas, g.StateGas)
}