|
@@ -1438,6 +1438,71 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Tests that the pool rejects duplicate transactions.
|
|
|
|
|
+func TestTransactionDeduplication(t *testing.T) {
|
|
|
|
|
+ t.Parallel()
|
|
|
|
|
+
|
|
|
|
|
+ // Create the pool to test the pricing enforcement with
|
|
|
|
|
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
|
|
|
|
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
|
|
|
|
+
|
|
|
|
|
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
|
|
|
|
+ defer pool.Stop()
|
|
|
|
|
+
|
|
|
|
|
+ // Create a test account to add transactions with
|
|
|
|
|
+ key, _ := crypto.GenerateKey()
|
|
|
|
|
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
|
|
|
|
|
+
|
|
|
|
|
+ // Create a batch of transactions and add a few of them
|
|
|
|
|
+ txs := make([]*types.Transaction, 16)
|
|
|
|
|
+ for i := 0; i < len(txs); i++ {
|
|
|
|
|
+ txs[i] = pricedTransaction(uint64(i), 100000, big.NewInt(1), key)
|
|
|
|
|
+ }
|
|
|
|
|
+ var firsts []*types.Transaction
|
|
|
|
|
+ for i := 0; i < len(txs); i += 2 {
|
|
|
|
|
+ firsts = append(firsts, txs[i])
|
|
|
|
|
+ }
|
|
|
|
|
+ errs := pool.AddRemotesSync(firsts)
|
|
|
|
|
+ if len(errs) != len(firsts) {
|
|
|
|
|
+ t.Fatalf("first add mismatching result count: have %d, want %d", len(errs), len(firsts))
|
|
|
|
|
+ }
|
|
|
|
|
+ for i, err := range errs {
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Errorf("add %d failed: %v", i, err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ pending, queued := pool.Stats()
|
|
|
|
|
+ if pending != 1 {
|
|
|
|
|
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 1)
|
|
|
|
|
+ }
|
|
|
|
|
+ if queued != len(txs)/2-1 {
|
|
|
|
|
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, len(txs)/2-1)
|
|
|
|
|
+ }
|
|
|
|
|
+ // Try to add all of them now and ensure previous ones error out as knowns
|
|
|
|
|
+ errs = pool.AddRemotesSync(txs)
|
|
|
|
|
+ if len(errs) != len(txs) {
|
|
|
|
|
+ t.Fatalf("all add mismatching result count: have %d, want %d", len(errs), len(txs))
|
|
|
|
|
+ }
|
|
|
|
|
+ for i, err := range errs {
|
|
|
|
|
+ if i%2 == 0 && err == nil {
|
|
|
|
|
+ t.Errorf("add %d succeeded, should have failed as known", i)
|
|
|
|
|
+ }
|
|
|
|
|
+ if i%2 == 1 && err != nil {
|
|
|
|
|
+ t.Errorf("add %d failed: %v", i, err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ pending, queued = pool.Stats()
|
|
|
|
|
+ if pending != len(txs) {
|
|
|
|
|
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, len(txs))
|
|
|
|
|
+ }
|
|
|
|
|
+ if queued != 0 {
|
|
|
|
|
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := validateTxPoolInternals(pool); err != nil {
|
|
|
|
|
+ t.Fatalf("pool internal state corrupted: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// Tests that the pool rejects replacement transactions that don't meet the minimum
|
|
// Tests that the pool rejects replacement transactions that don't meet the minimum
|
|
|
// price bump required.
|
|
// price bump required.
|
|
|
func TestTransactionReplacement(t *testing.T) {
|
|
func TestTransactionReplacement(t *testing.T) {
|