From 6544e222622b14fd250f235675ae15e7a94fd551 Mon Sep 17 00:00:00 2001 From: Jiho Park <39119451+ohijiho@users.noreply.github.com> Date: Mon, 2 Oct 2023 22:22:18 +0900 Subject: [PATCH] txpool slotGauge uint64 underflow (#1907) --- txpool/slot_gauge.go | 19 +++++++++++++++++++ txpool/txpool.go | 30 +++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/txpool/slot_gauge.go b/txpool/slot_gauge.go index d726c8ed64..7dae77d400 100644 --- a/txpool/slot_gauge.go +++ b/txpool/slot_gauge.go @@ -29,6 +29,25 @@ func (g *slotGauge) increase(slots uint64) { metrics.SetGauge([]string{txPoolMetrics, "slots_used"}, float32(newHeight)) } +// increaseWithinLimit increases the height of the gauge by the specified slots amount only if the increased height is +// less than max. Returns true if the height is increased. +func (g *slotGauge) increaseWithinLimit(slots uint64) (updated bool) { + for { + old := g.read() + newHeight := old + slots + + if newHeight > g.max { + return false + } + + if atomic.CompareAndSwapUint64(&g.height, old, newHeight) { + metrics.SetGauge([]string{txPoolMetrics, "slots_used"}, float32(newHeight)) + + return true + } + } +} + // decrease decreases the height of the gauge by the specified slots amount. func (g *slotGauge) decrease(slots uint64) { newHeight := atomic.AddUint64(&g.height, ^(slots - 1)) diff --git a/txpool/txpool.go b/txpool/txpool.go index 232b280084..27e8e7bd8b 100644 --- a/txpool/txpool.go +++ b/txpool/txpool.go @@ -787,8 +787,6 @@ func (p *TxPool) addTx(origin txOrigin, tx *types.Transaction) error { // initialize account for this address once or retrieve existing one account := p.getOrCreateAccount(tx.From) - // populate currently free slots - slotsFree := p.gauge.freeSlots() account.promoted.lock(true) account.enqueued.lock(true) @@ -827,8 +825,6 @@ func (p *TxPool) addTx(origin txOrigin, tx *types.Transaction) error { return ErrReplacementUnderpriced } - - slotsFree += slotsRequired(oldTxWithSameNonce) // add old tx slots } else { if account.enqueued.length() == account.maxEnqueued && tx.Nonce != accountNonce { return ErrMaxEnqueuedLimitReached @@ -842,27 +838,43 @@ func (p *TxPool) addTx(origin txOrigin, tx *types.Transaction) error { } } - // check for overflow - if slotsRequired(tx) > slotsFree { - return ErrTxPoolOverflow + slotsAllocated := slotsRequired(tx) + + var slotsFreed uint64 + if oldTxWithSameNonce != nil { + slotsFreed = slotsRequired(oldTxWithSameNonce) + } + + var slotsIncreased uint64 + if slotsAllocated > slotsFreed { + slotsIncreased = slotsAllocated - slotsFreed + if !p.gauge.increaseWithinLimit(slotsIncreased) { + return ErrTxPoolOverflow + } } // add to index if ok := p.index.add(tx); !ok { metrics.IncrCounter([]string{txPoolMetrics, "already_known_tx"}, 1) + if slotsIncreased > 0 { + p.gauge.decrease(slotsIncreased) + } + return ErrAlreadyKnown } + if slotsFreed > slotsAllocated { + p.gauge.decrease(slotsFreed - slotsAllocated) + } + if oldTxWithSameNonce != nil { p.index.remove(oldTxWithSameNonce) - p.gauge.decrease(slotsRequired(oldTxWithSameNonce)) } else { metrics.SetGauge([]string{txPoolMetrics, "added_tx"}, 1) } account.enqueue(tx, oldTxWithSameNonce != nil) // add or replace tx into account - p.gauge.increase(slotsRequired(tx)) go p.invokePromotion(tx, tx.Nonce <= accountNonce) // don't signal promotion for higher nonce txs