Skip to content

Commit

Permalink
fix: Check L1DataFee in txpool promoteExecutables and demoteUnexecuta…
Browse files Browse the repository at this point in the history
…bles (#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 <[email protected]>

* Update core/tx_pool.go

Co-authored-by: Péter Garamvölgyi <[email protected]>

* 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 <[email protected]>
Co-authored-by: Péter Garamvölgyi <[email protected]>
Co-authored-by: georgehao <[email protected]>
  • Loading branch information
4 people authored Mar 27, 2024
1 parent 61e8de9 commit d1e4b59
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 10 deletions.
47 changes: 45 additions & 2 deletions core/tx_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions core/tx_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
}
}
Expand Down
34 changes: 29 additions & 5 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion params/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down

0 comments on commit d1e4b59

Please sign in to comment.