From b0b3cf2eeb74e45b373d4708375e899688c7f9c8 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 22 Jun 2017 10:14:31 +0200 Subject: [PATCH] core: add testcase for txpool --- core/tx_pool.go | 13 +++++++++ core/tx_pool_test.go | 66 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/core/tx_pool.go b/core/tx_pool.go index 04ffa8a98..86a2ed232 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -301,6 +301,19 @@ func (pool *TxPool) Stats() (int, int) { return pool.stats() } +// validateInternals checks if the content in pool.all +// is consistent with the numbers reported in pending and queued +func (pool *TxPool) validateInternals() error { + pool.mu.RLock() + defer pool.mu.RUnlock() + p, q := pool.stats() + a := len(pool.all) + if a != p+q { + return fmt.Errorf("Pool.all size %d != %d pending + %d queued", a, p, q) + } + return nil +} + // stats retrieves the current pool stats, namely the number of pending and the // number of queued (non-executable) transactions. func (pool *TxPool) stats() (int, int) { diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 94b07170d..d82a8ef5a 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -719,6 +719,12 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) { txns = append(txns, transaction(origin+i, big.NewInt(100000), key2)) } pool2.AddBatch(txns) + if err := pool2.validateInternals(); err != nil { + t.Error(err) + } + if err := pool1.validateInternals(); err != nil { + t.Error(err) + } // Ensure the batch optimization honors the same pool mechanics if len(pool1.pending) != len(pool2.pending) { @@ -769,6 +775,9 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) { // Import the batch and verify that limits have been enforced pool.AddBatch(txs) + if err := pool.validateInternals(); err != nil { + t.Error(err) + } pending := 0 for _, list := range pool.pending { pending += list.Len() @@ -778,6 +787,42 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) { } } +// Tests that if transactions start being capped, transasctions are also removed from 'all' +func TestTransactionCapClearsFromAll(t *testing.T) { + // Reduce the queue limits to shorten test time + defer func(old uint64) { DefaultTxPoolConfig.GlobalSlots = old }(DefaultTxPoolConfig.GlobalSlots) + DefaultTxPoolConfig.AccountSlots = 2 + DefaultTxPoolConfig.AccountQueue = 2 + DefaultTxPoolConfig.GlobalSlots = 8 + + // Create the pool to test the limit enforcement with + db, _ := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, db) + + pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + pool.resetState() + + // Create a number of test accounts and fund them + state, _ := pool.currentState() + + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + state.AddBalance(addr, big.NewInt(1000000)) + + txs := types.Transactions{} + nonce := uint64(0) + for j := 0; j < int(DefaultTxPoolConfig.GlobalSlots)*2; j++ { + tx := transaction(nonce, big.NewInt(100000), key) + txs = append(txs, tx) + nonce++ + } + // Import the batch and verify that limits have been enforced + pool.AddBatch(txs) + if err := pool.validateInternals(); err != nil { + t.Error(err) + } +} + // Tests that if the transaction count belonging to multiple accounts go above // some hard threshold, if they are under the minimum guaranteed slot count then // the transactions are still kept. @@ -815,6 +860,10 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { // Import the batch and verify that limits have been enforced pool.AddBatch(txs) + if err := pool.validateInternals(); err != nil { + t.Error(err) + } + for addr, list := range pool.pending { if list.Len() != int(DefaultTxPoolConfig.AccountSlots) { t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), DefaultTxPoolConfig.AccountSlots) @@ -860,6 +909,10 @@ func TestTransactionPoolRepricing(t *testing.T) { // Import the batch and that both pending and queued transactions match up pool.AddBatch(txs) + if err := pool.validateInternals(); err != nil { + t.Error(err) + } + pending, queued := pool.stats() if pending != 4 { t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 4) @@ -894,6 +947,10 @@ func TestTransactionPoolRepricing(t *testing.T) { if pending, _ = pool.stats(); pending != 3 { t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) } + if err := pool.validateInternals(); err != nil { + t.Error(err) + } + } // Tests that when the pool reaches its global transaction limit, underpriced @@ -937,6 +994,9 @@ func TestTransactionPoolUnderpricing(t *testing.T) { // Import the batch and that both pending and queued transactions match up pool.AddBatch(txs) + if err := pool.validateInternals(); err != nil { + t.Error(err) + } pending, queued := pool.stats() if pending != 3 { @@ -980,6 +1040,9 @@ func TestTransactionPoolUnderpricing(t *testing.T) { if queued != 2 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) } + if err := pool.validateInternals(); err != nil { + t.Error(err) + } } // Tests that the pool rejects replacement transactions that don't meet the minimum @@ -1041,6 +1104,9 @@ func TestTransactionReplacement(t *testing.T) { if err := pool.Add(pricedTransaction(2, big.NewInt(100000), big.NewInt(threshold+1), key)); err != nil { t.Fatalf("failed to replace original queued transaction: %v", err) } + if err := pool.validateInternals(); err != nil { + t.Error(err) + } } // Benchmarks the speed of validating the contents of the pending queue of the