Skip to content

Commit 280609c

Browse files
committed
core: check for gas limit exceeding txs too on new block
1 parent dd06c85 commit 280609c

File tree

3 files changed

+72
-31
lines changed

3 files changed

+72
-31
lines changed

core/tx_list.go

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,11 @@ func (m *txSortedMap) Flatten() types.Transactions {
220220
// the executable/pending queue; and for storing gapped transactions for the non-
221221
// executable/future queue, with minor behavioral changes.
222222
type txList struct {
223-
strict bool // Whether nonces are strictly continuous or not
224-
txs *txSortedMap // Heap indexed sorted hash map of the transactions
225-
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
223+
strict bool // Whether nonces are strictly continuous or not
224+
txs *txSortedMap // Heap indexed sorted hash map of the transactions
225+
226+
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
227+
gascap *big.Int // Gas limit of the highest spending transaction (reset only if exceeds block limit)
226228
}
227229

228230
// newTxList create a new transaction list for maintaining nonce-indexable fast,
@@ -232,6 +234,7 @@ func newTxList(strict bool) *txList {
232234
strict: strict,
233235
txs: newTxSortedMap(),
234236
costcap: new(big.Int),
237+
gascap: new(big.Int),
235238
}
236239
}
237240

@@ -244,8 +247,8 @@ func (l *txList) Overlaps(tx *types.Transaction) bool {
244247
// Add tries to insert a new transaction into the list, returning whether the
245248
// transaction was accepted, and if yes, any previous transaction it replaced.
246249
//
247-
// If the new transaction is accepted into the list, the lists' cost threshold
248-
// is also potentially updated.
250+
// If the new transaction is accepted into the list, the lists' cost and gas
251+
// thresholds are also potentially updated.
249252
func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
250253
// If there's an older better transaction, abort
251254
old := l.txs.Get(tx.Nonce())
@@ -260,6 +263,9 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
260263
if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
261264
l.costcap = cost
262265
}
266+
if gas := tx.Gas(); l.gascap.Cmp(gas) < 0 {
267+
l.gascap = gas
268+
}
263269
return true, old
264270
}
265271

@@ -270,23 +276,25 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
270276
return l.txs.Forward(threshold)
271277
}
272278

273-
// Filter removes all transactions from the list with a cost higher than the
274-
// provided threshold. Every removed transaction is returned for any post-removal
275-
// maintenance. Strict-mode invalidated transactions are also returned.
279+
// Filter removes all transactions from the list with a cost or gas limit higher
280+
// than the provided thresholds. Every removed transaction is returned for any
281+
// post-removal maintenance. Strict-mode invalidated transactions are also
282+
// returned.
276283
//
277-
// This method uses the cached costcap to quickly decide if there's even a point
278-
// in calculating all the costs or if the balance covers all. If the threshold is
279-
// lower than the costcap, the costcap will be reset to a new high after removing
280-
// expensive the too transactions.
281-
func (l *txList) Filter(threshold *big.Int) (types.Transactions, types.Transactions) {
284+
// This method uses the cached costcap and gascap to quickly decide if there's even
285+
// a point in calculating all the costs or if the balance covers all. If the threshold
286+
// is lower than the costgas cap, the caps will be reset to a new high after removing
287+
// the newly invalidated transactions.
288+
func (l *txList) Filter(costLimit, gasLimit *big.Int) (types.Transactions, types.Transactions) {
282289
// If all transactions are below the threshold, short circuit
283-
if l.costcap.Cmp(threshold) <= 0 {
290+
if l.costcap.Cmp(costLimit) <= 0 && l.gascap.Cmp(gasLimit) <= 0 {
284291
return nil, nil
285292
}
286-
l.costcap = new(big.Int).Set(threshold) // Lower the cap to the threshold
293+
l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds
294+
l.gascap = new(big.Int).Set(gasLimit)
287295

288296
// Filter out all the transactions above the account's funds
289-
removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(threshold) > 0 })
297+
removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(costLimit) > 0 || tx.Gas().Cmp(gasLimit) > 0 })
290298

291299
// If the list was strict, filter anything above the lowest nonce
292300
var invalids types.Transactions

core/tx_pool.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,8 @@ func (pool *TxPool) removeTx(hash common.Hash) {
663663
// future queue to the set of pending transactions. During this process, all
664664
// invalidated transactions (low nonce, low balance) are deleted.
665665
func (pool *TxPool) promoteExecutables(state *state.StateDB) {
666+
gaslimit := pool.gasLimit()
667+
666668
// Iterate over all accounts and promote any executable transactions
667669
queued := uint64(0)
668670
for addr, list := range pool.queue {
@@ -673,8 +675,8 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
673675
delete(pool.all, hash)
674676
pool.priced.Removed()
675677
}
676-
// Drop all transactions that are too costly (low balance)
677-
drops, _ := list.Filter(state.GetBalance(addr))
678+
// Drop all transactions that are too costly (low balance or out of gas)
679+
drops, _ := list.Filter(state.GetBalance(addr), gaslimit)
678680
for _, tx := range drops {
679681
hash := tx.Hash()
680682
log.Trace("Removed unpayable queued transaction", "hash", hash)
@@ -798,6 +800,8 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
798800
// executable/pending queue and any subsequent transactions that become unexecutable
799801
// are moved back into the future queue.
800802
func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
803+
gaslimit := pool.gasLimit()
804+
801805
// Iterate over all accounts and demote any non-executable transactions
802806
for addr, list := range pool.pending {
803807
nonce := state.GetNonce(addr)
@@ -809,8 +813,8 @@ func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
809813
delete(pool.all, hash)
810814
pool.priced.Removed()
811815
}
812-
// Drop all transactions that are too costly (low balance), and queue any invalids back for later
813-
drops, invalids := list.Filter(state.GetBalance(addr))
816+
// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
817+
drops, invalids := list.Filter(state.GetBalance(addr), gaslimit)
814818
for _, tx := range drops {
815819
hash := tx.Hash()
816820
log.Trace("Removed unpayable pending transaction", "hash", hash)

core/tx_pool_test.go

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -397,50 +397,79 @@ func TestTransactionDropping(t *testing.T) {
397397
var (
398398
tx0 = transaction(0, big.NewInt(100), key)
399399
tx1 = transaction(1, big.NewInt(200), key)
400+
tx2 = transaction(2, big.NewInt(300), key)
400401
tx10 = transaction(10, big.NewInt(100), key)
401402
tx11 = transaction(11, big.NewInt(200), key)
403+
tx12 = transaction(12, big.NewInt(300), key)
402404
)
403405
pool.promoteTx(account, tx0.Hash(), tx0)
404406
pool.promoteTx(account, tx1.Hash(), tx1)
407+
pool.promoteTx(account, tx1.Hash(), tx2)
405408
pool.enqueueTx(tx10.Hash(), tx10)
406409
pool.enqueueTx(tx11.Hash(), tx11)
410+
pool.enqueueTx(tx11.Hash(), tx12)
407411

408412
// Check that pre and post validations leave the pool as is
409-
if pool.pending[account].Len() != 2 {
410-
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 2)
413+
if pool.pending[account].Len() != 3 {
414+
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 3)
411415
}
412-
if pool.queue[account].Len() != 2 {
413-
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 2)
416+
if pool.queue[account].Len() != 3 {
417+
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
414418
}
415419
if len(pool.all) != 4 {
416420
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
417421
}
418422
pool.resetState()
419-
if pool.pending[account].Len() != 2 {
420-
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 2)
423+
if pool.pending[account].Len() != 3 {
424+
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 3)
421425
}
422-
if pool.queue[account].Len() != 2 {
423-
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 2)
426+
if pool.queue[account].Len() != 3 {
427+
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
424428
}
425429
if len(pool.all) != 4 {
426430
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
427431
}
428432
// Reduce the balance of the account, and check that invalidated transactions are dropped
429-
state.AddBalance(account, big.NewInt(-750))
433+
state.AddBalance(account, big.NewInt(-650))
430434
pool.resetState()
431435

432436
if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
433437
t.Errorf("funded pending transaction missing: %v", tx0)
434438
}
435-
if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; ok {
439+
if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; !ok {
440+
t.Errorf("funded pending transaction missing: %v", tx0)
441+
}
442+
if _, ok := pool.pending[account].txs.items[tx2.Nonce()]; ok {
436443
t.Errorf("out-of-fund pending transaction present: %v", tx1)
437444
}
438445
if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok {
439446
t.Errorf("funded queued transaction missing: %v", tx10)
440447
}
441-
if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok {
448+
if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; !ok {
449+
t.Errorf("funded queued transaction missing: %v", tx10)
450+
}
451+
if _, ok := pool.queue[account].txs.items[tx12.Nonce()]; ok {
442452
t.Errorf("out-of-fund queued transaction present: %v", tx11)
443453
}
454+
if len(pool.all) != 4 {
455+
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
456+
}
457+
// Reduce the block gas limit, check that invalidated transactions are dropped
458+
pool.gasLimit = func() *big.Int { return big.NewInt(100) }
459+
pool.resetState()
460+
461+
if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
462+
t.Errorf("funded pending transaction missing: %v", tx0)
463+
}
464+
if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; ok {
465+
t.Errorf("over-gased pending transaction present: %v", tx1)
466+
}
467+
if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok {
468+
t.Errorf("funded queued transaction missing: %v", tx10)
469+
}
470+
if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok {
471+
t.Errorf("over-gased queued transaction present: %v", tx11)
472+
}
444473
if len(pool.all) != 2 {
445474
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 2)
446475
}

0 commit comments

Comments
 (0)