Skip to content

Commit f1da70f

Browse files
committed
core/txpool/blobpool: reduce overhead in benchmark, make blobpool validation-function settable
The benchmarks for assembling the pending lazy-tx lists were extremely intense in setup, 1. writing a lot of data to disk, and 2. performing a lot of expensive blob validations. This change fixes both, by replacing billy with a no-op store, and replacing the blobpool validation function with a yes-dummy.
1 parent 393d8b7 commit f1da70f

File tree

3 files changed

+47
-9
lines changed

3 files changed

+47
-9
lines changed

core/txpool/blobpool/blobpool.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,10 @@ type BlobPool struct {
318318
discoverFeed event.Feed // Event feed to send out new tx events on pool discovery (reorg excluded)
319319
insertFeed event.Feed // Event feed to send out new tx events on pool inclusion (reorg included)
320320

321+
// txValidationFn defaults to txpool.ValidateTransaction, but can be
322+
// overridden for testing purposes.
323+
txValidationFn txpool.ValidationFunction
324+
321325
lock sync.RWMutex // Mutex protecting the pool during reorg handling
322326
}
323327

@@ -329,12 +333,13 @@ func New(config Config, chain BlockChain) *BlobPool {
329333

330334
// Create the transaction pool with its initial settings
331335
return &BlobPool{
332-
config: config,
333-
signer: types.LatestSigner(chain.Config()),
334-
chain: chain,
335-
lookup: newLookup(),
336-
index: make(map[common.Address][]*blobTxMeta),
337-
spent: make(map[common.Address]*uint256.Int),
336+
config: config,
337+
signer: types.LatestSigner(chain.Config()),
338+
chain: chain,
339+
lookup: newLookup(),
340+
index: make(map[common.Address][]*blobTxMeta),
341+
spent: make(map[common.Address]*uint256.Int),
342+
txValidationFn: txpool.ValidateTransaction,
338343
}
339344
}
340345

@@ -1090,7 +1095,8 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error {
10901095
MaxSize: txMaxSize,
10911096
MinTip: p.gasTip.ToBig(),
10921097
}
1093-
if err := txpool.ValidateTransaction(tx, p.head, p.signer, baseOpts); err != nil {
1098+
1099+
if err := p.txValidationFn(tx, p.head, p.signer, baseOpts); err != nil {
10941100
return err
10951101
}
10961102
// Ensure the transaction adheres to the stateful pool filters (nonce, balance)

core/txpool/blobpool/blobpool_test.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,11 +1449,29 @@ func TestAdd(t *testing.T) {
14491449
}
14501450
}
14511451

1452+
// fakeBilly is a billy.Database implementation which just drops data on the floor.
1453+
type fakeBilly struct {
1454+
billy.Database
1455+
count uint64
1456+
}
1457+
1458+
func (f *fakeBilly) Put(data []byte) (uint64, error) {
1459+
f.count++
1460+
return f.count, nil
1461+
}
1462+
1463+
var _ billy.Database = (*fakeBilly)(nil)
1464+
14521465
// Benchmarks the time it takes to assemble the lazy pending transaction list
14531466
// from the pool contents.
14541467
func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_000_000) }
14551468
func BenchmarkPoolPending1GB(b *testing.B) { benchmarkPoolPending(b, 1_000_000_000) }
1456-
func BenchmarkPoolPending10GB(b *testing.B) { benchmarkPoolPending(b, 10_000_000_000) }
1469+
func BenchmarkPoolPending10GB(b *testing.B) {
1470+
if testing.Short() {
1471+
b.Skip("Skipping in short-mode")
1472+
}
1473+
benchmarkPoolPending(b, 10_000_000_000)
1474+
}
14571475

14581476
func benchmarkPoolPending(b *testing.B, datacap uint64) {
14591477
// Calculate the maximum number of transaction that would fit into the pool
@@ -1477,13 +1495,22 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) {
14771495
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
14781496
b.Fatalf("failed to create blob pool: %v", err)
14791497
}
1498+
// Make the pool not use disk (just drop everything). This test never reads
1499+
// back the data, it just iterates over the pool in-memory items
1500+
pool.store = &fakeBilly{pool.store, 0}
1501+
// Avoid validation - verifying all blob proofs take significant time
1502+
// when the capacity is large. The purpose of this bench is to measure assembling
1503+
// the lazies, not the kzg verifications.
1504+
pool.txValidationFn = func(tx *types.Transaction, head *types.Header, signer types.Signer, opts *txpool.ValidationOptions) error {
1505+
return nil // accept all
1506+
}
14801507
// Fill the pool up with one random transaction from each account with the
14811508
// same price and everything to maximize the worst case scenario
14821509
for i := 0; i < int(capacity); i++ {
14831510
blobtx := makeUnsignedTx(0, 10, basefee+10, blobfee)
14841511
blobtx.R = uint256.NewInt(1)
14851512
blobtx.S = uint256.NewInt(uint64(100 + i))
1486-
blobtx.V = uint256.NewInt(0)
1513+
blobtx.V = fuint256.NewInt(0)
14871514
tx := types.NewTx(blobtx)
14881515
addr, err := types.Sender(signer, tx)
14891516
if err != nil {

core/txpool/validation.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ type ValidationOptions struct {
4747
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
4848
}
4949

50+
// ValidationFunction is an method type which the pools use to perform the tx-validations which do not
51+
// require state access. Production code typically uses ValidateTransaction, whereas testing-code
52+
// might choose to instead use something else, e.g. to always fail or avoid heavy cpu usage.
53+
type ValidationFunction func(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error
54+
5055
// ValidateTransaction is a helper method to check whether a transaction is valid
5156
// according to the consensus rules, but does not check state-dependent validation
5257
// (balance, nonce, etc).

0 commit comments

Comments
 (0)