Merge pull request #16240 from cuiweixie/txpool

core: should enqueue the invalids tx anyway
This commit is contained in:
Péter Szilágyi 2018-03-07 11:45:28 +02:00 committed by GitHub
commit d43ffdbf6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 115 additions and 55 deletions

View File

@ -877,15 +877,14 @@ func (pool *TxPool) removeTx(hash common.Hash) {
// Remove the transaction from the pending lists and reset the account nonce // Remove the transaction from the pending lists and reset the account nonce
if pending := pool.pending[addr]; pending != nil { if pending := pool.pending[addr]; pending != nil {
if removed, invalids := pending.Remove(tx); removed { if removed, invalids := pending.Remove(tx); removed {
// If no more transactions are left, remove the list // If no more pending transactions are left, remove the list
if pending.Empty() { if pending.Empty() {
delete(pool.pending, addr) delete(pool.pending, addr)
delete(pool.beats, addr) delete(pool.beats, addr)
} else { }
// Otherwise postpone any invalidated transactions // Postpone any invalidated transactions
for _, tx := range invalids { for _, tx := range invalids {
pool.enqueueTx(tx.Hash(), tx) pool.enqueueTx(tx.Hash(), tx)
}
} }
// Update the account nonce if needed // Update the account nonce if needed
if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce { if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {

View File

@ -557,74 +557,112 @@ func TestTransactionDropping(t *testing.T) {
func TestTransactionPostponing(t *testing.T) { func TestTransactionPostponing(t *testing.T) {
t.Parallel() t.Parallel()
// Create a test account and fund it // Create the pool to test the postponing with
pool, key := setupTxPool() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop() defer pool.Stop()
account, _ := deriveSender(transaction(0, 0, key)) // Create two test accounts to produce different gap profiles with
pool.currentState.AddBalance(account, big.NewInt(1000)) keys := make([]*ecdsa.PrivateKey, 2)
accs := make([]common.Address, len(keys))
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
accs[i] = crypto.PubkeyToAddress(keys[i].PublicKey)
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(50100))
}
// Add a batch consecutive pending transactions for validation // Add a batch consecutive pending transactions for validation
txns := []*types.Transaction{} txs := []*types.Transaction{}
for i := 0; i < 100; i++ { for i, key := range keys {
var tx *types.Transaction
if i%2 == 0 { for j := 0; j < 100; j++ {
tx = transaction(uint64(i), 100, key) var tx *types.Transaction
} else { if (i+j)%2 == 0 {
tx = transaction(uint64(i), 500, key) tx = transaction(uint64(j), 25000, key)
} else {
tx = transaction(uint64(j), 50000, key)
}
txs = append(txs, tx)
}
}
for i, err := range pool.AddRemotes(txs) {
if err != nil {
t.Fatalf("tx %d: failed to add transactions: %v", i, err)
} }
pool.promoteTx(account, tx.Hash(), tx)
txns = append(txns, tx)
} }
// Check that pre and post validations leave the pool as is // Check that pre and post validations leave the pool as is
if pool.pending[account].Len() != len(txns) { if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) {
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), len(txns)) t.Errorf("pending transaction mismatch: have %d, want %d", pending, len(txs))
} }
if len(pool.queue) != 0 { if len(pool.queue) != 0 {
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 0) t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
} }
if len(pool.all) != len(txns) { if len(pool.all) != len(txs) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)) t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs))
} }
pool.lockedReset(nil, nil) pool.lockedReset(nil, nil)
if pool.pending[account].Len() != len(txns) { if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) {
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), len(txns)) t.Errorf("pending transaction mismatch: have %d, want %d", pending, len(txs))
} }
if len(pool.queue) != 0 { if len(pool.queue) != 0 {
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 0) t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
} }
if len(pool.all) != len(txns) { if len(pool.all) != len(txs) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)) t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs))
} }
// Reduce the balance of the account, and check that transactions are reorganised // Reduce the balance of the account, and check that transactions are reorganised
pool.currentState.AddBalance(account, big.NewInt(-750)) for _, addr := range accs {
pool.currentState.AddBalance(addr, big.NewInt(-1))
}
pool.lockedReset(nil, nil) pool.lockedReset(nil, nil)
if _, ok := pool.pending[account].txs.items[txns[0].Nonce()]; !ok { // The first account's first transaction remains valid, check that subsequent
t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txns[0]) // ones are either filtered out, or queued up for later.
if _, ok := pool.pending[accs[0]].txs.items[txs[0].Nonce()]; !ok {
t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txs[0])
} }
if _, ok := pool.queue[account].txs.items[txns[0].Nonce()]; ok { if _, ok := pool.queue[accs[0]].txs.items[txs[0].Nonce()]; ok {
t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txns[0]) t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txs[0])
} }
for i, tx := range txns[1:] { for i, tx := range txs[1:100] {
if i%2 == 1 { if i%2 == 1 {
if _, ok := pool.pending[account].txs.items[tx.Nonce()]; ok { if _, ok := pool.pending[accs[0]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: valid but future transaction present in pending pool: %v", i+1, tx) t.Errorf("tx %d: valid but future transaction present in pending pool: %v", i+1, tx)
} }
if _, ok := pool.queue[account].txs.items[tx.Nonce()]; !ok { if _, ok := pool.queue[accs[0]].txs.items[tx.Nonce()]; !ok {
t.Errorf("tx %d: valid but future transaction missing from future queue: %v", i+1, tx) t.Errorf("tx %d: valid but future transaction missing from future queue: %v", i+1, tx)
} }
} else { } else {
if _, ok := pool.pending[account].txs.items[tx.Nonce()]; ok { if _, ok := pool.pending[accs[0]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in pending pool: %v", i+1, tx) t.Errorf("tx %d: out-of-fund transaction present in pending pool: %v", i+1, tx)
} }
if _, ok := pool.queue[account].txs.items[tx.Nonce()]; ok { if _, ok := pool.queue[accs[0]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx) t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx)
} }
} }
} }
if len(pool.all) != len(txns)/2 { // The second account's first transaction got invalid, check that all transactions
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)/2) // are either filtered out, or queued up for later.
if pool.pending[accs[1]] != nil {
t.Errorf("invalidated account still has pending transactions")
}
for i, tx := range txs[100:] {
if i%2 == 1 {
if _, ok := pool.queue[accs[1]].txs.items[tx.Nonce()]; !ok {
t.Errorf("tx %d: valid but future transaction missing from future queue: %v", 100+i, tx)
}
} else {
if _, ok := pool.queue[accs[1]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", 100+i, tx)
}
}
}
if len(pool.all) != len(txs)/2 {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs)/2)
} }
} }
@ -949,11 +987,11 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
account2, _ := deriveSender(transaction(0, 0, key2)) account2, _ := deriveSender(transaction(0, 0, key2))
pool2.currentState.AddBalance(account2, big.NewInt(1000000)) pool2.currentState.AddBalance(account2, big.NewInt(1000000))
txns := []*types.Transaction{} txs := []*types.Transaction{}
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ { for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
txns = append(txns, transaction(origin+i, 100000, key2)) txs = append(txs, transaction(origin+i, 100000, key2))
} }
pool2.AddRemotes(txns) pool2.AddRemotes(txs)
// Ensure the batch optimization honors the same pool mechanics // Ensure the batch optimization honors the same pool mechanics
if len(pool1.pending) != len(pool2.pending) { if len(pool1.pending) != len(pool2.pending) {
@ -1124,7 +1162,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
defer sub.Unsubscribe() defer sub.Unsubscribe()
// Create a number of test accounts and fund them // Create a number of test accounts and fund them
keys := make([]*ecdsa.PrivateKey, 3) keys := make([]*ecdsa.PrivateKey, 4)
for i := 0; i < len(keys); i++ { for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey() keys[i], _ = crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
@ -1136,24 +1174,28 @@ func TestTransactionPoolRepricing(t *testing.T) {
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0])) 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, pricedTransaction(2, 100000, big.NewInt(2), keys[0]))
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[1]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[1])) txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[1]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[1])) txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[1]))
txs = append(txs, pricedTransaction(3, 100000, big.NewInt(2), keys[1]))
ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[2]) txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[2]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[2]))
txs = append(txs, pricedTransaction(3, 100000, big.NewInt(2), keys[2]))
ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[3])
// Import the batch and that both pending and queued transactions match up // Import the batch and that both pending and queued transactions match up
pool.AddRemotes(txs) pool.AddRemotes(txs)
pool.AddLocal(ltx) pool.AddLocal(ltx)
pending, queued := pool.Stats() pending, queued := pool.Stats()
if pending != 4 { if pending != 7 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 4) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 7)
} }
if queued != 3 { if queued != 3 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3) t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
} }
if err := validateEvents(events, 4); err != nil { if err := validateEvents(events, 7); err != nil {
t.Fatalf("original event firing failed: %v", err) t.Fatalf("original event firing failed: %v", err)
} }
if err := validateTxPoolInternals(pool); err != nil { if err := validateTxPoolInternals(pool); err != nil {
@ -1166,8 +1208,8 @@ func TestTransactionPoolRepricing(t *testing.T) {
if pending != 2 { if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
} }
if queued != 3 { if queued != 5 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3) t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 5)
} }
if err := validateEvents(events, 0); err != nil { if err := validateEvents(events, 0); err != nil {
t.Fatalf("reprice event firing failed: %v", err) t.Fatalf("reprice event firing failed: %v", err)
@ -1179,7 +1221,10 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), keys[0])); err != ErrUnderpriced { if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), keys[0])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced) t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
} }
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced { if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), keys[2])); err != ErrUnderpriced {
t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced) t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
} }
if err := validateEvents(events, 0); err != nil { if err := validateEvents(events, 0); err != nil {
@ -1189,7 +1234,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err) t.Fatalf("pool internal state corrupted: %v", err)
} }
// However we can add local underpriced transactions // However we can add local underpriced transactions
tx := pricedTransaction(1, 100000, big.NewInt(1), keys[2]) tx := pricedTransaction(1, 100000, big.NewInt(1), keys[3])
if err := pool.AddLocal(tx); err != nil { if err := pool.AddLocal(tx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err) t.Fatalf("failed to add underpriced local transaction: %v", err)
} }
@ -1202,6 +1247,22 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateTxPoolInternals(pool); err != nil { if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err) t.Fatalf("pool internal state corrupted: %v", err)
} }
// And we can fill gaps with properly priced transactions
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(2), keys[0])); err != nil {
t.Fatalf("failed to add pending transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(2), keys[1])); err != nil {
t.Fatalf("failed to add pending transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(2), keys[2])); err != nil {
t.Fatalf("failed to add queued transaction: %v", err)
}
if err := validateEvents(events, 5); err != nil {
t.Fatalf("post-reprice event firing failed: %v", err)
}
if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
} }
// Tests that setting the transaction pool gas price to a higher value does not // Tests that setting the transaction pool gas price to a higher value does not