mirror of https://github.com/ethereum/go-ethereum
all: move main transaction pool into a subpool (#27463)
* all: move main transaction pool into a subpool * go.mod: remove superfluous updates * core/txpool: review fixes, handle txs rejected by all subpools * core/txpool: typospull/27479/head
parent
c375936e81
commit
d40a255e97
@ -0,0 +1,53 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package txpool |
||||||
|
|
||||||
|
import "errors" |
||||||
|
|
||||||
|
var ( |
||||||
|
// ErrAlreadyKnown is returned if the transactions is already contained
|
||||||
|
// within the pool.
|
||||||
|
ErrAlreadyKnown = errors.New("already known") |
||||||
|
|
||||||
|
// ErrInvalidSender is returned if the transaction contains an invalid signature.
|
||||||
|
ErrInvalidSender = errors.New("invalid sender") |
||||||
|
|
||||||
|
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
|
||||||
|
// configured for the transaction pool.
|
||||||
|
ErrUnderpriced = errors.New("transaction underpriced") |
||||||
|
|
||||||
|
// ErrReplaceUnderpriced is returned if a transaction is attempted to be replaced
|
||||||
|
// with a different one without the required price bump.
|
||||||
|
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced") |
||||||
|
|
||||||
|
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
|
||||||
|
// maximum allowance of the current block.
|
||||||
|
ErrGasLimit = errors.New("exceeds block gas limit") |
||||||
|
|
||||||
|
// ErrNegativeValue is a sanity error to ensure no one is able to specify a
|
||||||
|
// transaction with a negative value.
|
||||||
|
ErrNegativeValue = errors.New("negative value") |
||||||
|
|
||||||
|
// ErrOversizedData is returned if the input data of a transaction is greater
|
||||||
|
// than some meaningful limit a user might use. This is not a consensus error
|
||||||
|
// making the transaction invalid, rather a DOS protection.
|
||||||
|
ErrOversizedData = errors.New("oversized data") |
||||||
|
|
||||||
|
// ErrFutureReplacePending is returned if a future transaction replaces a pending
|
||||||
|
// transaction. Future transactions should only be able to replace other future transactions.
|
||||||
|
ErrFutureReplacePending = errors.New("future transaction tries to replace pending") |
||||||
|
) |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,111 @@ |
|||||||
|
// Copyright 2023 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package txpool |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/crypto/kzg4844" |
||||||
|
"github.com/ethereum/go-ethereum/event" |
||||||
|
) |
||||||
|
|
||||||
|
// Transaction is a helper struct to group together a canonical transaction with
|
||||||
|
// satellite data items that are needed by the pool but are not part of the chain.
|
||||||
|
type Transaction struct { |
||||||
|
Tx *types.Transaction // Canonical transaction
|
||||||
|
|
||||||
|
BlobTxBlobs []kzg4844.Blob // Blobs needed by the blob pool
|
||||||
|
BlobTxCommits []kzg4844.Commitment // Commitments needed by the blob pool
|
||||||
|
BlobTxProofs []kzg4844.Proof // Proofs needed by the blob pool
|
||||||
|
} |
||||||
|
|
||||||
|
// SubPool represents a specialized transaction pool that lives on its own (e.g.
|
||||||
|
// blob pool). Since independent of how many specialized pools we have, they do
|
||||||
|
// need to be updated in lockstep and assemble into one coherent view for block
|
||||||
|
// production, this interface defines the common methods that allow the primary
|
||||||
|
// transaction pool to manage the subpools.
|
||||||
|
type SubPool interface { |
||||||
|
// Filter is a selector used to decide whether a transaction whould be added
|
||||||
|
// to this particular subpool.
|
||||||
|
Filter(tx *types.Transaction) bool |
||||||
|
|
||||||
|
// Init sets the base parameters of the subpool, allowing it to load any saved
|
||||||
|
// transactions from disk and also permitting internal maintenance routines to
|
||||||
|
// start up.
|
||||||
|
//
|
||||||
|
// These should not be passed as a constructor argument - nor should the pools
|
||||||
|
// start by themselves - in order to keep multiple subpools in lockstep with
|
||||||
|
// one another.
|
||||||
|
Init(gasTip *big.Int, head *types.Header) error |
||||||
|
|
||||||
|
// Close terminates any background processing threads and releases any held
|
||||||
|
// resources.
|
||||||
|
Close() error |
||||||
|
|
||||||
|
// Reset retrieves the current state of the blockchain and ensures the content
|
||||||
|
// of the transaction pool is valid with regard to the chain state.
|
||||||
|
Reset(oldHead, newHead *types.Header) |
||||||
|
|
||||||
|
// SetGasTip updates the minimum price required by the subpool for a new
|
||||||
|
// transaction, and drops all transactions below this threshold.
|
||||||
|
SetGasTip(tip *big.Int) |
||||||
|
|
||||||
|
// Has returns an indicator whether subpool has a transaction cached with the
|
||||||
|
// given hash.
|
||||||
|
Has(hash common.Hash) bool |
||||||
|
|
||||||
|
// Get returns a transaction if it is contained in the pool, or nil otherwise.
|
||||||
|
Get(hash common.Hash) *Transaction |
||||||
|
|
||||||
|
// Add enqueues a batch of transactions into the pool if they are valid. Due
|
||||||
|
// to the large transaction churn, add may postpone fully integrating the tx
|
||||||
|
// to a later point to batch multiple ones together.
|
||||||
|
Add(txs []*Transaction, local bool, sync bool) []error |
||||||
|
|
||||||
|
// Pending retrieves all currently processable transactions, grouped by origin
|
||||||
|
// account and sorted by nonce.
|
||||||
|
Pending(enforceTips bool) map[common.Address][]*types.Transaction |
||||||
|
|
||||||
|
// SubscribeTransactions subscribes to new transaction events.
|
||||||
|
SubscribeTransactions(ch chan<- core.NewTxsEvent) event.Subscription |
||||||
|
|
||||||
|
// Nonce returns the next nonce of an account, with all transactions executable
|
||||||
|
// by the pool already applied on top.
|
||||||
|
Nonce(addr common.Address) uint64 |
||||||
|
|
||||||
|
// Stats retrieves the current pool stats, namely the number of pending and the
|
||||||
|
// number of queued (non-executable) transactions.
|
||||||
|
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.
|
||||||
|
Content() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) |
||||||
|
|
||||||
|
// ContentFrom retrieves the data content of the transaction pool, returning the
|
||||||
|
// pending as well as queued transactions of this address, grouped by nonce.
|
||||||
|
ContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction) |
||||||
|
|
||||||
|
// Locals retrieves the accounts currently considered local by the pool.
|
||||||
|
Locals() []common.Address |
||||||
|
|
||||||
|
// Status returns the known status (unknown/pending/queued) of a transaction
|
||||||
|
// identified by their hashes.
|
||||||
|
Status(hash common.Hash) TxStatus |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,50 @@ |
|||||||
|
// Copyright 2023 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package event |
||||||
|
|
||||||
|
// JoinSubscriptions joins multiple subscriptions to be able to track them as
|
||||||
|
// one entity and collectively cancel them of consume any errors from them.
|
||||||
|
func JoinSubscriptions(subs ...Subscription) Subscription { |
||||||
|
return NewSubscription(func(unsubbed <-chan struct{}) error { |
||||||
|
// Unsubscribe all subscriptions before returning
|
||||||
|
defer func() { |
||||||
|
for _, sub := range subs { |
||||||
|
sub.Unsubscribe() |
||||||
|
} |
||||||
|
}() |
||||||
|
// Wait for an error on any of the subscriptions and propagate up
|
||||||
|
errc := make(chan error, len(subs)) |
||||||
|
for i := range subs { |
||||||
|
go func(sub Subscription) { |
||||||
|
select { |
||||||
|
case err := <-sub.Err(): |
||||||
|
if err != nil { |
||||||
|
errc <- err |
||||||
|
} |
||||||
|
case <-unsubbed: |
||||||
|
} |
||||||
|
}(subs[i]) |
||||||
|
} |
||||||
|
|
||||||
|
select { |
||||||
|
case err := <-errc: |
||||||
|
return err |
||||||
|
case <-unsubbed: |
||||||
|
return nil |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,175 @@ |
|||||||
|
// Copyright 2023 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package event |
||||||
|
|
||||||
|
import ( |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
func TestMultisub(t *testing.T) { |
||||||
|
// Create a double subscription and ensure events propagate through
|
||||||
|
var ( |
||||||
|
feed1 Feed |
||||||
|
feed2 Feed |
||||||
|
) |
||||||
|
sink1 := make(chan int, 1) |
||||||
|
sink2 := make(chan int, 1) |
||||||
|
|
||||||
|
sub1 := feed1.Subscribe(sink1) |
||||||
|
sub2 := feed2.Subscribe(sink2) |
||||||
|
|
||||||
|
sub := JoinSubscriptions(sub1, sub2) |
||||||
|
|
||||||
|
feed1.Send(1) |
||||||
|
select { |
||||||
|
case n := <-sink1: |
||||||
|
if n != 1 { |
||||||
|
t.Errorf("sink 1 delivery mismatch: have %d, want %d", n, 1) |
||||||
|
} |
||||||
|
default: |
||||||
|
t.Error("sink 1 missing delivery") |
||||||
|
} |
||||||
|
|
||||||
|
feed2.Send(2) |
||||||
|
select { |
||||||
|
case n := <-sink2: |
||||||
|
if n != 2 { |
||||||
|
t.Errorf("sink 2 delivery mismatch: have %d, want %d", n, 2) |
||||||
|
} |
||||||
|
default: |
||||||
|
t.Error("sink 2 missing delivery") |
||||||
|
} |
||||||
|
// Unsubscribe and ensure no more events are delivered
|
||||||
|
sub.Unsubscribe() |
||||||
|
select { |
||||||
|
case <-sub.Err(): |
||||||
|
case <-time.After(50 * time.Millisecond): |
||||||
|
t.Error("multisub didn't propagate closure") |
||||||
|
} |
||||||
|
|
||||||
|
feed1.Send(11) |
||||||
|
select { |
||||||
|
case n := <-sink1: |
||||||
|
t.Errorf("sink 1 unexpected delivery: %d", n) |
||||||
|
default: |
||||||
|
} |
||||||
|
|
||||||
|
feed2.Send(22) |
||||||
|
select { |
||||||
|
case n := <-sink2: |
||||||
|
t.Errorf("sink 2 unexpected delivery: %d", n) |
||||||
|
default: |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestMutisubPartialUnsubscribe(t *testing.T) { |
||||||
|
// Create a double subscription but terminate one half, ensuring no error
|
||||||
|
// is propagated yet up to the outer subscription
|
||||||
|
var ( |
||||||
|
feed1 Feed |
||||||
|
feed2 Feed |
||||||
|
) |
||||||
|
sink1 := make(chan int, 1) |
||||||
|
sink2 := make(chan int, 1) |
||||||
|
|
||||||
|
sub1 := feed1.Subscribe(sink1) |
||||||
|
sub2 := feed2.Subscribe(sink2) |
||||||
|
|
||||||
|
sub := JoinSubscriptions(sub1, sub2) |
||||||
|
|
||||||
|
sub1.Unsubscribe() |
||||||
|
select { |
||||||
|
case <-sub.Err(): |
||||||
|
t.Error("multisub propagated closure") |
||||||
|
case <-time.After(50 * time.Millisecond): |
||||||
|
} |
||||||
|
// Ensure that events cross only the second feed
|
||||||
|
feed1.Send(1) |
||||||
|
select { |
||||||
|
case n := <-sink1: |
||||||
|
t.Errorf("sink 1 unexpected delivery: %d", n) |
||||||
|
default: |
||||||
|
} |
||||||
|
|
||||||
|
feed2.Send(2) |
||||||
|
select { |
||||||
|
case n := <-sink2: |
||||||
|
if n != 2 { |
||||||
|
t.Errorf("sink 2 delivery mismatch: have %d, want %d", n, 2) |
||||||
|
} |
||||||
|
default: |
||||||
|
t.Error("sink 2 missing delivery") |
||||||
|
} |
||||||
|
// Unsubscribe and ensure no more events are delivered
|
||||||
|
sub.Unsubscribe() |
||||||
|
select { |
||||||
|
case <-sub.Err(): |
||||||
|
case <-time.After(50 * time.Millisecond): |
||||||
|
t.Error("multisub didn't propagate closure") |
||||||
|
} |
||||||
|
|
||||||
|
feed1.Send(11) |
||||||
|
select { |
||||||
|
case n := <-sink1: |
||||||
|
t.Errorf("sink 1 unexpected delivery: %d", n) |
||||||
|
default: |
||||||
|
} |
||||||
|
|
||||||
|
feed2.Send(22) |
||||||
|
select { |
||||||
|
case n := <-sink2: |
||||||
|
t.Errorf("sink 2 unexpected delivery: %d", n) |
||||||
|
default: |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestMultisubFullUnsubscribe(t *testing.T) { |
||||||
|
// Create a double subscription and terminate the multi sub, ensuring an
|
||||||
|
// error is propagated up.
|
||||||
|
var ( |
||||||
|
feed1 Feed |
||||||
|
feed2 Feed |
||||||
|
) |
||||||
|
sink1 := make(chan int, 1) |
||||||
|
sink2 := make(chan int, 1) |
||||||
|
|
||||||
|
sub1 := feed1.Subscribe(sink1) |
||||||
|
sub2 := feed2.Subscribe(sink2) |
||||||
|
|
||||||
|
sub := JoinSubscriptions(sub1, sub2) |
||||||
|
sub.Unsubscribe() |
||||||
|
select { |
||||||
|
case <-sub.Err(): |
||||||
|
case <-time.After(50 * time.Millisecond): |
||||||
|
t.Error("multisub didn't propagate closure") |
||||||
|
} |
||||||
|
// Ensure no more events are delivered
|
||||||
|
feed1.Send(1) |
||||||
|
select { |
||||||
|
case n := <-sink1: |
||||||
|
t.Errorf("sink 1 unexpected delivery: %d", n) |
||||||
|
default: |
||||||
|
} |
||||||
|
|
||||||
|
feed2.Send(2) |
||||||
|
select { |
||||||
|
case n := <-sink2: |
||||||
|
t.Errorf("sink 2 unexpected delivery: %d", n) |
||||||
|
default: |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue