core: skip tx gas cap after Amsterdam (#34841)
Some checks failed
/ Linux Build (push) Has been cancelled
/ Linux Build (arm) (push) Has been cancelled
/ Keeper Build (push) Has been cancelled
/ Windows Build (push) Has been cancelled
/ Docker Image (push) Has been cancelled

EIP-7825 caps the transaction gas limit at `MaxTxGas`, but after
Amsterdam/EIP-8037 the transaction gas limit can include state gas
reservoir in addition to the regular gas dimension. Applying the Osaka
cap to the full `tx.Gas()` rejects otherwise valid Amsterdam
transactions that need more than `MaxTxGas` total gas because of state
gas, while their regular gas use remains within the intended limit.

This changes geth to stop applying the full transaction gas cap once
Amsterdam is active:

- txpool stateless validation no longer rejects `tx.Gas() > MaxTxGas`
under Amsterdam
- legacy pool reorg cleanup does not purge high-total-gas transactions
at the Osaka transition if Amsterdam is also active
- execution precheck mirrors the txpool behavior and does not reject
high-total-gas messages under Amsterdam

The block gas limit check remains in place, so transactions still cannot
request more total gas than the current block gas limit.

Validation run:

```
go test ./core/txpool ./core/txpool/legacypool
go test ./core -run TestStateProcessorErrors
```

---------

Co-authored-by: Gary Rong <garyrong0905@gmail.com>
This commit is contained in:
Giulio rebuffo 2026-04-28 17:25:16 +02:00 committed by GitHub
parent db8d6abced
commit 01036bed83
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 15 additions and 10 deletions

View file

@ -185,7 +185,9 @@ func Transaction(ctx *cli.Context) error {
}
}
if chainConfig.IsOsaka(new(big.Int), 0) && tx.Gas() > params.MaxTxGas {
isOsaka := chainConfig.IsOsaka(new(big.Int), 0)
isAmsterdam := chainConfig.IsAmsterdam(new(big.Int), 0)
if isOsaka && !isAmsterdam && tx.Gas() > params.MaxTxGas {
r.Error = errors.New("gas limit exceeds maximum")
}
results = append(results, r)

View file

@ -344,9 +344,10 @@ func (st *stateTransition) preCheck() error {
}
}
isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time)
isAmsterdam := st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time)
if !msg.SkipTransactionChecks {
// Verify tx gas limit does not exceed EIP-7825 cap.
if isOsaka && msg.GasLimit > params.MaxTxGas {
if isOsaka && !isAmsterdam && msg.GasLimit > params.MaxTxGas {
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
}
// Make sure the sender is an EOA

View file

@ -1222,8 +1222,10 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
pool.mu.Lock()
if reset != nil {
if reset.newHead != nil && reset.oldHead != nil {
// Discard the transactions with the gas limit higher than the cap.
if pool.chainconfig.IsOsaka(reset.newHead.Number, reset.newHead.Time) && !pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) {
// Discard the transactions with the gas limit higher than the cap at the
// Osaka fork boundary.
if pool.chainconfig.IsOsaka(reset.newHead.Number, reset.newHead.Time) &&
!pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) {
var hashes []common.Hash
pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
if tx.Gas() > params.MaxTxGas {

View file

@ -92,7 +92,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
return err
}
}
if rules.IsOsaka && tx.Gas() > params.MaxTxGas {
if rules.IsOsaka && !rules.IsAmsterdam && tx.Gas() > params.MaxTxGas {
return fmt.Errorf("%w (cap: %d, tx: %d)", core.ErrGasLimitTooHigh, params.MaxTxGas, tx.Gas())
}
// Transactions can't be negative. This may never happen using RLP decoded

View file

@ -63,10 +63,10 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
}
// Cap the maximum gas allowance according to EIP-7825 if the estimation targets Osaka
if hi > params.MaxTxGas {
if opts.Config.IsOsaka(opts.Header.Number, opts.Header.Time) {
hi = params.MaxTxGas
}
isOsaka := opts.Config.IsOsaka(opts.Header.Number, opts.Header.Time)
isAmsterdam := opts.Config.IsAmsterdam(opts.Header.Number, opts.Header.Time)
if hi > params.MaxTxGas && isOsaka && !isAmsterdam {
hi = params.MaxTxGas
}
// Normalize the max fee per gas the call is willing to spend.

View file

@ -563,7 +563,7 @@ func (miner *Miner) fillTransactions(ctx context.Context, interrupt *atomic.Int3
if env.header.ExcessBlobGas != nil {
filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(miner.chainConfig, env.header))
}
if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) {
if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) && !miner.chainConfig.IsAmsterdam(env.header.Number, env.header.Time) {
filter.GasLimitCap = params.MaxTxGas
}
filter.BlobTxs = false