mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 13:46:43 +00:00
core/txpool/legacypool: move queue out of main txpool
This commit is contained in:
parent
8a20e87fcf
commit
a25e5f626c
1 changed files with 226 additions and 0 deletions
226
core/txpool/legacypool/queue.go
Normal file
226
core/txpool/legacypool/queue.go
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
package legacypool
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/txpool"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
type queue struct {
|
||||
config Config
|
||||
signer types.Signer
|
||||
queued map[common.Address]*list // Queued but non-processable transactions
|
||||
beats map[common.Address]time.Time // Last heartbeat from each known account
|
||||
}
|
||||
|
||||
func newQueue(config Config, signer types.Signer) *queue {
|
||||
return &queue{
|
||||
signer: signer,
|
||||
config: config,
|
||||
queued: make(map[common.Address]*list),
|
||||
beats: make(map[common.Address]time.Time),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *queue) evict(force bool) []common.Hash {
|
||||
removed := make([]common.Hash, 0)
|
||||
for addr, list := range q.queued {
|
||||
// Any transactions old enough should be removed
|
||||
if force || time.Since(q.beats[addr]) > q.config.Lifetime {
|
||||
list := list.Flatten()
|
||||
for _, tx := range list {
|
||||
q.removeTx(addr, tx)
|
||||
removed = append(removed, tx.Hash())
|
||||
}
|
||||
queuedEvictionMeter.Mark(int64(len(list)))
|
||||
}
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
||||
func (q *queue) stats() int {
|
||||
queued := 0
|
||||
for _, list := range q.queued {
|
||||
queued += list.Len()
|
||||
}
|
||||
return queued
|
||||
}
|
||||
|
||||
func (q *queue) content() map[common.Address][]*types.Transaction {
|
||||
queued := make(map[common.Address][]*types.Transaction, len(q.queued))
|
||||
for addr, list := range q.queued {
|
||||
queued[addr] = list.Flatten()
|
||||
}
|
||||
return queued
|
||||
}
|
||||
|
||||
func (q *queue) contentFrom(addr common.Address) []*types.Transaction {
|
||||
var queued []*types.Transaction
|
||||
if list, ok := q.get(addr); ok {
|
||||
queued = list.Flatten()
|
||||
}
|
||||
return queued
|
||||
}
|
||||
|
||||
func (q *queue) get(addr common.Address) (*list, bool) {
|
||||
l, ok := q.queued[addr]
|
||||
return l, ok
|
||||
}
|
||||
|
||||
func (q *queue) bump(addr common.Address) {
|
||||
q.beats[addr] = time.Now()
|
||||
}
|
||||
|
||||
func (q *queue) addresses() []common.Address {
|
||||
addrs := make([]common.Address, 0, len(q.queued))
|
||||
for addr := range q.queued {
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func (q queue) removeTx(addr common.Address, tx *types.Transaction) {
|
||||
if future := q.queued[addr]; future != nil {
|
||||
if txOld := future.txs.Get(tx.Nonce()); txOld != nil && txOld.Hash() != tx.Hash() {
|
||||
// Edge case, a different transaction
|
||||
// with the same nonce is in the queued, just ignore
|
||||
return
|
||||
}
|
||||
if removed, _ := future.Remove(tx); removed {
|
||||
// Reduce the queued counter
|
||||
queuedGauge.Dec(1)
|
||||
}
|
||||
if future.Empty() {
|
||||
delete(q.queued, addr)
|
||||
delete(q.beats, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *queue) add(hash common.Hash, tx *types.Transaction) (*common.Hash, error) {
|
||||
// Try to insert the transaction into the future queue
|
||||
from, _ := types.Sender(q.signer, tx) // already validated
|
||||
if q.queued[from] == nil {
|
||||
q.queued[from] = newList(false)
|
||||
}
|
||||
inserted, old := q.queued[from].Add(tx, q.config.PriceBump)
|
||||
if !inserted {
|
||||
// An older transaction was better, discard this
|
||||
queuedDiscardMeter.Mark(1)
|
||||
return nil, txpool.ErrReplaceUnderpriced
|
||||
}
|
||||
// If we never record the heartbeat, do it right now.
|
||||
if _, exist := q.beats[from]; !exist {
|
||||
q.beats[from] = time.Now()
|
||||
}
|
||||
if old == nil {
|
||||
// Nothing was replaced, bump the queued counter
|
||||
queuedGauge.Inc(1)
|
||||
return nil, nil
|
||||
}
|
||||
h := old.Hash()
|
||||
// Transaction was replaced, bump the replacement counter
|
||||
queuedReplaceMeter.Mark(1)
|
||||
return &h, nil
|
||||
}
|
||||
|
||||
func (q *queue) promoteExecutables(accounts []common.Address, gasLimit uint64, currentState *state.StateDB, nonces *noncer) ([]*types.Transaction, []common.Hash) {
|
||||
// Track the promoteable transactions to broadcast them at once
|
||||
var promoteable []*types.Transaction
|
||||
var removeable []common.Hash
|
||||
|
||||
// Iterate over all accounts and promote any executable transactions
|
||||
for _, addr := range accounts {
|
||||
list := q.queued[addr]
|
||||
if list == nil {
|
||||
continue // Just in case someone calls with a non existing account
|
||||
}
|
||||
// Drop all transactions that are deemed too old (low nonce)
|
||||
forwards := list.Forward(currentState.GetNonce(addr))
|
||||
for _, tx := range forwards {
|
||||
removeable = append(removeable, tx.Hash())
|
||||
}
|
||||
log.Trace("Removing old queued transactions", "count", len(forwards))
|
||||
// Drop all transactions that are too costly (low balance or out of gas)
|
||||
drops, _ := list.Filter(currentState.GetBalance(addr), gasLimit)
|
||||
for _, tx := range drops {
|
||||
removeable = append(removeable, tx.Hash())
|
||||
}
|
||||
log.Trace("Removing unpayable queued transactions", "count", len(drops))
|
||||
queuedNofundsMeter.Mark(int64(len(drops)))
|
||||
|
||||
// Gather all executable transactions and promote them
|
||||
readies := list.Ready(nonces.get(addr))
|
||||
promoteable = append(promoteable, readies...)
|
||||
log.Trace("Promoting queued transactions", "count", len(promoteable))
|
||||
queuedGauge.Dec(int64(len(readies)))
|
||||
|
||||
// Drop all transactions over the allowed limit
|
||||
var caps = list.Cap(int(q.config.AccountQueue))
|
||||
for _, tx := range caps {
|
||||
hash := tx.Hash()
|
||||
removeable = append(removeable, hash)
|
||||
log.Trace("Removing cap-exceeding queued transaction", "hash", hash)
|
||||
}
|
||||
queuedRateLimitMeter.Mark(int64(len(caps)))
|
||||
queuedGauge.Dec(int64(len(removeable)))
|
||||
|
||||
// Delete the entire queue entry if it became empty.
|
||||
if list.Empty() {
|
||||
delete(q.queued, addr)
|
||||
delete(q.beats, addr)
|
||||
}
|
||||
}
|
||||
return promoteable, removeable
|
||||
}
|
||||
|
||||
func (q *queue) truncate() []common.Hash {
|
||||
queued := uint64(0)
|
||||
for _, list := range q.queued {
|
||||
queued += uint64(list.Len())
|
||||
}
|
||||
if queued <= q.config.GlobalQueue {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sort all accounts with queued transactions by heartbeat
|
||||
addresses := make(addressesByHeartbeat, 0, len(q.queued))
|
||||
for addr := range q.queued {
|
||||
addresses = append(addresses, addressByHeartbeat{addr, q.beats[addr]})
|
||||
}
|
||||
sort.Sort(sort.Reverse(addresses))
|
||||
removed := make([]common.Hash, 0)
|
||||
|
||||
// Drop transactions until the total is below the limit
|
||||
for drop := queued - q.config.GlobalQueue; drop > 0 && len(addresses) > 0; {
|
||||
addr := addresses[len(addresses)-1]
|
||||
list := q.queued[addr.address]
|
||||
|
||||
addresses = addresses[:len(addresses)-1]
|
||||
|
||||
// Drop all transactions if they are less than the overflow
|
||||
if size := uint64(list.Len()); size <= drop {
|
||||
for _, tx := range list.Flatten() {
|
||||
q.removeTx(addr.address, tx)
|
||||
removed = append(removed, tx.Hash())
|
||||
}
|
||||
drop -= size
|
||||
queuedRateLimitMeter.Mark(int64(size))
|
||||
continue
|
||||
}
|
||||
// Otherwise drop only last few transactions
|
||||
txs := list.Flatten()
|
||||
for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
|
||||
q.removeTx(addr.address, txs[i])
|
||||
removed = append(removed, txs[i].Hash())
|
||||
drop--
|
||||
queuedRateLimitMeter.Mark(1)
|
||||
}
|
||||
}
|
||||
return removed
|
||||
}
|
||||
Loading…
Reference in a new issue