Merge pull request #20085 from karalabe/txpool-api-fix
core: fix tx dedup return error count
This commit is contained in:
commit
7b32d2a470
|
@ -766,21 +766,40 @@ func (pool *TxPool) AddRemote(tx *types.Transaction) error {
|
||||||
// addTxs attempts to queue a batch of transactions if they are valid.
|
// addTxs attempts to queue a batch of transactions if they are valid.
|
||||||
func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error {
|
func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error {
|
||||||
// Filter out known ones without obtaining the pool lock or recovering signatures
|
// Filter out known ones without obtaining the pool lock or recovering signatures
|
||||||
for i := 0; i < len(txs); i++ {
|
var (
|
||||||
if pool.all.Get(txs[i].Hash()) != nil {
|
errs = make([]error, len(txs))
|
||||||
|
news = make([]*types.Transaction, 0, len(txs))
|
||||||
|
)
|
||||||
|
for i, tx := range txs {
|
||||||
|
// If the transaction is known, pre-set the error slot
|
||||||
|
if pool.all.Get(tx.Hash()) != nil {
|
||||||
|
errs[i] = fmt.Errorf("known transaction: %x", tx.Hash())
|
||||||
knownTxMeter.Mark(1)
|
knownTxMeter.Mark(1)
|
||||||
txs = append(txs[:i], txs[i+1:]...)
|
continue
|
||||||
i--
|
|
||||||
}
|
}
|
||||||
|
// Accumulate all unknown transactions for deeper processing
|
||||||
|
news = append(news, tx)
|
||||||
|
}
|
||||||
|
if len(news) == 0 {
|
||||||
|
return errs
|
||||||
}
|
}
|
||||||
// Cache senders in transactions before obtaining lock (pool.signer is immutable)
|
// Cache senders in transactions before obtaining lock (pool.signer is immutable)
|
||||||
for _, tx := range txs {
|
for _, tx := range news {
|
||||||
types.Sender(pool.signer, tx)
|
types.Sender(pool.signer, tx)
|
||||||
}
|
}
|
||||||
|
// Process all the new transaction and merge any errors into the original slice
|
||||||
pool.mu.Lock()
|
pool.mu.Lock()
|
||||||
errs, dirtyAddrs := pool.addTxsLocked(txs, local)
|
newErrs, dirtyAddrs := pool.addTxsLocked(news, local)
|
||||||
pool.mu.Unlock()
|
pool.mu.Unlock()
|
||||||
|
|
||||||
|
var nilSlot = 0
|
||||||
|
for _, err := range newErrs {
|
||||||
|
for errs[nilSlot] != nil {
|
||||||
|
nilSlot++
|
||||||
|
}
|
||||||
|
errs[nilSlot] = err
|
||||||
|
}
|
||||||
|
// Reorg the pool internals if needed and return
|
||||||
done := pool.requestPromoteExecutables(dirtyAddrs)
|
done := pool.requestPromoteExecutables(dirtyAddrs)
|
||||||
if sync {
|
if sync {
|
||||||
<-done
|
<-done
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue