Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 230df98

Browse files
core/txpool: disallow future churn by remote txs (ethereum#26907)
Prior to this change, it was possible that transactions are erroneously deemed as 'future' although they are in fact 'pending', causing them to be dropped due to 'future' not being allowed to replace 'pending'. This change fixes that, by doing a more in-depth inspection of the queue.
1 parent 2adce0b commit 230df98

File tree

3 files changed

+51
-19
lines changed

3 files changed

+51
-19
lines changed

core/txpool/list.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,10 @@ func newList(strict bool) *list {
270270
}
271271
}
272272

273-
// Overlaps returns whether the transaction specified has the same nonce as one
274-
// already contained within the list.
275-
func (l *list) Overlaps(tx *types.Transaction) bool {
276-
return l.txs.Get(tx.Nonce()) != nil
273+
// Contains returns whether the list contains a transaction
274+
// with the provided nonce.
275+
func (l *list) Contains(nonce uint64) bool {
276+
return l.txs.Get(nonce) != nil
277277
}
278278

279279
// Add tries to insert a new transaction into the list, returning whether the

core/txpool/txpool.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -745,11 +745,11 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
745745
}
746746

747747
// If the new transaction is a future transaction it should never churn pending transactions
748-
if !isLocal && pool.isFuture(from, tx) {
748+
if !isLocal && pool.isGapped(from, tx) {
749749
var replacesPending bool
750750
for _, dropTx := range drop {
751751
dropSender, _ := types.Sender(pool.signer, dropTx)
752-
if list := pool.pending[dropSender]; list != nil && list.Overlaps(dropTx) {
752+
if list := pool.pending[dropSender]; list != nil && list.Contains(dropTx.Nonce()) {
753753
replacesPending = true
754754
break
755755
}
@@ -774,7 +774,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
774774
}
775775

776776
// Try to replace an existing transaction in the pending pool
777-
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
777+
if list := pool.pending[from]; list != nil && list.Contains(tx.Nonce()) {
778778
// Nonce already pending, check if required price bump is met
779779
inserted, old := list.Add(tx, pool.config.PriceBump)
780780
if !inserted {
@@ -817,18 +817,26 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
817817
return replaced, nil
818818
}
819819

820-
// isFuture reports whether the given transaction is immediately executable.
821-
func (pool *TxPool) isFuture(from common.Address, tx *types.Transaction) bool {
822-
list := pool.pending[from]
823-
if list == nil {
824-
return pool.pendingNonces.get(from) != tx.Nonce()
820+
// isGapped reports whether the given transaction is immediately executable.
821+
func (pool *TxPool) isGapped(from common.Address, tx *types.Transaction) bool {
822+
// Short circuit if transaction matches pending nonce and can be promoted
823+
// to pending list as an executable transaction.
824+
next := pool.pendingNonces.get(from)
825+
if tx.Nonce() == next {
826+
return false
825827
}
826-
// Sender has pending transactions.
827-
if old := list.txs.Get(tx.Nonce()); old != nil {
828-
return false // It replaces a pending transaction.
828+
// The transaction has a nonce gap with pending list, it's only considered
829+
// as executable if transactions in queue can fill up the nonce gap.
830+
queue, ok := pool.queue[from]
831+
if !ok {
832+
return true
829833
}
830-
// Not replacing, check if parent nonce exists in pending.
831-
return list.txs.Get(tx.Nonce()-1) == nil
834+
for nonce := next; nonce < tx.Nonce(); nonce++ {
835+
if !queue.Contains(nonce) {
836+
return true // txs in queue can't fill up the nonce gap
837+
}
838+
}
839+
return false
832840
}
833841

834842
// enqueueTx inserts a new transaction into the non-executable transaction queue.

core/txpool/txpool2_test.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func count(t *testing.T, pool *TxPool) (pending int, queued int) {
4242
return pending, queued
4343
}
4444

45-
func fillPool(t *testing.T, pool *TxPool) {
45+
func fillPool(t testing.TB, pool *TxPool) {
4646
t.Helper()
4747
// Create a number of test accounts, fund them and make transactions
4848
executableTxs := types.Transactions{}
@@ -189,7 +189,7 @@ func TestTransactionZAttack(t *testing.T) {
189189
key, _ := crypto.GenerateKey()
190190
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000))
191191
for j := 0; j < int(pool.config.GlobalSlots); j++ {
192-
overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 60000000000, 21000, big.NewInt(500), key))
192+
overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 600000000000, 21000, big.NewInt(500), key))
193193
}
194194
}
195195
pool.AddRemotesSync(overDraftTxs)
@@ -210,3 +210,27 @@ func TestTransactionZAttack(t *testing.T) {
210210
newIvPending, ivPending, pool.config.GlobalSlots, newQueued)
211211
}
212212
}
213+
214+
func BenchmarkFutureAttack(b *testing.B) {
215+
// Create the pool to test the limit enforcement with
216+
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
217+
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
218+
config := testTxPoolConfig
219+
config.GlobalQueue = 100
220+
config.GlobalSlots = 100
221+
pool := NewTxPool(config, eip1559Config, blockchain)
222+
defer pool.Stop()
223+
fillPool(b, pool)
224+
225+
key, _ := crypto.GenerateKey()
226+
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000))
227+
futureTxs := types.Transactions{}
228+
229+
for n := 0; n < b.N; n++ {
230+
futureTxs = append(futureTxs, pricedTransaction(1000+uint64(n), 100000, big.NewInt(500), key))
231+
}
232+
b.ResetTimer()
233+
for i := 0; i < 5; i++ {
234+
pool.AddRemotesSync(futureTxs)
235+
}
236+
}

0 commit comments

Comments
 (0)