mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +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"
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"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/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
|
// 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
|
// If the new transaction is accepted into the list, the lists' cost and gas
|
||||||
// thresholds are also potentially updated.
|
// 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
|
// If there's an older better transaction, abort
|
||||||
old := l.txs.Get(tx.Nonce())
|
old := l.txs.Get(tx.Nonce())
|
||||||
if old != nil {
|
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
|
// 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)
|
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
|
l.costcap = cost
|
||||||
}
|
}
|
||||||
if gas := tx.Gas(); l.gascap < gas {
|
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
|
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
|
// Cap places a hard limit on the number of items, returning all transactions
|
||||||
// exceeding that limit.
|
// exceeding that limit.
|
||||||
func (l *txList) Cap(threshold int) types.Transactions {
|
func (l *txList) Cap(threshold int) types.Transactions {
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ func TestStrictTxListAdd(t *testing.T) {
|
||||||
// Insert the transactions in a random order
|
// Insert the transactions in a random order
|
||||||
list := newTxList(true)
|
list := newTxList(true)
|
||||||
for _, v := range rand.Perm(len(txs)) {
|
for _, v := range rand.Perm(len(txs)) {
|
||||||
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
|
list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump)
|
||||||
}
|
}
|
||||||
// Verify internal state
|
// Verify internal state
|
||||||
if len(list.txs.items) != len(txs) {
|
if len(list.txs.items) != len(txs) {
|
||||||
|
|
@ -65,7 +65,7 @@ func BenchmarkTxListAdd(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
list := newTxList(true)
|
list := newTxList(true)
|
||||||
for _, v := range rand.Perm(len(txs)) {
|
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)
|
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
|
from, _ := types.Sender(pool.signer, tx) // already validated
|
||||||
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
|
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
|
||||||
// Nonce already pending, check if required price bump is met
|
// 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 {
|
if !inserted {
|
||||||
pendingDiscardMeter.Mark(1)
|
pendingDiscardMeter.Mark(1)
|
||||||
return false, ErrReplaceUnderpriced
|
return false, ErrReplaceUnderpriced
|
||||||
|
|
@ -804,7 +804,8 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local boo
|
||||||
if pool.queue[from] == nil {
|
if pool.queue[from] == nil {
|
||||||
pool.queue[from] = newTxList(false)
|
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 {
|
if !inserted {
|
||||||
// An older transaction was better, discard this
|
// An older transaction was better, discard this
|
||||||
queuedDiscardMeter.Mark(1)
|
queuedDiscardMeter.Mark(1)
|
||||||
|
|
@ -858,7 +859,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
|
||||||
}
|
}
|
||||||
list := pool.pending[addr]
|
list := pool.pending[addr]
|
||||||
|
|
||||||
inserted, old := list.Add(tx, pool.config.PriceBump)
|
inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump)
|
||||||
if !inserted {
|
if !inserted {
|
||||||
// An older transaction was better, discard this
|
// An older transaction was better, discard this
|
||||||
pool.all.Remove(hash)
|
pool.all.Remove(hash)
|
||||||
|
|
@ -1382,8 +1383,10 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
|
||||||
pool.all.Remove(hash)
|
pool.all.Remove(hash)
|
||||||
}
|
}
|
||||||
log.Trace("Removed old queued transactions", "count", len(forwards))
|
log.Trace("Removed old queued transactions", "count", len(forwards))
|
||||||
|
|
||||||
// Drop all transactions that are too costly (low balance or out of gas)
|
// 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 {
|
for _, tx := range drops {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
pool.all.Remove(hash)
|
pool.all.Remove(hash)
|
||||||
|
|
@ -1428,6 +1431,26 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
|
||||||
return promoted
|
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
|
// 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
|
// pending limit. The algorithm tries to reduce transaction counts by an approximately
|
||||||
// equal number for all for accounts with many pending transactions.
|
// 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)
|
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
|
// 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 {
|
for _, tx := range drops {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
log.Trace("Removed unpayable pending transaction", "hash", hash)
|
log.Trace("Removed unpayable pending transaction", "hash", hash)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 5 // Major version component of the current release
|
VersionMajor = 5 // Major version component of the current release
|
||||||
VersionMinor = 1 // Minor 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
|
VersionMeta = "mainnet" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue