mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-08 07:58:40 +00:00
core/txpool/legacypool: skip MaxTxGas purge when Amsterdam is active
PR #34841 lifts the EIP-7825 per-transaction gas cap once Amsterdam (EIP-8037) is active, since tx.Gas() then includes the state-gas reservoir in addition to the regular gas dimension. The PR added an !IsAmsterdam guard in five call sites (txpool.ValidateTransaction, state_transition.preCheck, miner.fillTransactions, eth/gasestimator, cmd/evm t8ntool) but missed one: legacypool.runReorg still purges all transactions with tx.Gas() > MaxTxGas at the Osaka fork boundary, regardless of whether Amsterdam is also active on the new head. On a chain that activates Osaka and Amsterdam at the same timestamp (the expected EIP-8037 devnet/testnet layout before mainnet cuts the two forks), the first reorg crossing that boundary deletes otherwise valid Amsterdam transactions whose total gas legitimately exceeds MaxTxGas. Add the missing IsAmsterdam guard so the purge matches the stated semantics of #34841. Add a regression test that constructs a chain config with OsakaTime == AmsterdamTime, injects a tx with gas = MaxTxGas+100k in the pre-fork state, and verifies the tx survives the reorg into the post-fork head. The test fails on master and passes with this change.
This commit is contained in:
parent
01036bed83
commit
0ab02b4383
2 changed files with 80 additions and 2 deletions
|
|
@ -1223,9 +1223,11 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
|
|||
if reset != nil {
|
||||
if reset.newHead != nil && reset.oldHead != nil {
|
||||
// Discard the transactions with the gas limit higher than the cap at the
|
||||
// Osaka fork boundary.
|
||||
// Osaka fork boundary. Amsterdam/EIP-8037 lifts the per-transaction gas
|
||||
// cap, so skip this purge when Amsterdam is also active on the new head.
|
||||
if pool.chainconfig.IsOsaka(reset.newHead.Number, reset.newHead.Time) &&
|
||||
!pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) {
|
||||
!pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) &&
|
||||
!pool.chainconfig.IsAmsterdam(reset.newHead.Number, reset.newHead.Time) {
|
||||
var hashes []common.Hash
|
||||
pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
|
||||
if tx.Gas() > params.MaxTxGas {
|
||||
|
|
|
|||
|
|
@ -2627,6 +2627,82 @@ func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 1
|
|||
func BenchmarkPendingDemotion1000(b *testing.B) { benchmarkPendingDemotion(b, 1000) }
|
||||
func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 10000) }
|
||||
|
||||
// TestOsakaAmsterdamGasCapPurge verifies that the legacy pool does NOT purge
|
||||
// transactions with gas > MaxTxGas at the Osaka fork boundary when Amsterdam is
|
||||
// also active. Under EIP-8037 (Amsterdam), transactions can include state-gas
|
||||
// in addition to regular gas, so tx.Gas() is allowed to exceed MaxTxGas.
|
||||
//
|
||||
// The fix for this (#34841) added the IsAmsterdam guard in 4 of the 5 sites
|
||||
// (ValidateTransaction, preCheck, miner.fillTransactions, gasestimator, t8ntool),
|
||||
// but the legacypool.runReorg site was missed: it still purges on
|
||||
// IsOsaka(newHead) && !IsOsaka(oldHead) without checking IsAmsterdam.
|
||||
//
|
||||
// Failing without fix: the pre-injected high-gas transaction is removed after
|
||||
// the reorg into Osaka/Amsterdam, even though Amsterdam makes it legal.
|
||||
func TestOsakaAmsterdamGasCapPurge(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Fork config: Osaka and Amsterdam activate at the same timestamp (1000).
|
||||
// Before 1000 both are inactive; at/after 1000 both are active.
|
||||
forkTime := uint64(1000)
|
||||
config := *params.MergedTestChainConfig
|
||||
config.OsakaTime = &forkTime
|
||||
config.AmsterdamTime = &forkTime
|
||||
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||
// gasLimit must accommodate the high-gas tx.
|
||||
blockchain := newTestBlockChain(&config, 50_000_000, statedb, new(event.Feed))
|
||||
|
||||
key, _ := crypto.GenerateKey()
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
// Enough balance for gas*gasPrice + value with a 20M-gas tx.
|
||||
statedb.AddBalance(addr, uint256.NewInt(1e18), tracing.BalanceChangeUnspecified)
|
||||
|
||||
pool := New(testTxPoolConfig, blockchain)
|
||||
// Init pool at a pre-fork head (Time=0). At this time IsOsaka=false so
|
||||
// tx.Gas() > MaxTxGas is NOT rejected by stateless validation.
|
||||
preForkHead := &types.Header{
|
||||
Number: big.NewInt(0),
|
||||
Difficulty: common.Big0,
|
||||
GasLimit: 50_000_000,
|
||||
BaseFee: big.NewInt(1),
|
||||
Time: 0,
|
||||
}
|
||||
if err := pool.Init(testTxPoolConfig.PriceLimit, preForkHead, newReserver()); err != nil {
|
||||
t.Fatalf("pool init: %v", err)
|
||||
}
|
||||
defer pool.Close()
|
||||
<-pool.initDoneCh
|
||||
|
||||
// Inject a transaction with gas above MaxTxGas. Valid under pre-Osaka rules.
|
||||
highGasTx := pricedTransaction(0, params.MaxTxGas+100_000, big.NewInt(1), key)
|
||||
if err := pool.addRemoteSync(highGasTx); err != nil {
|
||||
t.Fatalf("failed to add high-gas tx: %v", err)
|
||||
}
|
||||
if pool.Status(highGasTx.Hash()) == txpool.TxStatusUnknown {
|
||||
t.Fatalf("tx missing after add (pre-reorg)")
|
||||
}
|
||||
|
||||
// Reorg: oldHead is still pre-fork (Time=0), newHead crosses into Osaka AND
|
||||
// Amsterdam (Time=forkTime). Under the current buggy code this triggers the
|
||||
// purge branch because IsOsaka(newHead) && !IsOsaka(oldHead), ignoring that
|
||||
// Amsterdam is also active.
|
||||
newHead := &types.Header{
|
||||
Number: big.NewInt(1),
|
||||
Difficulty: common.Big0,
|
||||
GasLimit: 50_000_000,
|
||||
BaseFee: big.NewInt(1),
|
||||
Time: forkTime,
|
||||
}
|
||||
<-pool.requestReset(preForkHead, newHead)
|
||||
|
||||
// With the fix: tx is kept because Amsterdam is active and tx.Gas() >
|
||||
// MaxTxGas is legal. Without the fix: tx is purged.
|
||||
if pool.Status(highGasTx.Hash()) == txpool.TxStatusUnknown {
|
||||
t.Fatalf("high-gas tx was purged despite Amsterdam being active at newHead; runReorg is missing the !IsAmsterdam guard at legacypool.go:1227")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkPendingDemotion(b *testing.B, size int) {
|
||||
// Add a batch of transactions to a pool one by one
|
||||
pool, key := setupPool()
|
||||
|
|
|
|||
Loading…
Reference in a new issue