mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
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 Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> * Update core/tx_pool.go Co-authored-by: Péter Garamvölgyi <peter@scroll.io> * 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: colin <102356659+colinlyguo@users.noreply.github.com> Co-authored-by: Péter Garamvölgyi <peter@scroll.io> Co-authored-by: georgehao <haohongfan@gmail.com>
This commit is contained in:
parent
61e8de9c3f
commit
d1e4b59eec
4 changed files with 77 additions and 10 deletions
|
|
@ -26,7 +26,10 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/core/state"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"github.com/scroll-tech/go-ethereum/rollup/fees"
|
||||
)
|
||||
|
||||
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
|
||||
|
|
@ -278,7 +281,7 @@ func (l *txList) Overlaps(tx *types.Transaction) bool {
|
|||
//
|
||||
// If the new transaction is accepted into the list, the lists' cost and gas
|
||||
// thresholds are also potentially updated.
|
||||
func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
|
||||
func (l *txList) Add(tx *types.Transaction, state *state.StateDB, priceBump uint64) (bool, *types.Transaction) {
|
||||
// If there's an older better transaction, abort
|
||||
old := l.txs.Get(tx.Nonce())
|
||||
if old != nil {
|
||||
|
|
@ -303,8 +306,17 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
|
|||
}
|
||||
}
|
||||
// Otherwise overwrite the old transaction with the current one
|
||||
l1DataFee := big.NewInt(0)
|
||||
if state != nil {
|
||||
var err error
|
||||
l1DataFee, err = fees.CalculateL1DataFee(tx, state)
|
||||
if err != nil {
|
||||
log.Error("Failed to calculate L1 data fee", "err", err, "tx", tx)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
l.txs.Put(tx)
|
||||
if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
|
||||
if cost := new(big.Int).Add(tx.Cost(), l1DataFee); l.costcap.Cmp(cost) < 0 {
|
||||
l.costcap = cost
|
||||
}
|
||||
if gas := tx.Gas(); l.gascap < gas {
|
||||
|
|
@ -360,6 +372,37 @@ func (l *txList) 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 *txList) 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 *txList) Cap(threshold int) types.Transactions {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ func TestStrictTxListAdd(t *testing.T) {
|
|||
// Insert the transactions in a random order
|
||||
list := newTxList(true)
|
||||
for _, v := range rand.Perm(len(txs)) {
|
||||
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
|
||||
list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump)
|
||||
}
|
||||
// Verify internal state
|
||||
if len(list.txs.items) != len(txs) {
|
||||
|
|
@ -65,7 +65,7 @@ func BenchmarkTxListAdd(b *testing.B) {
|
|||
for i := 0; i < b.N; i++ {
|
||||
list := newTxList(true)
|
||||
for _, v := range rand.Perm(len(txs)) {
|
||||
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
|
||||
list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump)
|
||||
list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -754,7 +754,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
|
|||
from, _ := types.Sender(pool.signer, tx) // already validated
|
||||
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
|
||||
// Nonce already pending, check if required price bump is met
|
||||
inserted, old := list.Add(tx, pool.config.PriceBump)
|
||||
inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump)
|
||||
if !inserted {
|
||||
pendingDiscardMeter.Mark(1)
|
||||
return false, ErrReplaceUnderpriced
|
||||
|
|
@ -804,7 +804,8 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local boo
|
|||
if pool.queue[from] == nil {
|
||||
pool.queue[from] = newTxList(false)
|
||||
}
|
||||
inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump)
|
||||
|
||||
inserted, old := pool.queue[from].Add(tx, pool.currentState, pool.config.PriceBump)
|
||||
if !inserted {
|
||||
// An older transaction was better, discard this
|
||||
queuedDiscardMeter.Mark(1)
|
||||
|
|
@ -858,7 +859,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
|
|||
}
|
||||
list := pool.pending[addr]
|
||||
|
||||
inserted, old := list.Add(tx, pool.config.PriceBump)
|
||||
inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump)
|
||||
if !inserted {
|
||||
// An older transaction was better, discard this
|
||||
pool.all.Remove(hash)
|
||||
|
|
@ -1382,8 +1383,10 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
|
|||
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), pool.currentMaxGas)
|
||||
costLimit := pool.currentState.GetBalance(addr)
|
||||
drops, _ := list.FilterF(costLimit, pool.currentMaxGas, pool.executableTxFilter(costLimit))
|
||||
for _, tx := range drops {
|
||||
hash := tx.Hash()
|
||||
pool.all.Remove(hash)
|
||||
|
|
@ -1428,6 +1431,26 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
|
|||
return promoted
|
||||
}
|
||||
|
||||
func (pool *TxPool) executableTxFilter(costLimit *big.Int) func(tx *types.Transaction) bool {
|
||||
return func(tx *types.Transaction) bool {
|
||||
if tx.Gas() > pool.currentMaxGas || 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)
|
||||
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.
|
||||
|
|
@ -1582,7 +1605,8 @@ func (pool *TxPool) 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), pool.currentMaxGas)
|
||||
costLimit := pool.currentState.GetBalance(addr)
|
||||
drops, invalids := list.FilterF(costLimit, pool.currentMaxGas, pool.executableTxFilter(costLimit))
|
||||
for _, tx := range drops {
|
||||
hash := tx.Hash()
|
||||
log.Trace("Removed unpayable pending transaction", "hash", hash)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
const (
|
||||
VersionMajor = 5 // Major version component of the current release
|
||||
VersionMinor = 1 // Minor version component of the current release
|
||||
VersionPatch = 27 // Patch version component of the current release
|
||||
VersionPatch = 28 // Patch version component of the current release
|
||||
VersionMeta = "mainnet" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue