@@ -99,7 +99,30 @@ func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
99
99
100
100
// Filter iterates over the list of transactions and removes all of them for which
101
101
// the specified function evaluates to true.
102
+ // Filter, as opposed to 'filter', re-initialises the heap after the operation is done.
103
+ // If you want to do several consecutive filterings, it's therefore better to first
104
+ // do a .filter(func1) followed by .Filter(func2) or reheap()
102
105
func (m * txSortedMap ) Filter (filter func (* types.Transaction ) bool ) types.Transactions {
106
+ removed := m .filter (filter )
107
+ // If transactions were removed, the heap and cache are ruined
108
+ if len (removed ) > 0 {
109
+ m .reheap ()
110
+ }
111
+ return removed
112
+ }
113
+
114
+ func (m * txSortedMap ) reheap () {
115
+ * m .index = make ([]uint64 , 0 , len (m .items ))
116
+ for nonce := range m .items {
117
+ * m .index = append (* m .index , nonce )
118
+ }
119
+ heap .Init (m .index )
120
+ m .cache = nil
121
+ }
122
+
123
+ // filter is identical to Filter, but **does not** regenerate the heap. This method
124
+ // should only be used if followed immediately by a call to Filter or reheap()
125
+ func (m * txSortedMap ) filter (filter func (* types.Transaction ) bool ) types.Transactions {
103
126
var removed types.Transactions
104
127
105
128
// Collect all the transactions to filter out
@@ -109,14 +132,7 @@ func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transac
109
132
delete (m .items , nonce )
110
133
}
111
134
}
112
- // If transactions were removed, the heap and cache are ruined
113
135
if len (removed ) > 0 {
114
- * m .index = make ([]uint64 , 0 , len (m .items ))
115
- for nonce := range m .items {
116
- * m .index = append (* m .index , nonce )
117
- }
118
- heap .Init (m .index )
119
-
120
136
m .cache = nil
121
137
}
122
138
return removed
@@ -197,10 +213,7 @@ func (m *txSortedMap) Len() int {
197
213
return len (m .items )
198
214
}
199
215
200
- // Flatten creates a nonce-sorted slice of transactions based on the loosely
201
- // sorted internal representation. The result of the sorting is cached in case
202
- // it's requested again before any modifications are made to the contents.
203
- func (m * txSortedMap ) Flatten () types.Transactions {
216
+ func (m * txSortedMap ) flatten () types.Transactions {
204
217
// If the sorting was not cached yet, create and cache it
205
218
if m .cache == nil {
206
219
m .cache = make (types.Transactions , 0 , len (m .items ))
@@ -209,12 +222,27 @@ func (m *txSortedMap) Flatten() types.Transactions {
209
222
}
210
223
sort .Sort (types .TxByNonce (m .cache ))
211
224
}
225
+ return m .cache
226
+ }
227
+
228
+ // Flatten creates a nonce-sorted slice of transactions based on the loosely
229
+ // sorted internal representation. The result of the sorting is cached in case
230
+ // it's requested again before any modifications are made to the contents.
231
+ func (m * txSortedMap ) Flatten () types.Transactions {
212
232
// Copy the cache to prevent accidental modifications
213
- txs := make (types.Transactions , len (m .cache ))
214
- copy (txs , m .cache )
233
+ cache := m .flatten ()
234
+ txs := make (types.Transactions , len (cache ))
235
+ copy (txs , cache )
215
236
return txs
216
237
}
217
238
239
+ // LastElement returns the last element of a flattened list, thus, the
240
+ // transaction with the highest nonce
241
+ func (m * txSortedMap ) LastElement () * types.Transaction {
242
+ cache := m .flatten ()
243
+ return cache [len (cache )- 1 ]
244
+ }
245
+
218
246
// txList is a "list" of transactions belonging to an account, sorted by account
219
247
// nonce. The same type can be used both for storing contiguous transactions for
220
248
// the executable/pending queue; and for storing gapped transactions for the non-
@@ -223,17 +251,16 @@ type txList struct {
223
251
strict bool // Whether nonces are strictly continuous or not
224
252
txs * txSortedMap // Heap indexed sorted hash map of the transactions
225
253
226
- costcap * big. Int // Price of the highest costing transaction (reset only if exceeds balance)
227
- gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
254
+ costcap uint64 // Price of the highest costing transaction (reset only if exceeds balance)
255
+ gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
228
256
}
229
257
230
258
// newTxList create a new transaction list for maintaining nonce-indexable fast,
231
259
// gapped, sortable transaction lists.
232
260
func newTxList (strict bool ) * txList {
233
261
return & txList {
234
- strict : strict ,
235
- txs : newTxSortedMap (),
236
- costcap : new (big.Int ),
262
+ strict : strict ,
263
+ txs : newTxSortedMap (),
237
264
}
238
265
}
239
266
@@ -252,17 +279,26 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
252
279
// If there's an older better transaction, abort
253
280
old := l .txs .Get (tx .Nonce ())
254
281
if old != nil {
255
- threshold := new (big.Int ).Div (new (big.Int ).Mul (old .GasPrice (), big .NewInt (100 + int64 (priceBump ))), big .NewInt (100 ))
282
+ // threshold = oldGP * (100 + priceBump) / 100
283
+ a := big .NewInt (100 + int64 (priceBump ))
284
+ a = a .Mul (a , old .GasPrice ())
285
+ b := big .NewInt (100 )
286
+ threshold := a .Div (a , b )
256
287
// Have to ensure that the new gas price is higher than the old gas
257
288
// price as well as checking the percentage threshold to ensure that
258
289
// this is accurate for low (Wei-level) gas price replacements
259
290
if old .GasPriceCmp (tx ) >= 0 || tx .GasPriceIntCmp (threshold ) < 0 {
260
291
return false , nil
261
292
}
262
293
}
294
+ cost , overflow := tx .CostU64 ()
295
+ if overflow {
296
+ log .Warn ("transaction cost overflown, txHash: %v txCost: %v" , tx .Hash (), cost )
297
+ return false , nil
298
+ }
263
299
// Otherwise overwrite the old transaction with the current one
264
300
l .txs .Put (tx )
265
- if cost := tx . Cost (); l .costcap . Cmp ( cost ) < 0 {
301
+ if l .costcap < cost {
266
302
l .costcap = cost
267
303
}
268
304
if gas := tx .Gas (); l .gascap < gas {
@@ -287,29 +323,35 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
287
323
// a point in calculating all the costs or if the balance covers all. If the threshold
288
324
// is lower than the costgas cap, the caps will be reset to a new high after removing
289
325
// the newly invalidated transactions.
290
- func (l * txList ) Filter (costLimit * big. Int , gasLimit uint64 ) (types.Transactions , types.Transactions ) {
326
+ func (l * txList ) Filter (costLimit uint64 , gasLimit uint64 ) (types.Transactions , types.Transactions ) {
291
327
// If all transactions are below the threshold, short circuit
292
- if l .costcap . Cmp ( costLimit ) <= 0 && l .gascap <= gasLimit {
328
+ if l .costcap <= costLimit && l .gascap <= gasLimit {
293
329
return nil , nil
294
330
}
295
- l .costcap = new (big. Int ). Set ( costLimit ) // Lower the caps to the thresholds
331
+ l .costcap = costLimit // Lower the caps to the thresholds
296
332
l .gascap = gasLimit
297
333
298
334
// Filter out all the transactions above the account's funds
299
- removed := l .txs .Filter (func (tx * types.Transaction ) bool { return tx .Cost ().Cmp (costLimit ) > 0 || tx .Gas () > gasLimit })
335
+ removed := l .txs .filter (func (tx * types.Transaction ) bool {
336
+ cost , _ := tx .CostU64 ()
337
+ return cost > costLimit || tx .Gas () > gasLimit
338
+ })
300
339
301
- // If the list was strict, filter anything above the lowest nonce
340
+ if len (removed ) == 0 {
341
+ return nil , nil
342
+ }
302
343
var invalids types.Transactions
303
-
304
- if l .strict && len ( removed ) > 0 {
344
+ // If the list was strict, filter anything above the lowest nonce
345
+ if l .strict {
305
346
lowest := uint64 (math .MaxUint64 )
306
347
for _ , tx := range removed {
307
348
if nonce := tx .Nonce (); lowest > nonce {
308
349
lowest = nonce
309
350
}
310
351
}
311
- invalids = l .txs .Filter (func (tx * types.Transaction ) bool { return tx .Nonce () > lowest })
352
+ invalids = l .txs .filter (func (tx * types.Transaction ) bool { return tx .Nonce () > lowest })
312
353
}
354
+ l .txs .reheap ()
313
355
return removed , invalids
314
356
}
315
357
@@ -363,6 +405,12 @@ func (l *txList) Flatten() types.Transactions {
363
405
return l .txs .Flatten ()
364
406
}
365
407
408
+ // LastElement returns the last element of a flattened list, thus, the
409
+ // transaction with the highest nonce
410
+ func (l * txList ) LastElement () * types.Transaction {
411
+ return l .txs .LastElement ()
412
+ }
413
+
366
414
// priceHeap is a heap.Interface implementation over transactions for retrieving
367
415
// price-sorted transactions to discard when the pool fills up.
368
416
type priceHeap []* types.Transaction
@@ -495,8 +543,29 @@ func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) boo
495
543
// Discard finds a number of most underpriced transactions, removes them from the
496
544
// priced list and returns them for further removal from the entire pool.
497
545
func (l * txPricedList ) Discard (slots int , local * accountSet ) types.Transactions {
498
- drop := make (types.Transactions , 0 , slots ) // Remote underpriced transactions to drop
499
- save := make (types.Transactions , 0 , 64 ) // Local underpriced transactions to keep
546
+ // If we have some local accountset, those will not be discarded
547
+ if ! local .empty () {
548
+ // In case the list is filled to the brim with 'local' txs, we do this
549
+ // little check to avoid unpacking / repacking the heap later on, which
550
+ // is very expensive
551
+ discardable := 0
552
+ for _ , tx := range * l .items {
553
+ if ! local .containsTx (tx ) {
554
+ discardable ++
555
+ }
556
+ if discardable >= slots {
557
+ break
558
+ }
559
+ }
560
+ if slots > discardable {
561
+ slots = discardable
562
+ }
563
+ }
564
+ if slots == 0 {
565
+ return nil
566
+ }
567
+ drop := make (types.Transactions , 0 , slots ) // Remote underpriced transactions to drop
568
+ save := make (types.Transactions , 0 , len (* l .items )- slots ) // Local underpriced transactions to keep
500
569
501
570
for len (* l .items ) > 0 && slots > 0 {
502
571
// Discard stale transactions if found during cleanup
0 commit comments