core/txpool: revert changes in txpool

This commit is contained in:
Gary Rong 2023-09-27 18:55:46 +08:00
parent 281a704544
commit c5890605b3
3 changed files with 45 additions and 115 deletions

View file

@ -307,8 +307,10 @@ type BlobPool struct {
spent map[common.Address]*uint256.Int // Expenditure tracking for individual accounts
evict *evictHeap // Heap of cheapest accounts for eviction when full
eventFeed event.Feed // Event feed to send out new tx events on pool inclusion
lock sync.RWMutex // Mutex protecting the pool during reorg handling
eventFeed event.Feed // Event feed to send out new tx events on pool inclusion
eventScope event.SubscriptionScope // Event scope to track and mass unsubscribe on termination
lock sync.RWMutex // Mutex protecting the pool during reorg handling
}
// New creates a new blob transaction pool to gather, sort and filter inbound
@ -353,7 +355,13 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr
return err
}
}
// Initialize the state with head block, or fallback to empty one in
// case the head state is not available(might occur when node is not
// fully synced).
state, err := p.chain.StateAt(head.Root)
if err != nil {
state, err = p.chain.StateAt(types.EmptyRootHash)
}
if err != nil {
return err
}
@ -428,6 +436,8 @@ func (p *BlobPool) Close() error {
if err := p.store.Close(); err != nil {
errs = append(errs, err)
}
p.eventScope.Close()
switch {
case errs == nil:
return nil
@ -1461,7 +1471,7 @@ func (p *BlobPool) updateLimboMetrics() {
// SubscribeTransactions registers a subscription of NewTxsEvent and
// starts sending event to the given channel.
func (p *BlobPool) SubscribeTransactions(ch chan<- core.NewTxsEvent) event.Subscription {
return p.eventFeed.Subscribe(ch)
return p.eventScope.Track(p.eventFeed.Subscribe(ch))
}
// Nonce returns the next nonce of an account, with all transactions executable

View file

@ -208,6 +208,7 @@ type LegacyPool struct {
chain BlockChain
gasTip atomic.Pointer[big.Int]
txFeed event.Feed
scope event.SubscriptionScope
signer types.Signer
mu sync.RWMutex
@ -297,7 +298,20 @@ func (pool *LegacyPool) Init(gasTip *big.Int, head *types.Header, reserve txpool
// Set the basic pool parameters
pool.gasTip.Store(gasTip)
pool.reset(nil, head)
// Initialize the state with head block, or fallback to empty one in
// case the head state is not available(might occur when node is not
// fully synced).
statedb, err := pool.chain.StateAt(head.Root)
if err != nil {
statedb, err = pool.chain.StateAt(types.EmptyRootHash)
}
if err != nil {
return err
}
pool.currentHead.Store(head)
pool.currentState = statedb
pool.pendingNonces = newNoncer(statedb)
// Start the reorg loop early, so it can handle requests generated during
// journal loading.
@ -390,6 +404,9 @@ func (pool *LegacyPool) loop() {
// Close terminates the transaction pool.
func (pool *LegacyPool) Close() error {
// Unsubscribe all subscriptions registered from txpool
pool.scope.Close()
// Terminate the pool reorger and return
close(pool.reorgShutdownCh)
pool.wg.Wait()
@ -411,7 +428,7 @@ func (pool *LegacyPool) Reset(oldHead, newHead *types.Header) {
// SubscribeTransactions registers a subscription of NewTxsEvent and
// starts sending event to the given channel.
func (pool *LegacyPool) SubscribeTransactions(ch chan<- core.NewTxsEvent) event.Subscription {
return pool.txFeed.Subscribe(ch)
return pool.scope.Track(pool.txFeed.Subscribe(ch))
}
// SetGasTip updates the minimum gas tip required by the transaction pool for a

View file

@ -21,7 +21,6 @@ import (
"fmt"
"math/big"
"sync"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
@ -56,9 +55,6 @@ type BlockChain interface {
// CurrentBlock returns the current head of the chain.
CurrentBlock() *types.Header
// HasState checks if state is present in the database or not.
HasState(common.Hash) bool
// SubscribeChainHeadEvent subscribes to new blocks being added to the chain.
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
}
@ -69,8 +65,7 @@ type BlockChain interface {
// They exit the pool when they are included in the blockchain or evicted due to
// resource constraints.
type TxPool struct {
inited atomic.Bool // Flag whether the subpools are initialized
subpools []SubPool // List of subpools for specialized transaction handling
subpools []SubPool // List of subpools for specialized transaction handling
reservations map[common.Address]SubPool // Map with the account to pool reservations
reserveLock sync.Mutex // Lock protecting the account reservations
@ -92,63 +87,18 @@ func New(gasTip *big.Int, chain BlockChain, subpools []SubPool) (*TxPool, error)
reservations: make(map[common.Address]SubPool),
quit: make(chan chan error),
}
if chain.HasState(head.Root) {
if err := pool.init(gasTip, head); err != nil {
for i, subpool := range subpools {
if err := subpool.Init(gasTip, head, pool.reserver(i, subpool)); err != nil {
for j := i - 1; j >= 0; j-- {
subpools[j].Close()
}
return nil, err
}
go pool.loop(head, chain)
} else {
go pool.lazyInit(gasTip, chain)
}
go pool.loop(head, chain)
return pool, nil
}
// init performs the initialization for subpools.
func (p *TxPool) init(gasTip *big.Int, head *types.Header) error {
for i, subpool := range p.subpools {
if err := subpool.Init(gasTip, head, p.reserver(i, subpool)); err != nil {
for j := i - 1; j >= 0; j-- {
p.subpools[j].Close()
}
return err
}
}
p.inited.Store(true)
log.Info("Initialized subpools", "head", head.Number, "hash", head.Hash())
return nil
}
// lazyInit waits the signal that state sync is completed and initializes the subpools.
func (p *TxPool) lazyInit(gasTip *big.Int, chain BlockChain) {
var (
newHeadCh = make(chan core.ChainHeadEvent)
newHeadSub = chain.SubscribeChainHeadEvent(newHeadCh)
)
defer newHeadSub.Unsubscribe()
var errc chan error
for errc == nil {
select {
case event := <-newHeadCh:
head := event.Block.Header()
if !chain.HasState(head.Root) {
continue // shouldn't happen
}
if err := p.init(gasTip, head); err != nil {
// TODO(rjl493456442) can we shutdown the node gracefully?
log.Crit("Failed to lazy init subpools", "err", err)
}
go p.loop(head, chain)
return
case errc = <-p.quit:
// Termination requested, break out on the next loop round
}
}
// Notify the closer of termination (no error possible for now)
errc <- nil
}
// reserver is a method to create an address reservation callback to exclusively
// assign/deassign addresses to/from subpools. This can ensure that at any point
// in time, only a single subpool is able to manage an account, avoiding cross
@ -205,19 +155,15 @@ func (p *TxPool) Close() error {
if err := <-errc; err != nil {
errs = append(errs, err)
}
// Terminate each subpool if they are initialized
if p.inited.Load() {
for _, subpool := range p.subpools {
if err := subpool.Close(); err != nil {
errs = append(errs, err)
}
// Terminate each subpool
for _, subpool := range p.subpools {
if err := subpool.Close(); err != nil {
errs = append(errs, err)
}
}
// Terminate all the subpool subscriptions
p.subs.Close()
if len(errs) > 0 {
return fmt.Errorf("txpool close errors: %v", errs)
return fmt.Errorf("subpool close errors: %v", errs)
}
return nil
}
@ -286,10 +232,6 @@ func (p *TxPool) loop(head *types.Header, chain BlockChain) {
// SetGasTip updates the minimum gas tip required by the transaction pool for a
// new transaction, and drops all transactions below this threshold.
func (p *TxPool) SetGasTip(tip *big.Int) {
if !p.inited.Load() {
log.Info("Skip tip adjustment as txpool hasn't been initialized", "provided", tip)
return
}
for _, subpool := range p.subpools {
subpool.SetGasTip(tip)
}
@ -298,9 +240,6 @@ func (p *TxPool) SetGasTip(tip *big.Int) {
// Has returns an indicator whether the pool has a transaction cached with the
// given hash.
func (p *TxPool) Has(hash common.Hash) bool {
if !p.inited.Load() {
return false
}
for _, subpool := range p.subpools {
if subpool.Has(hash) {
return true
@ -311,9 +250,6 @@ func (p *TxPool) Has(hash common.Hash) bool {
// Get returns a transaction if it is contained in the pool, or nil otherwise.
func (p *TxPool) Get(hash common.Hash) *types.Transaction {
if !p.inited.Load() {
return nil
}
for _, subpool := range p.subpools {
if tx := subpool.Get(hash); tx != nil {
return tx
@ -326,13 +262,6 @@ func (p *TxPool) Get(hash common.Hash) *types.Transaction {
// to the large transaction churn, add may postpone fully integrating the tx
// to a later point to batch multiple ones together.
func (p *TxPool) Add(txs []*types.Transaction, local bool, sync bool) []error {
if !p.inited.Load() {
errs := make([]error, len(txs))
for i := 0; i < len(errs); i++ {
errs[i] = errors.New("txpool is not initialized")
}
return errs
}
// Split the input transactions between the subpools. It shouldn't really
// happen that we receive merged batches, but better graceful than strange
// errors.
@ -378,9 +307,6 @@ func (p *TxPool) Add(txs []*types.Transaction, local bool, sync bool) []error {
// Pending retrieves all currently processable transactions, grouped by origin
// account and sorted by nonce.
func (p *TxPool) Pending(enforceTips bool) map[common.Address][]*LazyTransaction {
if !p.inited.Load() {
return nil
}
txs := make(map[common.Address][]*LazyTransaction)
for _, subpool := range p.subpools {
for addr, set := range subpool.Pending(enforceTips) {
@ -403,9 +329,6 @@ func (p *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscrip
// Nonce returns the next nonce of an account, with all transactions executable
// by the pool already applied on top.
func (p *TxPool) Nonce(addr common.Address) uint64 {
if !p.inited.Load() {
return 0
}
// Since (for now) accounts are unique to subpools, only one pool will have
// (at max) a non-state nonce. To avoid stateful lookups, just return the
// highest nonce for now.
@ -421,9 +344,6 @@ func (p *TxPool) Nonce(addr common.Address) uint64 {
// Stats retrieves the current pool stats, namely the number of pending and the
// number of queued (non-executable) transactions.
func (p *TxPool) Stats() (int, int) {
if !p.inited.Load() {
return 0, 0
}
var runnable, blocked int
for _, subpool := range p.subpools {
run, block := subpool.Stats()
@ -437,9 +357,6 @@ func (p *TxPool) Stats() (int, int) {
// Content retrieves the data content of the transaction pool, returning all the
// pending as well as queued transactions, grouped by account and sorted by nonce.
func (p *TxPool) Content() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) {
if !p.inited.Load() {
return nil, nil
}
var (
runnable = make(map[common.Address][]*types.Transaction)
blocked = make(map[common.Address][]*types.Transaction)
@ -460,9 +377,6 @@ func (p *TxPool) Content() (map[common.Address][]*types.Transaction, map[common.
// ContentFrom retrieves the data content of the transaction pool, returning the
// pending as well as queued transactions of this address, grouped by nonce.
func (p *TxPool) ContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction) {
if !p.inited.Load() {
return nil, nil
}
for _, subpool := range p.subpools {
run, block := subpool.ContentFrom(addr)
if len(run) != 0 || len(block) != 0 {
@ -474,9 +388,6 @@ func (p *TxPool) ContentFrom(addr common.Address) ([]*types.Transaction, []*type
// Locals retrieves the accounts currently considered local by the pool.
func (p *TxPool) Locals() []common.Address {
if !p.inited.Load() {
return nil
}
// Retrieve the locals from each subpool and deduplicate them
locals := make(map[common.Address]struct{})
for _, subpool := range p.subpools {
@ -495,9 +406,6 @@ func (p *TxPool) Locals() []common.Address {
// Status returns the known status (unknown/pending/queued) of a transaction
// identified by their hashes.
func (p *TxPool) Status(hash common.Hash) TxStatus {
if !p.inited.Load() {
return TxStatusUnknown
}
for _, subpool := range p.subpools {
if status := subpool.Status(hash); status != TxStatusUnknown {
return status
@ -505,8 +413,3 @@ func (p *TxPool) Status(hash common.Hash) TxStatus {
}
return TxStatusUnknown
}
// Inited returns the indicator if txpool is fully initialized.
func (p *TxPool) Inited() bool {
return p.inited.Load()
}