| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // 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. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | package core | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"crypto/ecdsa" | 
					
						
							| 
									
										
										
										
											2015-04-04 21:41:24 +02:00
										 |  |  | 	"math/big" | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-18 13:38:47 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2015-04-04 21:41:24 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/state" | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							| 
									
										
										
										
											2015-01-07 13:17:48 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/ethdb" | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/event" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction { | 
					
						
							|  |  |  | 	tx, _ := types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil).SignECDSA(key) | 
					
						
							|  |  |  | 	return tx | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-08 20:47:32 +02:00
										 |  |  | func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { | 
					
						
							|  |  |  | 	db, _ := ethdb.NewMemDatabase() | 
					
						
							|  |  |  | 	statedb := state.New(common.Hash{}, db) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | 	var m event.TypeMux | 
					
						
							|  |  |  | 	key, _ := crypto.GenerateKey() | 
					
						
							| 
									
										
										
										
											2015-04-24 17:45:51 +02:00
										 |  |  | 	return NewTxPool(&m, func() *state.StateDB { return statedb }, func() *big.Int { return big.NewInt(1000000) }), key | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-08 20:47:32 +02:00
										 |  |  | func TestInvalidTransactions(t *testing.T) { | 
					
						
							|  |  |  | 	pool, key := setupTxPool() | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	tx := transaction(0, big.NewInt(100), key) | 
					
						
							|  |  |  | 	if err := pool.Add(tx); err != ErrNonExistentAccount { | 
					
						
							| 
									
										
										
										
											2015-04-08 20:47:32 +02:00
										 |  |  | 		t.Error("expected", ErrNonExistentAccount) | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-08 20:47:32 +02:00
										 |  |  | 	from, _ := tx.From() | 
					
						
							| 
									
										
										
										
											2015-06-09 18:14:46 +02:00
										 |  |  | 	pool.currentState().AddBalance(from, big.NewInt(1)) | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	if err := pool.Add(tx); err != ErrInsufficientFunds { | 
					
						
							| 
									
										
										
										
											2015-04-08 20:47:32 +02:00
										 |  |  | 		t.Error("expected", ErrInsufficientFunds) | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 11:19:40 +02:00
										 |  |  | 	balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice())) | 
					
						
							| 
									
										
										
										
											2015-06-09 18:14:46 +02:00
										 |  |  | 	pool.currentState().AddBalance(from, balance) | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	if err := pool.Add(tx); err != ErrIntrinsicGas { | 
					
						
							| 
									
										
										
										
											2015-04-26 11:19:40 +02:00
										 |  |  | 		t.Error("expected", ErrIntrinsicGas, "got", err) | 
					
						
							| 
									
										
										
										
											2015-01-02 12:09:38 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-02 12:18:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-09 18:14:46 +02:00
										 |  |  | 	pool.currentState().SetNonce(from, 1) | 
					
						
							|  |  |  | 	pool.currentState().AddBalance(from, big.NewInt(0xffffffffffffff)) | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	tx = transaction(0, big.NewInt(100000), key) | 
					
						
							|  |  |  | 	if err := pool.Add(tx); err != ErrNonce { | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 		t.Error("expected", ErrNonce) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestTransactionQueue(t *testing.T) { | 
					
						
							|  |  |  | 	pool, key := setupTxPool() | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	tx := transaction(0, big.NewInt(100), key) | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 	from, _ := tx.From() | 
					
						
							| 
									
										
										
										
											2015-06-09 18:14:46 +02:00
										 |  |  | 	pool.currentState().AddBalance(from, big.NewInt(1)) | 
					
						
							| 
									
										
										
										
											2015-06-03 14:06:20 +02:00
										 |  |  | 	pool.queueTx(tx.Hash(), tx) | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pool.checkQueue() | 
					
						
							| 
									
										
										
										
											2015-06-04 12:47:46 +02:00
										 |  |  | 	if len(pool.pending) != 1 { | 
					
						
							|  |  |  | 		t.Error("expected valid txs to be 1 is", len(pool.pending)) | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	tx = transaction(1, big.NewInt(100), key) | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 	from, _ = tx.From() | 
					
						
							| 
									
										
										
										
											2015-06-09 18:14:46 +02:00
										 |  |  | 	pool.currentState().SetNonce(from, 2) | 
					
						
							| 
									
										
										
										
											2015-06-03 14:06:20 +02:00
										 |  |  | 	pool.queueTx(tx.Hash(), tx) | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 	pool.checkQueue() | 
					
						
							| 
									
										
										
										
											2015-06-04 12:47:46 +02:00
										 |  |  | 	if _, ok := pool.pending[tx.Hash()]; ok { | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 		t.Error("expected transaction to be in tx pool") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-03 22:22:20 +02:00
										 |  |  | 	if len(pool.queue[from]) > 0 { | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 		t.Error("expected transaction queue to be empty. is", len(pool.queue[from])) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pool, key = setupTxPool() | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	tx1 := transaction(0, big.NewInt(100), key) | 
					
						
							|  |  |  | 	tx2 := transaction(10, big.NewInt(100), key) | 
					
						
							|  |  |  | 	tx3 := transaction(11, big.NewInt(100), key) | 
					
						
							| 
									
										
										
										
											2015-06-03 14:06:20 +02:00
										 |  |  | 	pool.queueTx(tx1.Hash(), tx1) | 
					
						
							|  |  |  | 	pool.queueTx(tx2.Hash(), tx2) | 
					
						
							|  |  |  | 	pool.queueTx(tx3.Hash(), tx3) | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 	from, _ = tx1.From() | 
					
						
							| 
									
										
										
										
											2015-06-03 14:06:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 	pool.checkQueue() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-04 12:47:46 +02:00
										 |  |  | 	if len(pool.pending) != 1 { | 
					
						
							| 
									
										
										
										
											2015-04-21 22:01:04 +02:00
										 |  |  | 		t.Error("expected tx pool to be 1 =") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-03 14:06:20 +02:00
										 |  |  | 	if len(pool.queue[from]) != 2 { | 
					
						
							|  |  |  | 		t.Error("expected len(queue) == 2, got", len(pool.queue[from])) | 
					
						
							| 
									
										
										
										
											2015-01-31 17:22:17 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-04-30 00:20:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestRemoveTx(t *testing.T) { | 
					
						
							|  |  |  | 	pool, key := setupTxPool() | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	tx := transaction(0, big.NewInt(100), key) | 
					
						
							| 
									
										
										
										
											2015-04-30 00:20:59 +02:00
										 |  |  | 	from, _ := tx.From() | 
					
						
							| 
									
										
										
										
											2015-06-09 18:14:46 +02:00
										 |  |  | 	pool.currentState().AddBalance(from, big.NewInt(1)) | 
					
						
							| 
									
										
										
										
											2015-06-03 14:06:20 +02:00
										 |  |  | 	pool.queueTx(tx.Hash(), tx) | 
					
						
							| 
									
										
										
										
											2015-06-03 22:53:33 +02:00
										 |  |  | 	pool.addTx(tx.Hash(), from, tx) | 
					
						
							| 
									
										
										
										
											2015-04-30 00:20:59 +02:00
										 |  |  | 	if len(pool.queue) != 1 { | 
					
						
							|  |  |  | 		t.Error("expected queue to be 1, got", len(pool.queue)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-04 12:47:46 +02:00
										 |  |  | 	if len(pool.pending) != 1 { | 
					
						
							|  |  |  | 		t.Error("expected txs to be 1, got", len(pool.pending)) | 
					
						
							| 
									
										
										
										
											2015-04-30 00:20:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-10 11:35:15 +02:00
										 |  |  | 	pool.RemoveTx(tx.Hash()) | 
					
						
							| 
									
										
										
										
											2015-04-30 00:20:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if len(pool.queue) > 0 { | 
					
						
							|  |  |  | 		t.Error("expected queue to be 0, got", len(pool.queue)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-04 12:47:46 +02:00
										 |  |  | 	if len(pool.pending) > 0 { | 
					
						
							|  |  |  | 		t.Error("expected txs to be 0, got", len(pool.pending)) | 
					
						
							| 
									
										
										
										
											2015-04-30 00:20:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-05-26 19:50:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestNegativeValue(t *testing.T) { | 
					
						
							|  |  |  | 	pool, key := setupTxPool() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	tx, _ := types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil).SignECDSA(key) | 
					
						
							| 
									
										
										
										
											2015-05-26 19:50:42 +02:00
										 |  |  | 	from, _ := tx.From() | 
					
						
							| 
									
										
										
										
											2015-06-09 18:14:46 +02:00
										 |  |  | 	pool.currentState().AddBalance(from, big.NewInt(1)) | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	if err := pool.Add(tx); err != ErrNegativeValue { | 
					
						
							| 
									
										
										
										
											2015-05-26 19:50:42 +02:00
										 |  |  | 		t.Error("expected", ErrNegativeValue, "got", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-06-04 17:28:09 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestTransactionChainFork(t *testing.T) { | 
					
						
							|  |  |  | 	pool, key := setupTxPool() | 
					
						
							|  |  |  | 	addr := crypto.PubkeyToAddress(key.PublicKey) | 
					
						
							| 
									
										
										
										
											2015-06-09 18:14:46 +02:00
										 |  |  | 	resetState := func() { | 
					
						
							|  |  |  | 		db, _ := ethdb.NewMemDatabase() | 
					
						
							|  |  |  | 		statedb := state.New(common.Hash{}, db) | 
					
						
							|  |  |  | 		pool.currentState = func() *state.StateDB { return statedb } | 
					
						
							|  |  |  | 		pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) | 
					
						
							|  |  |  | 		pool.resetState() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	resetState() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	tx := transaction(0, big.NewInt(100000), key) | 
					
						
							|  |  |  | 	if err := pool.add(tx); err != nil { | 
					
						
							| 
									
										
										
										
											2015-06-04 17:28:09 +02:00
										 |  |  | 		t.Error("didn't expect error", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pool.RemoveTransactions([]*types.Transaction{tx}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// reset the pool's internal state | 
					
						
							| 
									
										
										
										
											2015-06-09 18:14:46 +02:00
										 |  |  | 	resetState() | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	if err := pool.add(tx); err != nil { | 
					
						
							| 
									
										
										
										
											2015-06-04 17:28:09 +02:00
										 |  |  | 		t.Error("didn't expect error", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestTransactionDoubleNonce(t *testing.T) { | 
					
						
							|  |  |  | 	pool, key := setupTxPool() | 
					
						
							|  |  |  | 	addr := crypto.PubkeyToAddress(key.PublicKey) | 
					
						
							| 
									
										
										
										
											2015-06-09 18:14:46 +02:00
										 |  |  | 	resetState := func() { | 
					
						
							|  |  |  | 		db, _ := ethdb.NewMemDatabase() | 
					
						
							|  |  |  | 		statedb := state.New(common.Hash{}, db) | 
					
						
							|  |  |  | 		pool.currentState = func() *state.StateDB { return statedb } | 
					
						
							|  |  |  | 		pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) | 
					
						
							|  |  |  | 		pool.resetState() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	resetState() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	tx := transaction(0, big.NewInt(100000), key) | 
					
						
							|  |  |  | 	tx2 := transaction(0, big.NewInt(1000000), key) | 
					
						
							|  |  |  | 	if err := pool.add(tx); err != nil { | 
					
						
							| 
									
										
										
										
											2015-06-04 17:28:09 +02:00
										 |  |  | 		t.Error("didn't expect error", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	if err := pool.add(tx2); err != nil { | 
					
						
							| 
									
										
										
										
											2015-06-04 17:28:09 +02:00
										 |  |  | 		t.Error("didn't expect error", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-30 11:04:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pool.checkQueue() | 
					
						
							| 
									
										
										
										
											2015-06-04 17:28:09 +02:00
										 |  |  | 	if len(pool.pending) != 2 { | 
					
						
							|  |  |  | 		t.Error("expected 2 pending txs. Got", len(pool.pending)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-06-09 00:41:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestMissingNonce(t *testing.T) { | 
					
						
							|  |  |  | 	pool, key := setupTxPool() | 
					
						
							|  |  |  | 	addr := crypto.PubkeyToAddress(key.PublicKey) | 
					
						
							|  |  |  | 	pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) | 
					
						
							| 
									
										
										
										
											2015-06-11 14:05:32 +02:00
										 |  |  | 	tx := transaction(1, big.NewInt(100000), key) | 
					
						
							|  |  |  | 	if err := pool.add(tx); err != nil { | 
					
						
							| 
									
										
										
										
											2015-06-09 00:41:47 +02:00
										 |  |  | 		t.Error("didn't expect error", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(pool.pending) != 0 { | 
					
						
							|  |  |  | 		t.Error("expected 0 pending transactions, got", len(pool.pending)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(pool.queue[addr]) != 1 { | 
					
						
							|  |  |  | 		t.Error("expected 1 queued transaction, got", len(pool.queue[addr])) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |