mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
feat(txpool): StatsWithMinBaseFee (#884)
* feat(txpool): implement StatsWithMinBaseFee * feat(gpo): use StatsWithMinBaseFee to filter out tx below the current blocks base fee * feat(les/txpool): implement StatsWithMinBaseFee * use flatten() (lowercase) to avoid copying of tx list * chore: auto version bump [bot] --------- Co-authored-by: jonastheis <jonastheis@users.noreply.github.com>
This commit is contained in:
parent
ae45e1230e
commit
8c7a3aed74
8 changed files with 134 additions and 2 deletions
|
|
@ -496,6 +496,40 @@ func (pool *TxPool) stats() (int, int) {
|
||||||
return pending, queued
|
return pending, queued
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatsWithMinBaseFee retrieves the current pool stats, namely the number of pending and the
|
||||||
|
// number of queued (non-executable) transactions greater equal minBaseFee.
|
||||||
|
func (pool *TxPool) StatsWithMinBaseFee(minBaseFee *big.Int) (int, int) {
|
||||||
|
pool.mu.RLock()
|
||||||
|
defer pool.mu.RUnlock()
|
||||||
|
|
||||||
|
return pool.statsWithMinBaseFee(minBaseFee)
|
||||||
|
}
|
||||||
|
|
||||||
|
// statsWithMinBaseFee retrieves the current pool stats, namely the number of pending and the
|
||||||
|
// number of queued (non-executable) transactions greater equal minBaseFee.
|
||||||
|
func (pool *TxPool) statsWithMinBaseFee(minBaseFee *big.Int) (int, int) {
|
||||||
|
pending := 0
|
||||||
|
for _, list := range pool.pending {
|
||||||
|
for _, tx := range list.txs.flatten() {
|
||||||
|
if _, err := tx.EffectiveGasTip(minBaseFee); err != nil {
|
||||||
|
break // basefee too low, discard rest of txs with higher nonces from the account
|
||||||
|
}
|
||||||
|
pending++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queued := 0
|
||||||
|
for _, list := range pool.queue {
|
||||||
|
for _, tx := range list.txs.flatten() {
|
||||||
|
if _, err := tx.EffectiveGasTip(minBaseFee); err != nil {
|
||||||
|
break // basefee too low, discard rest of txs with higher nonces from the account
|
||||||
|
}
|
||||||
|
queued++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pending, queued
|
||||||
|
}
|
||||||
|
|
||||||
// Content retrieves the data content of the transaction pool, returning all the
|
// 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.
|
// pending as well as queued transactions, grouped by account and sorted by nonce.
|
||||||
func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
||||||
|
|
|
||||||
|
|
@ -2601,3 +2601,74 @@ func TestPoolPending(t *testing.T) {
|
||||||
maxAccounts := 10
|
maxAccounts := 10
|
||||||
assert.Len(t, pool.PendingWithMax(false, maxAccounts), maxAccounts)
|
assert.Len(t, pool.PendingWithMax(false, maxAccounts), maxAccounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStatsWithMinBaseFee(t *testing.T) {
|
||||||
|
// Create the pool to test the pricing enforcement with
|
||||||
|
pool, _ := setupTxPoolWithConfig(eip1559NoL1DataFeeConfig)
|
||||||
|
defer pool.Stop()
|
||||||
|
|
||||||
|
// Keep track of transaction events to ensure all executables get announced
|
||||||
|
events := make(chan NewTxsEvent, 32)
|
||||||
|
sub := pool.txFeed.Subscribe(events)
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
|
// Create a number of test accounts and fund them
|
||||||
|
keys := make([]*ecdsa.PrivateKey, 4)
|
||||||
|
for i := 0; i < len(keys); i++ {
|
||||||
|
keys[i], _ = crypto.GenerateKey()
|
||||||
|
testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
|
||||||
|
}
|
||||||
|
// Generate and queue a batch of transactions, both pending and queued
|
||||||
|
txs := types.Transactions{}
|
||||||
|
|
||||||
|
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(5), keys[0])) // will stay pending
|
||||||
|
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0]))
|
||||||
|
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[0]))
|
||||||
|
|
||||||
|
txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(5), big.NewInt(1), keys[1])) // will stay pending
|
||||||
|
txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(3), big.NewInt(2), keys[1])) // will stay pending
|
||||||
|
txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(2), big.NewInt(1), keys[1]))
|
||||||
|
txs = append(txs, dynamicFeeTx(3, 100000, big.NewInt(4), big.NewInt(1), keys[1]))
|
||||||
|
|
||||||
|
localTx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[3])
|
||||||
|
|
||||||
|
// queued
|
||||||
|
txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(3), big.NewInt(2), keys[2])) // will stay queued
|
||||||
|
txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2]))
|
||||||
|
txs = append(txs, dynamicFeeTx(3, 100000, big.NewInt(2), big.NewInt(2), keys[2]))
|
||||||
|
|
||||||
|
// Import the batch and that both pending and queued transactions match up
|
||||||
|
pool.AddRemotesSync(txs)
|
||||||
|
pool.AddLocal(localTx)
|
||||||
|
|
||||||
|
minBaseFee := big.NewInt(3)
|
||||||
|
pool.priced.SetBaseFee(minBaseFee)
|
||||||
|
|
||||||
|
// Check pool.Stats(), all tx should be counted
|
||||||
|
{
|
||||||
|
pending, queued := pool.Stats()
|
||||||
|
if pending != 8 {
|
||||||
|
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 8)
|
||||||
|
}
|
||||||
|
if queued != 3 {
|
||||||
|
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
|
||||||
|
}
|
||||||
|
if err := validateEvents(events, 8); err != nil {
|
||||||
|
t.Fatalf("original event firing failed: %v", err)
|
||||||
|
}
|
||||||
|
if err := validateTxPoolInternals(pool); err != nil {
|
||||||
|
t.Fatalf("pool internal state corrupted: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check pool.StatsWithMinBaseFee(), only tx with base fee >= minBaseFee should be counted
|
||||||
|
{
|
||||||
|
pending, queued := pool.StatsWithMinBaseFee(minBaseFee)
|
||||||
|
if pending != 3 {
|
||||||
|
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
|
||||||
|
}
|
||||||
|
if queued != 1 {
|
||||||
|
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -292,6 +292,10 @@ func (b *EthAPIBackend) Stats() (pending int, queued int) {
|
||||||
return b.eth.txPool.Stats()
|
return b.eth.txPool.Stats()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthAPIBackend) StatsWithMinBaseFee(minBaseFee *big.Int) (pending int, queued int) {
|
||||||
|
return b.eth.txPool.StatsWithMinBaseFee(minBaseFee)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
func (b *EthAPIBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
||||||
return b.eth.TxPool().Content()
|
return b.eth.TxPool().Content()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ type OracleBackend interface {
|
||||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||||
StateAt(root common.Hash) (*state.StateDB, error)
|
StateAt(root common.Hash) (*state.StateDB, error)
|
||||||
Stats() (pending int, queued int)
|
Stats() (pending int, queued int)
|
||||||
|
StatsWithMinBaseFee(minBaseFee *big.Int) (pending int, queued int)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oracle recommends gas prices based on the content of recent
|
// Oracle recommends gas prices based on the content of recent
|
||||||
|
|
@ -192,7 +193,7 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
// If pending txs are less than oracle.congestedThreshold, we consider the network to be non-congested and suggest
|
// If pending txs are less than oracle.congestedThreshold, we consider the network to be non-congested and suggest
|
||||||
// a minimal tip cap. This is to prevent users from overpaying for gas when the network is not congested and a few
|
// a minimal tip cap. This is to prevent users from overpaying for gas when the network is not congested and a few
|
||||||
// high-priced txs are causing the suggested tip cap to be high.
|
// high-priced txs are causing the suggested tip cap to be high.
|
||||||
pendingTxCount, _ := oracle.backend.Stats()
|
pendingTxCount, _ := oracle.backend.StatsWithMinBaseFee(head.BaseFee)
|
||||||
if pendingTxCount < oracle.congestedThreshold {
|
if pendingTxCount < oracle.congestedThreshold {
|
||||||
// Before Curie (EIP-1559), we need to return the total suggested gas price. After Curie we return 2 wei as the tip cap,
|
// Before Curie (EIP-1559), we need to return the total suggested gas price. After Curie we return 2 wei as the tip cap,
|
||||||
// as the base fee is set separately or added manually for legacy transactions.
|
// as the base fee is set separately or added manually for legacy transactions.
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,10 @@ func (b *testBackend) Stats() (int, int) {
|
||||||
return b.pendingTxCount, 0
|
return b.pendingTxCount, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) StatsWithMinBaseFee(minBaseFee *big.Int) (int, int) {
|
||||||
|
return b.pendingTxCount, 0
|
||||||
|
}
|
||||||
|
|
||||||
func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool, pendingTxCount int) *testBackend {
|
func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool, pendingTxCount int) *testBackend {
|
||||||
var (
|
var (
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
|
|
||||||
|
|
@ -225,6 +225,10 @@ func (b *LesApiBackend) Stats() (pending int, queued int) {
|
||||||
return b.eth.txPool.Stats(), 0
|
return b.eth.txPool.Stats(), 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *LesApiBackend) StatsWithMinBaseFee(minBaseFee *big.Int) (pending int, queued int) {
|
||||||
|
return b.eth.txPool.StatsWithMinBaseFee(minBaseFee), 0
|
||||||
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
||||||
return b.eth.txPool.Content()
|
return b.eth.txPool.Content()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -353,6 +353,20 @@ func (pool *TxPool) Stats() (pending int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatsWithMinBaseFee returns the number of currently pending (locally created) transactions and ignores the base fee.
|
||||||
|
func (pool *TxPool) StatsWithMinBaseFee(minBaseFee *big.Int) (pending int) {
|
||||||
|
pool.mu.RLock()
|
||||||
|
defer pool.mu.RUnlock()
|
||||||
|
|
||||||
|
for _, tx := range pool.pending {
|
||||||
|
if _, err := tx.EffectiveGasTip(minBaseFee); err == nil {
|
||||||
|
pending++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pending
|
||||||
|
}
|
||||||
|
|
||||||
// validateTx checks whether a transaction is valid according to the consensus rules.
|
// validateTx checks whether a transaction is valid according to the consensus rules.
|
||||||
func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error {
|
func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error {
|
||||||
// Validate sender
|
// Validate sender
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 5 // Major version component of the current release
|
VersionMajor = 5 // Major version component of the current release
|
||||||
VersionMinor = 5 // Minor version component of the current release
|
VersionMinor = 5 // Minor version component of the current release
|
||||||
VersionPatch = 7 // Patch version component of the current release
|
VersionPatch = 8 // Patch version component of the current release
|
||||||
VersionMeta = "mainnet" // Version metadata to append to the version string
|
VersionMeta = "mainnet" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue