core: fix 2D gas pool accounting for EIP-8037

ReturnGasAmsterdam was draining the gas pool by the 1D sum of
regular+state gas consumed per tx (tx_gas_used), but EIP-8037 defines
block validity as max(sum_regular, sum_state) <= gas_limit. This caused
valid blocks to be rejected when the sum of both dimensions exceeded
the gas limit, even though each dimension individually was within bounds.

Fix: set remaining = initial - max(cumulativeRegular, cumulativeState)
after each tx, so SubGas correctly gates subsequent transactions against
the 2D block capacity.

Co-authored-by: Jared Wasinger <j-wasinger@hotmail.com>
This commit is contained in:
qu0b 2026-03-27 19:10:36 +00:00 committed by Jared Wasinger
parent 710ffb03ad
commit 7098564c83
2 changed files with 12 additions and 10 deletions

View file

@ -73,18 +73,20 @@ func (gp *GasPool) ReturnGas(returned uint64, gasUsed uint64) error {
return nil
}
// ReturnGasAmsterdam handles 2D gas accounting for Amsterdam (EIP-8037).
// It undoes the SubGas deduction fully and accumulates per-dimension block totals.
func (gp *GasPool) ReturnGasAmsterdam(returned, txRegular, txState, receiptGasUsed uint64) error {
if gp.remaining > math.MaxUint64-returned {
return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned)
}
// Undo SubGas deduction fully (Amsterdam uses cumulative tracking)
gp.remaining += returned
// Accumulate 2D block dimensions
// ReturnGasAmsterdam calculates the new remaining gas in the pool after the
// execution of a message. The remaining gas in the pool is
// block.gasLimit - max(cumulative_regular, cumulative_state)
func (gp *GasPool) ReturnGasAmsterdam(txRegular, txState, receiptGasUsed uint64) error {
gp.cumulativeRegular += txRegular
gp.cumulativeState += txState
gp.cumulativeUsed += receiptGasUsed
blockUsed := max(gp.cumulativeRegular, gp.cumulativeState)
if gp.initial < blockUsed {
return fmt.Errorf("%w: block gas overflow: initial %d, used %d (regular: %d, state: %d)",
ErrGasLimitReached, gp.initial, blockUsed, gp.cumulativeRegular, gp.cumulativeState)
}
gp.remaining = gp.initial - blockUsed
return nil
}

View file

@ -608,7 +608,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
txState := (gas.StateGas - authRefund) + execGasUsed.StateGasCharged
txRegular := gas.RegularGas + execGasUsed.RegularGasUsed
txRegular = max(txRegular, floorDataGas)
if err := st.gp.ReturnGasAmsterdam(returned, txRegular, txState, st.gasUsed()); err != nil {
if err := st.gp.ReturnGasAmsterdam(txRegular, txState, st.gasUsed()); err != nil {
return nil, err
}
} else {