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