Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 89 additions & 9 deletions core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,19 @@ var (

var (
// Metrics for the pending pool
pendingDiscardMeter = metrics.NewRegisteredMeter("txpool/pending/discard", nil)
pendingReplaceMeter = metrics.NewRegisteredMeter("txpool/pending/replace", nil)
pendingRateLimitMeter = metrics.NewRegisteredMeter("txpool/pending/ratelimit", nil) // Dropped due to rate limiting
pendingNofundsMeter = metrics.NewRegisteredMeter("txpool/pending/nofunds", nil) // Dropped due to out-of-funds
pendingDiscardMeter = metrics.NewRegisteredMeter("txpool/pending/discard", nil)
pendingReplaceMeter = metrics.NewRegisteredMeter("txpool/pending/replace", nil)
pendingRateLimitMeter = metrics.NewRegisteredMeter("txpool/pending/ratelimit", nil) // Dropped due to rate limiting
pendingNofundsMeter = metrics.NewRegisteredMeter("txpool/pending/nofunds", nil) // Dropped due to out-of-funds
pendingBelowFloorMeter = metrics.NewRegisteredMeter("txpool/pending/belowfloor", nil) // Dropped due to a raised gas price floor

// Metrics for the queued pool
queuedDiscardMeter = metrics.NewRegisteredMeter("txpool/queued/discard", nil)
queuedReplaceMeter = metrics.NewRegisteredMeter("txpool/queued/replace", nil)
queuedRateLimitMeter = metrics.NewRegisteredMeter("txpool/queued/ratelimit", nil) // Dropped due to rate limiting
queuedNofundsMeter = metrics.NewRegisteredMeter("txpool/queued/nofunds", nil) // Dropped due to out-of-funds
queuedEvictionMeter = metrics.NewRegisteredMeter("txpool/queued/eviction", nil) // Dropped due to lifetime
queuedDiscardMeter = metrics.NewRegisteredMeter("txpool/queued/discard", nil)
queuedReplaceMeter = metrics.NewRegisteredMeter("txpool/queued/replace", nil)
queuedRateLimitMeter = metrics.NewRegisteredMeter("txpool/queued/ratelimit", nil) // Dropped due to rate limiting
queuedNofundsMeter = metrics.NewRegisteredMeter("txpool/queued/nofunds", nil) // Dropped due to out-of-funds
queuedEvictionMeter = metrics.NewRegisteredMeter("txpool/queued/eviction", nil) // Dropped due to lifetime
queuedBelowFloorMeter = metrics.NewRegisteredMeter("txpool/queued/belowfloor", nil) // Dropped due to a raised gas price floor

// General tx metrics
knownTxMeter = metrics.NewRegisteredMeter("txpool/known", nil)
Expand Down Expand Up @@ -1343,6 +1345,16 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
}
}

// Discard transactions a gas schedule fork priced out of the pool. Sweeping
// must precede promoteExecutables, which does not check price and would
// otherwise pull them back into pending.
if floor, previous := pool.raisedGasPriceFloor(oldPoolHead, newPoolHead); floor != nil {
if fromPending, fromQueue := pool.sweepUnderpriced(floor); fromPending+fromQueue > 0 {
pendingBelowFloorMeter.Mark(int64(fromPending))
queuedBelowFloorMeter.Mark(int64(fromQueue))
log.Warn("Dropped transactions below raised gas price floor", "pending", fromPending, "queued", fromQueue, "floor", floor, "previous", previous)
}
}
// Nonces were reset, discard any events that became stale
for addr := range events {
events[addr].Forward(pool.pendingNonces.get(addr))
Expand Down Expand Up @@ -1705,6 +1717,74 @@ func (pool *LegacyPool) demoteUnexecutables() {
}
}

// raisedGasPriceFloor returns the minimum gas price of the block pending on top
// of newHead when moving there raises it, plus the floor it replaces; nil
// otherwise. The nil-number guard is defensive.
func (pool *LegacyPool) raisedGasPriceFloor(oldHead, newHead *types.Header) (raised, previous *big.Int) {
if oldHead == nil || newHead == nil || oldHead.Number == nil || newHead.Number == nil {
return nil, nil
}
next := params.GetMinGasPrice(new(big.Int).Add(newHead.Number, common.Big1), pool.chainconfig)
prev := params.GetMinGasPrice(new(big.Int).Add(oldHead.Number, common.Big1), pool.chainconfig)
if next.Cmp(prev) <= 0 {
return nil, nil
}
return next, prev
}

// sweepUnderpriced drops every pooled transaction priced below the pool floor,
// returning how many came out of the pending list and the queue.
//
// A gas schedule fork raises the floor above transactions admitted under the
// previous tier; they can no longer be mined, yet keeping them pending would
// leave pendingNonces pointing past them. The queue is swept too because
// promoteExecutables does not check price and would promote them straight back
// into pending. Special transactions are exempt exactly as during admission.
func (pool *LegacyPool) sweepUnderpriced(minGasPrice *big.Int) (pending int, queued int) {
// Collect before removing: removeTx mutates both the pending map and the
// queue, moving demoted transactions from the former to the latter, which
// would also misattribute where each transaction sat before the sweep.
var pendingHashes, queuedHashes []common.Hash
pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
if tx.IsSpecialTransaction() || tx.GasPrice().Cmp(minGasPrice) >= 0 {
return true
}
// Match by hash, not merely by nonce: the pending list holding the nonce
// does not mean it holds this transaction.
addr, _ := types.Sender(pool.signer, tx)
var seated *types.Transaction
if l := pool.pending[addr]; l != nil {
seated = l.txs.Get(tx.Nonce())
}
if seated != nil && seated.Hash() == hash {
pendingHashes = append(pendingHashes, hash)
} else {
queuedHashes = append(queuedHashes, hash)
}
return true
})

// Remove without charging the price heap per removal: one charge for the
// whole batch reheaps once instead of part way through, the way SetGasTip
// does. Hashes already gone by their turn (dropped as a side effect of an
// earlier removal) are skipped and left out of the count.
drop := func(hashes []common.Hash) int {
dropped := 0
for _, hash := range hashes {
if pool.all.Get(hash) == nil {
continue
}
pool.removeTx(hash, false, true)
dropped++
}
return dropped
}
pending = drop(pendingHashes)
queued = drop(queuedHashes)
pool.priced.Removed(pending + queued)
return pending, queued
}

// SetSigner sets the function to identify signer addresses.
func (pool *LegacyPool) SetSigner(f func(address common.Address) bool) {
pool.isSigner = f
Expand Down
Loading