From 985cdac8a7d66db012fd218602306e90f729f059 Mon Sep 17 00:00:00 2001 From: jwasinger Date: Sun, 5 Apr 2026 18:12:48 -0400 Subject: [PATCH] core: fix 8037 gas accounting (#34631) To check whether a transaction can be applied, we validate that `blockGasLimit > txGasLimit + (cumulativeRegularGasUsed + cumulativeStateGasUsed)`. However, the check should only be applied to the bottleneck resource, i.e. `blockGasLimit > max(txRegularGasUsed+cumulativeRegularGasUsed, txStateGasUsed+ cumulativeStateGasUsed)`. The changes here break multiple tests. I am trying to determine why. --------- Co-authored-by: qu0b --- core/gaspool.go | 20 +++++++++++--------- core/parallel_state_processor.go | 7 ++++--- core/state_transition.go | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/core/gaspool.go b/core/gaspool.go index 690882c7b1..9ce156bb9b 100644 --- a/core/gaspool.go +++ b/core/gaspool.go @@ -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 } diff --git a/core/parallel_state_processor.go b/core/parallel_state_processor.go index 9b76ae27ca..fe0cb65dc1 100644 --- a/core/parallel_state_processor.go +++ b/core/parallel_state_processor.go @@ -201,7 +201,7 @@ func (p *ParallelStateProcessor) resultHandler(block *types.Block, preTxReads ba // 1. if the block has transactions, receive the execution results from all of them and return an error on resCh if any txs err'd // 2. once all txs are executed, compute the post-tx state transition and produce the ProcessResult sending it on resCh (or an error if the post-tx state didn't match what is reported in the BAL) var results []txExecResult - gp := NewGasPool(block.GasLimit()) + var cumulativeStateGas, cumulativeRegularGas uint64 var execErr error var numTxComplete int @@ -217,9 +217,10 @@ func (p *ParallelStateProcessor) resultHandler(block *types.Block, preTxReads ba // short-circuit if invalid block was detected if res.err != nil { execErr = res.err - } else if err := gp.SubGas(res.receipt.CumulativeGasUsed); err != nil { - execErr = err + } else if bottleneck := max(cumulativeRegularGas+res.txRegular, cumulativeStateGas+res.txState); bottleneck > block.GasLimit() { + execErr = fmt.Errorf("block used too much gas in bottleneck dimension: %d. block gas limit is %d", bottleneck, block.GasLimit()) } else { + cumulativeStateGas += res.txState results = append(results, res) accesses.Merge(res.stateReads) } diff --git a/core/state_transition.go b/core/state_transition.go index a4f298011f..b69bff85d1 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -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 {