From 58ed1a2a3bb88fd9dd991afdd3f6cb7ef76834bf Mon Sep 17 00:00:00 2001 From: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:54:13 +0800 Subject: [PATCH] fix: Check L1DataFee in txpool promoteExecutables and demoteUnexecutables (#948) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: Check L1DataFee in txpool promoteExecutables and demoteUnexecutables (#627) * Check L1DataFee in txpool promoteExecutables * bump version * implement L1 data fee in demoteUnexecutables as well * Update core/tx_list.go * Update core/tx_pool.go * feat: consider l1 data fee in txpool costcap (#681) * feat(txpool): consider l1 data fee in costcap * fix CI * simplify logic * remove one db read op * bump version --------- Co-authored-by: vyzo Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> Co-authored-by: Péter Garamvölgyi Co-authored-by: georgehao --- core/txpool/legacypool/legacypool.go | 27 ++++++++++++++++++++++-- core/txpool/legacypool/list.go | 31 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index ab84addef5..8624a59ed7 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -1500,8 +1500,10 @@ func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.T pool.all.Remove(hash) } log.Trace("Removed old queued transactions", "count", len(forwards)) + // Drop all transactions that are too costly (low balance or out of gas) - drops, _ := list.Filter(pool.currentState.GetBalance(addr), gasLimit) + costLimit := pool.currentState.GetBalance(addr) + drops, _ := list.FilterF(costLimit, gasLimit, pool.executableTxFilter(costLimit, gasLimit)) for _, tx := range drops { hash := tx.Hash() pool.all.Remove(hash) @@ -1549,6 +1551,26 @@ func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.T return promoted } +func (pool *LegacyPool) executableTxFilter(costLimit *big.Int, gasLimit uint64) func(tx *types.Transaction) bool { + return func(tx *types.Transaction) bool { + if tx.Gas() > gasLimit || tx.Cost().Cmp(costLimit) > 0 { + return true + } + + if pool.chainconfig.Scroll.FeeVaultEnabled() { + // recheck L1 data fee, as the oracle price may have changed + l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead.Load().Number) + if err != nil { + log.Error("Failed to calculate L1 data fee", "err", err, "tx", tx) + return false + } + return costLimit.Cmp(new(big.Int).Add(tx.Cost(), l1DataFee)) < 0 + } + + return false + } +} + // truncatePending removes transactions from the pending queue if the pool is above the // pending limit. The algorithm tries to reduce transaction counts by an approximately // equal number for all for accounts with many pending transactions. @@ -1702,7 +1724,8 @@ func (pool *LegacyPool) demoteUnexecutables() { log.Trace("Removed old pending transaction", "hash", hash) } // Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later - drops, invalids := list.Filter(pool.currentState.GetBalance(addr), gasLimit) + costLimit := pool.currentState.GetBalance(addr) + drops, invalids := list.FilterF(costLimit, gasLimit, pool.executableTxFilter(costLimit, gasLimit)) for _, tx := range drops { hash := tx.Hash() log.Trace("Removed unpayable pending transaction", "hash", hash) diff --git a/core/txpool/legacypool/list.go b/core/txpool/legacypool/list.go index ba760bc749..89dd4d5d09 100644 --- a/core/txpool/legacypool/list.go +++ b/core/txpool/legacypool/list.go @@ -404,6 +404,37 @@ func (l *list) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, return removed, invalids } +// FilterF removes all transactions from the list that satisfy a predicate. +// Every removed transaction is returned for any post-removal maintenance. +// Strict-mode invalidated transactions are also returned. +func (l *list) FilterF(costLimit *big.Int, gasLimit uint64, f func(tx *types.Transaction) bool) (types.Transactions, types.Transactions) { + // If all transactions are below the threshold, short circuit + if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit { + return nil, nil + } + l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds + l.gascap = gasLimit + + removed := l.txs.Filter(f) + + if len(removed) == 0 { + return nil, nil + } + var invalids types.Transactions + // If the list was strict, filter anything above the lowest nonce + if l.strict { + lowest := uint64(math.MaxUint64) + for _, tx := range removed { + if nonce := tx.Nonce(); lowest > nonce { + lowest = nonce + } + } + invalids = l.txs.filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest }) + } + l.txs.reheap() + return removed, invalids +} + // Cap places a hard limit on the number of items, returning all transactions // exceeding that limit. func (l *list) Cap(threshold int) types.Transactions {