Merge pull request #14547 from karalabe/txpool-gas-decrease
core: check for gas limit exceeding txs too on new block
This commit is contained in:
		@@ -220,9 +220,11 @@ func (m *txSortedMap) Flatten() types.Transactions {
 | 
				
			|||||||
// the executable/pending queue; and for storing gapped transactions for the non-
 | 
					// the executable/pending queue; and for storing gapped transactions for the non-
 | 
				
			||||||
// executable/future queue, with minor behavioral changes.
 | 
					// executable/future queue, with minor behavioral changes.
 | 
				
			||||||
type txList struct {
 | 
					type txList struct {
 | 
				
			||||||
	strict  bool         // Whether nonces are strictly continuous or not
 | 
						strict bool         // Whether nonces are strictly continuous or not
 | 
				
			||||||
	txs     *txSortedMap // Heap indexed sorted hash map of the transactions
 | 
						txs    *txSortedMap // Heap indexed sorted hash map of the transactions
 | 
				
			||||||
	costcap *big.Int     // Price of the highest costing transaction (reset only if exceeds balance)
 | 
					
 | 
				
			||||||
 | 
						costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
 | 
				
			||||||
 | 
						gascap  *big.Int // Gas limit of the highest spending transaction (reset only if exceeds block limit)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// newTxList create a new transaction list for maintaining nonce-indexable fast,
 | 
					// newTxList create a new transaction list for maintaining nonce-indexable fast,
 | 
				
			||||||
@@ -232,6 +234,7 @@ func newTxList(strict bool) *txList {
 | 
				
			|||||||
		strict:  strict,
 | 
							strict:  strict,
 | 
				
			||||||
		txs:     newTxSortedMap(),
 | 
							txs:     newTxSortedMap(),
 | 
				
			||||||
		costcap: new(big.Int),
 | 
							costcap: new(big.Int),
 | 
				
			||||||
 | 
							gascap:  new(big.Int),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -244,8 +247,8 @@ func (l *txList) Overlaps(tx *types.Transaction) bool {
 | 
				
			|||||||
// Add tries to insert a new transaction into the list, returning whether the
 | 
					// Add tries to insert a new transaction into the list, returning whether the
 | 
				
			||||||
// transaction was accepted, and if yes, any previous transaction it replaced.
 | 
					// transaction was accepted, and if yes, any previous transaction it replaced.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// If the new transaction is accepted into the list, the lists' cost threshold
 | 
					// If the new transaction is accepted into the list, the lists' cost and gas
 | 
				
			||||||
// is also potentially updated.
 | 
					// thresholds are also potentially updated.
 | 
				
			||||||
func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
 | 
					func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
 | 
				
			||||||
	// If there's an older better transaction, abort
 | 
						// If there's an older better transaction, abort
 | 
				
			||||||
	old := l.txs.Get(tx.Nonce())
 | 
						old := l.txs.Get(tx.Nonce())
 | 
				
			||||||
@@ -260,6 +263,9 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
 | 
				
			|||||||
	if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
 | 
						if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
 | 
				
			||||||
		l.costcap = cost
 | 
							l.costcap = cost
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if gas := tx.Gas(); l.gascap.Cmp(gas) < 0 {
 | 
				
			||||||
 | 
							l.gascap = gas
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return true, old
 | 
						return true, old
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -270,23 +276,25 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
 | 
				
			|||||||
	return l.txs.Forward(threshold)
 | 
						return l.txs.Forward(threshold)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Filter removes all transactions from the list with a cost higher than the
 | 
					// Filter removes all transactions from the list with a cost or gas limit higher
 | 
				
			||||||
// provided threshold. Every removed transaction is returned for any post-removal
 | 
					// than the provided thresholds. Every removed transaction is returned for any
 | 
				
			||||||
// maintenance. Strict-mode invalidated transactions are also returned.
 | 
					// post-removal maintenance. Strict-mode invalidated transactions are also
 | 
				
			||||||
 | 
					// returned.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This method uses the cached costcap to quickly decide if there's even a point
 | 
					// This method uses the cached costcap and gascap to quickly decide if there's even
 | 
				
			||||||
// in calculating all the costs or if the balance covers all. If the threshold is
 | 
					// a point in calculating all the costs or if the balance covers all. If the threshold
 | 
				
			||||||
// lower than the costcap, the costcap will be reset to a new high after removing
 | 
					// is lower than the costgas cap, the caps will be reset to a new high after removing
 | 
				
			||||||
// expensive the too transactions.
 | 
					// the newly invalidated transactions.
 | 
				
			||||||
func (l *txList) Filter(threshold *big.Int) (types.Transactions, types.Transactions) {
 | 
					func (l *txList) Filter(costLimit, gasLimit *big.Int) (types.Transactions, types.Transactions) {
 | 
				
			||||||
	// If all transactions are below the threshold, short circuit
 | 
						// If all transactions are below the threshold, short circuit
 | 
				
			||||||
	if l.costcap.Cmp(threshold) <= 0 {
 | 
						if l.costcap.Cmp(costLimit) <= 0 && l.gascap.Cmp(gasLimit) <= 0 {
 | 
				
			||||||
		return nil, nil
 | 
							return nil, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	l.costcap = new(big.Int).Set(threshold) // Lower the cap to the threshold
 | 
						l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds
 | 
				
			||||||
 | 
						l.gascap = new(big.Int).Set(gasLimit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Filter out all the transactions above the account's funds
 | 
						// Filter out all the transactions above the account's funds
 | 
				
			||||||
	removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(threshold) > 0 })
 | 
						removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(costLimit) > 0 || tx.Gas().Cmp(gasLimit) > 0 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If the list was strict, filter anything above the lowest nonce
 | 
						// If the list was strict, filter anything above the lowest nonce
 | 
				
			||||||
	var invalids types.Transactions
 | 
						var invalids types.Transactions
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -663,6 +663,8 @@ func (pool *TxPool) removeTx(hash common.Hash) {
 | 
				
			|||||||
// future queue to the set of pending transactions. During this process, all
 | 
					// future queue to the set of pending transactions. During this process, all
 | 
				
			||||||
// invalidated transactions (low nonce, low balance) are deleted.
 | 
					// invalidated transactions (low nonce, low balance) are deleted.
 | 
				
			||||||
func (pool *TxPool) promoteExecutables(state *state.StateDB) {
 | 
					func (pool *TxPool) promoteExecutables(state *state.StateDB) {
 | 
				
			||||||
 | 
						gaslimit := pool.gasLimit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Iterate over all accounts and promote any executable transactions
 | 
						// Iterate over all accounts and promote any executable transactions
 | 
				
			||||||
	queued := uint64(0)
 | 
						queued := uint64(0)
 | 
				
			||||||
	for addr, list := range pool.queue {
 | 
						for addr, list := range pool.queue {
 | 
				
			||||||
@@ -673,8 +675,8 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
 | 
				
			|||||||
			delete(pool.all, hash)
 | 
								delete(pool.all, hash)
 | 
				
			||||||
			pool.priced.Removed()
 | 
								pool.priced.Removed()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// Drop all transactions that are too costly (low balance)
 | 
							// Drop all transactions that are too costly (low balance or out of gas)
 | 
				
			||||||
		drops, _ := list.Filter(state.GetBalance(addr))
 | 
							drops, _ := list.Filter(state.GetBalance(addr), gaslimit)
 | 
				
			||||||
		for _, tx := range drops {
 | 
							for _, tx := range drops {
 | 
				
			||||||
			hash := tx.Hash()
 | 
								hash := tx.Hash()
 | 
				
			||||||
			log.Trace("Removed unpayable queued transaction", "hash", hash)
 | 
								log.Trace("Removed unpayable queued transaction", "hash", hash)
 | 
				
			||||||
@@ -798,6 +800,8 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
 | 
				
			|||||||
// executable/pending queue and any subsequent transactions that become unexecutable
 | 
					// executable/pending queue and any subsequent transactions that become unexecutable
 | 
				
			||||||
// are moved back into the future queue.
 | 
					// are moved back into the future queue.
 | 
				
			||||||
func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
 | 
					func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
 | 
				
			||||||
 | 
						gaslimit := pool.gasLimit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Iterate over all accounts and demote any non-executable transactions
 | 
						// Iterate over all accounts and demote any non-executable transactions
 | 
				
			||||||
	for addr, list := range pool.pending {
 | 
						for addr, list := range pool.pending {
 | 
				
			||||||
		nonce := state.GetNonce(addr)
 | 
							nonce := state.GetNonce(addr)
 | 
				
			||||||
@@ -809,8 +813,8 @@ func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
 | 
				
			|||||||
			delete(pool.all, hash)
 | 
								delete(pool.all, hash)
 | 
				
			||||||
			pool.priced.Removed()
 | 
								pool.priced.Removed()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// Drop all transactions that are too costly (low balance), and queue any invalids back for later
 | 
							// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
 | 
				
			||||||
		drops, invalids := list.Filter(state.GetBalance(addr))
 | 
							drops, invalids := list.Filter(state.GetBalance(addr), gaslimit)
 | 
				
			||||||
		for _, tx := range drops {
 | 
							for _, tx := range drops {
 | 
				
			||||||
			hash := tx.Hash()
 | 
								hash := tx.Hash()
 | 
				
			||||||
			log.Trace("Removed unpayable pending transaction", "hash", hash)
 | 
								log.Trace("Removed unpayable pending transaction", "hash", hash)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -397,49 +397,78 @@ func TestTransactionDropping(t *testing.T) {
 | 
				
			|||||||
	var (
 | 
						var (
 | 
				
			||||||
		tx0  = transaction(0, big.NewInt(100), key)
 | 
							tx0  = transaction(0, big.NewInt(100), key)
 | 
				
			||||||
		tx1  = transaction(1, big.NewInt(200), key)
 | 
							tx1  = transaction(1, big.NewInt(200), key)
 | 
				
			||||||
 | 
							tx2  = transaction(2, big.NewInt(300), key)
 | 
				
			||||||
		tx10 = transaction(10, big.NewInt(100), key)
 | 
							tx10 = transaction(10, big.NewInt(100), key)
 | 
				
			||||||
		tx11 = transaction(11, big.NewInt(200), key)
 | 
							tx11 = transaction(11, big.NewInt(200), key)
 | 
				
			||||||
 | 
							tx12 = transaction(12, big.NewInt(300), key)
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	pool.promoteTx(account, tx0.Hash(), tx0)
 | 
						pool.promoteTx(account, tx0.Hash(), tx0)
 | 
				
			||||||
	pool.promoteTx(account, tx1.Hash(), tx1)
 | 
						pool.promoteTx(account, tx1.Hash(), tx1)
 | 
				
			||||||
 | 
						pool.promoteTx(account, tx1.Hash(), tx2)
 | 
				
			||||||
	pool.enqueueTx(tx10.Hash(), tx10)
 | 
						pool.enqueueTx(tx10.Hash(), tx10)
 | 
				
			||||||
	pool.enqueueTx(tx11.Hash(), tx11)
 | 
						pool.enqueueTx(tx11.Hash(), tx11)
 | 
				
			||||||
 | 
						pool.enqueueTx(tx11.Hash(), tx12)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 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() != 2 {
 | 
						if pool.pending[account].Len() != 3 {
 | 
				
			||||||
		t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 2)
 | 
							t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 3)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if pool.queue[account].Len() != 2 {
 | 
						if pool.queue[account].Len() != 3 {
 | 
				
			||||||
		t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 2)
 | 
							t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(pool.all) != 4 {
 | 
						if len(pool.all) != 4 {
 | 
				
			||||||
		t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
 | 
							t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	pool.resetState()
 | 
						pool.resetState()
 | 
				
			||||||
	if pool.pending[account].Len() != 2 {
 | 
						if pool.pending[account].Len() != 3 {
 | 
				
			||||||
		t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 2)
 | 
							t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 3)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if pool.queue[account].Len() != 2 {
 | 
						if pool.queue[account].Len() != 3 {
 | 
				
			||||||
		t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 2)
 | 
							t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(pool.all) != 4 {
 | 
						if len(pool.all) != 4 {
 | 
				
			||||||
		t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
 | 
							t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Reduce the balance of the account, and check that invalidated transactions are dropped
 | 
						// Reduce the balance of the account, and check that invalidated transactions are dropped
 | 
				
			||||||
	state.AddBalance(account, big.NewInt(-750))
 | 
						state.AddBalance(account, big.NewInt(-650))
 | 
				
			||||||
 | 
						pool.resetState()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
 | 
				
			||||||
 | 
							t.Errorf("funded pending transaction missing: %v", tx0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; !ok {
 | 
				
			||||||
 | 
							t.Errorf("funded pending transaction missing: %v", tx0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, ok := pool.pending[account].txs.items[tx2.Nonce()]; ok {
 | 
				
			||||||
 | 
							t.Errorf("out-of-fund pending transaction present: %v", tx1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok {
 | 
				
			||||||
 | 
							t.Errorf("funded queued transaction missing: %v", tx10)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; !ok {
 | 
				
			||||||
 | 
							t.Errorf("funded queued transaction missing: %v", tx10)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, ok := pool.queue[account].txs.items[tx12.Nonce()]; ok {
 | 
				
			||||||
 | 
							t.Errorf("out-of-fund queued transaction present: %v", tx11)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(pool.all) != 4 {
 | 
				
			||||||
 | 
							t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Reduce the block gas limit, check that invalidated transactions are dropped
 | 
				
			||||||
 | 
						pool.gasLimit = func() *big.Int { return big.NewInt(100) }
 | 
				
			||||||
	pool.resetState()
 | 
						pool.resetState()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
 | 
						if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
 | 
				
			||||||
		t.Errorf("funded pending transaction missing: %v", tx0)
 | 
							t.Errorf("funded pending transaction missing: %v", tx0)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; ok {
 | 
						if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; ok {
 | 
				
			||||||
		t.Errorf("out-of-fund pending transaction present: %v", tx1)
 | 
							t.Errorf("over-gased pending transaction present: %v", tx1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok {
 | 
						if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok {
 | 
				
			||||||
		t.Errorf("funded queued transaction missing: %v", tx10)
 | 
							t.Errorf("funded queued transaction missing: %v", tx10)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok {
 | 
						if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok {
 | 
				
			||||||
		t.Errorf("out-of-fund queued transaction present: %v", tx11)
 | 
							t.Errorf("over-gased queued transaction present: %v", tx11)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(pool.all) != 2 {
 | 
						if len(pool.all) != 2 {
 | 
				
			||||||
		t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 2)
 | 
							t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 2)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user