github.com/core-coin/go-core/v2@v2.1.9/core/tx_list.go (about)

     1  // Copyright 2016 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core
    18  
    19  import (
    20  	"container/heap"
    21  	"math"
    22  	"math/big"
    23  	"sort"
    24  
    25  	"github.com/core-coin/go-core/v2/common"
    26  	"github.com/core-coin/go-core/v2/core/types"
    27  	"github.com/core-coin/go-core/v2/log"
    28  )
    29  
    30  // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
    31  // retrieving sorted transactions from the possibly gapped future queue.
    32  type nonceHeap []uint64
    33  
    34  func (h nonceHeap) Len() int           { return len(h) }
    35  func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] }
    36  func (h nonceHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
    37  
    38  func (h *nonceHeap) Push(x interface{}) {
    39  	*h = append(*h, x.(uint64))
    40  }
    41  
    42  func (h *nonceHeap) Pop() interface{} {
    43  	old := *h
    44  	n := len(old)
    45  	x := old[n-1]
    46  	*h = old[0 : n-1]
    47  	return x
    48  }
    49  
    50  // txSortedMap is a nonce->transaction hash map with a heap based index to allow
    51  // iterating over the contents in a nonce-incrementing way.
    52  type txSortedMap struct {
    53  	items map[uint64]*types.Transaction // Hash map storing the transaction data
    54  	index *nonceHeap                    // Heap of nonces of all the stored transactions (non-strict mode)
    55  	cache types.Transactions            // Cache of the transactions already sorted
    56  }
    57  
    58  // newTxSortedMap creates a new nonce-sorted transaction map.
    59  func newTxSortedMap() *txSortedMap {
    60  	return &txSortedMap{
    61  		items: make(map[uint64]*types.Transaction),
    62  		index: new(nonceHeap),
    63  	}
    64  }
    65  
    66  // Get retrieves the current transactions associated with the given nonce.
    67  func (m *txSortedMap) Get(nonce uint64) *types.Transaction {
    68  	return m.items[nonce]
    69  }
    70  
    71  // Put inserts a new transaction into the map, also updating the map's nonce
    72  // index. If a transaction already exists with the same nonce, it's overwritten.
    73  func (m *txSortedMap) Put(tx *types.Transaction) {
    74  	nonce := tx.Nonce()
    75  	if m.items[nonce] == nil {
    76  		heap.Push(m.index, nonce)
    77  	}
    78  	m.items[nonce], m.cache = tx, nil
    79  }
    80  
    81  // Forward removes all transactions from the map with a nonce lower than the
    82  // provided threshold. Every removed transaction is returned for any post-removal
    83  // maintenance.
    84  func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
    85  	var removed types.Transactions
    86  
    87  	// Pop off heap items until the threshold is reached
    88  	for m.index.Len() > 0 && (*m.index)[0] < threshold {
    89  		nonce := heap.Pop(m.index).(uint64)
    90  		removed = append(removed, m.items[nonce])
    91  		delete(m.items, nonce)
    92  	}
    93  	// If we had a cached order, shift the front
    94  	if m.cache != nil {
    95  		m.cache = m.cache[len(removed):]
    96  	}
    97  	return removed
    98  }
    99  
   100  // Filter iterates over the list of transactions and removes all of them for which
   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()
   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 {
   126  	var removed types.Transactions
   127  
   128  	// Collect all the transactions to filter out
   129  	for nonce, tx := range m.items {
   130  		if filter(tx) {
   131  			removed = append(removed, tx)
   132  			delete(m.items, nonce)
   133  		}
   134  	}
   135  	if len(removed) > 0 {
   136  		m.cache = nil
   137  	}
   138  	return removed
   139  }
   140  
   141  // Cap places a hard limit on the number of items, returning all transactions
   142  // exceeding that limit.
   143  func (m *txSortedMap) Cap(threshold int) types.Transactions {
   144  	// Short circuit if the number of items is under the limit
   145  	if len(m.items) <= threshold {
   146  		return nil
   147  	}
   148  	// Otherwise gather and drop the highest nonce'd transactions
   149  	var drops types.Transactions
   150  
   151  	sort.Sort(*m.index)
   152  	for size := len(m.items); size > threshold; size-- {
   153  		drops = append(drops, m.items[(*m.index)[size-1]])
   154  		delete(m.items, (*m.index)[size-1])
   155  	}
   156  	*m.index = (*m.index)[:threshold]
   157  	heap.Init(m.index)
   158  
   159  	// If we had a cache, shift the back
   160  	if m.cache != nil {
   161  		m.cache = m.cache[:len(m.cache)-len(drops)]
   162  	}
   163  	return drops
   164  }
   165  
   166  // Remove deletes a transaction from the maintained map, returning whether the
   167  // transaction was found.
   168  func (m *txSortedMap) Remove(nonce uint64) bool {
   169  	// Short circuit if no transaction is present
   170  	_, ok := m.items[nonce]
   171  	if !ok {
   172  		return false
   173  	}
   174  	// Otherwise delete the transaction and fix the heap index
   175  	for i := 0; i < m.index.Len(); i++ {
   176  		if (*m.index)[i] == nonce {
   177  			heap.Remove(m.index, i)
   178  			break
   179  		}
   180  	}
   181  	delete(m.items, nonce)
   182  	m.cache = nil
   183  
   184  	return true
   185  }
   186  
   187  // Ready retrieves a sequentially increasing list of transactions starting at the
   188  // provided nonce that is ready for processing. The returned transactions will be
   189  // removed from the list.
   190  //
   191  // Note, all transactions with nonces lower than start will also be returned to
   192  // prevent getting into and invalid state. This is not something that should ever
   193  // happen but better to be self correcting than failing!
   194  func (m *txSortedMap) Ready(start uint64) types.Transactions {
   195  	// Short circuit if no transactions are available
   196  	if m.index.Len() == 0 || (*m.index)[0] > start {
   197  		return nil
   198  	}
   199  	// Otherwise start accumulating incremental transactions
   200  	var ready types.Transactions
   201  	for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ {
   202  		ready = append(ready, m.items[next])
   203  		delete(m.items, next)
   204  		heap.Pop(m.index)
   205  	}
   206  	m.cache = nil
   207  
   208  	return ready
   209  }
   210  
   211  // Len returns the length of the transaction map.
   212  func (m *txSortedMap) Len() int {
   213  	return len(m.items)
   214  }
   215  
   216  func (m *txSortedMap) flatten() types.Transactions {
   217  	// If the sorting was not cached yet, create and cache it
   218  	if m.cache == nil {
   219  		m.cache = make(types.Transactions, 0, len(m.items))
   220  		for _, tx := range m.items {
   221  			m.cache = append(m.cache, tx)
   222  		}
   223  		sort.Sort(types.TxByNonce(m.cache))
   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 {
   232  	// Copy the cache to prevent accidental modifications
   233  	cache := m.flatten()
   234  	txs := make(types.Transactions, len(cache))
   235  	copy(txs, cache)
   236  	return txs
   237  }
   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  
   246  // txList is a "list" of transactions belonging to an account, sorted by account
   247  // nonce. The same type can be used both for storing contiguous transactions for
   248  // the executable/pending queue; and for storing gapped transactions for the non-
   249  // executable/future queue, with minor behavioral changes.
   250  type txList struct {
   251  	strict bool         // Whether nonces are strictly continuous or not
   252  	txs    *txSortedMap // Heap indexed sorted hash map of the transactions
   253  
   254  	costcap   *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
   255  	energycap uint64   // Energy limit of the highest spending transaction (reset only if exceeds block limit)
   256  }
   257  
   258  // newTxList create a new transaction list for maintaining nonce-indexable fast,
   259  // gapped, sortable transaction lists.
   260  func newTxList(strict bool) *txList {
   261  	return &txList{
   262  		strict:  strict,
   263  		txs:     newTxSortedMap(),
   264  		costcap: new(big.Int),
   265  	}
   266  }
   267  
   268  // Overlaps returns whether the transaction specified has the same nonce as one
   269  // already contained within the list.
   270  func (l *txList) Overlaps(tx *types.Transaction) bool {
   271  	return l.txs.Get(tx.Nonce()) != nil
   272  }
   273  
   274  // Add tries to insert a new transaction into the list, returning whether the
   275  // transaction was accepted, and if yes, any previous transaction it replaced.
   276  //
   277  // If the new transaction is accepted into the list, the lists' cost and energy
   278  // thresholds are also potentially updated.
   279  func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
   280  	// If there's an older better transaction, abort
   281  	old := l.txs.Get(tx.Nonce())
   282  	if old != nil {
   283  		// threshold = oldGP * (100 + priceBump) / 100
   284  		a := big.NewInt(100 + int64(priceBump))
   285  		a = a.Mul(a, old.EnergyPrice())
   286  		b := big.NewInt(100)
   287  		threshold := a.Div(a, b)
   288  		// Have to ensure that the new energy price is higher than the old energy
   289  		// price as well as checking the percentage threshold to ensure that
   290  		// this is accurate for low (Wei-level) energy price replacements
   291  		if old.EnergyPriceCmp(tx) >= 0 || tx.EnergyPriceIntCmp(threshold) < 0 {
   292  			return false, nil
   293  		}
   294  	}
   295  	// Otherwise overwrite the old transaction with the current one
   296  	l.txs.Put(tx)
   297  	if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
   298  		l.costcap = cost
   299  	}
   300  	if energy := tx.Energy(); l.energycap < energy {
   301  		l.energycap = energy
   302  	}
   303  	return true, old
   304  }
   305  
   306  // Forward removes all transactions from the list with a nonce lower than the
   307  // provided threshold. Every removed transaction is returned for any post-removal
   308  // maintenance.
   309  func (l *txList) Forward(threshold uint64) types.Transactions {
   310  	return l.txs.Forward(threshold)
   311  }
   312  
   313  // Filter removes all transactions from the list with a cost or energy limit higher
   314  // than the provided thresholds. Every removed transaction is returned for any
   315  // post-removal maintenance. Strict-mode invalidated transactions are also
   316  // returned.
   317  //
   318  // This method uses the cached costcap and energycap to quickly decide if there's even
   319  // a point in calculating all the costs or if the balance covers all. If the threshold
   320  // is lower than the costenergy cap, the caps will be reset to a new high after removing
   321  // the newly invalidated transactions.
   322  func (l *txList) Filter(costLimit *big.Int, energyLimit uint64) (types.Transactions, types.Transactions) {
   323  	// If all transactions are below the threshold, short circuit
   324  	if l.costcap.Cmp(costLimit) <= 0 && l.energycap <= energyLimit {
   325  		return nil, nil
   326  	}
   327  	l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds
   328  	l.energycap = energyLimit
   329  
   330  	// Filter out all the transactions above the account's funds
   331  	removed := l.txs.Filter(func(tx *types.Transaction) bool {
   332  		return tx.Energy() > energyLimit || tx.Cost().Cmp(costLimit) > 0
   333  	})
   334  
   335  	if len(removed) == 0 {
   336  		return nil, nil
   337  	}
   338  	var invalids types.Transactions
   339  	// If the list was strict, filter anything above the lowest nonce
   340  	if l.strict {
   341  		lowest := uint64(math.MaxUint64)
   342  		for _, tx := range removed {
   343  			if nonce := tx.Nonce(); lowest > nonce {
   344  				lowest = nonce
   345  			}
   346  		}
   347  		invalids = l.txs.filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
   348  	}
   349  	l.txs.reheap()
   350  	return removed, invalids
   351  }
   352  
   353  // Cap places a hard limit on the number of items, returning all transactions
   354  // exceeding that limit.
   355  func (l *txList) Cap(threshold int) types.Transactions {
   356  	return l.txs.Cap(threshold)
   357  }
   358  
   359  // Remove deletes a transaction from the maintained list, returning whether the
   360  // transaction was found, and also returning any transaction invalidated due to
   361  // the deletion (strict mode only).
   362  func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
   363  	// Remove the transaction from the set
   364  	nonce := tx.Nonce()
   365  	if removed := l.txs.Remove(nonce); !removed {
   366  		return false, nil
   367  	}
   368  	// In strict mode, filter out non-executable transactions
   369  	if l.strict {
   370  		return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce })
   371  	}
   372  	return true, nil
   373  }
   374  
   375  // Ready retrieves a sequentially increasing list of transactions starting at the
   376  // provided nonce that is ready for processing. The returned transactions will be
   377  // removed from the list.
   378  //
   379  // Note, all transactions with nonces lower than start will also be returned to
   380  // prevent getting into and invalid state. This is not something that should ever
   381  // happen but better to be self correcting than failing!
   382  func (l *txList) Ready(start uint64) types.Transactions {
   383  	return l.txs.Ready(start)
   384  }
   385  
   386  // Len returns the length of the transaction list.
   387  func (l *txList) Len() int {
   388  	return l.txs.Len()
   389  }
   390  
   391  // Empty returns whether the list of transactions is empty or not.
   392  func (l *txList) Empty() bool {
   393  	return l.Len() == 0
   394  }
   395  
   396  // Flatten creates a nonce-sorted slice of transactions based on the loosely
   397  // sorted internal representation. The result of the sorting is cached in case
   398  // it's requested again before any modifications are made to the contents.
   399  func (l *txList) Flatten() types.Transactions {
   400  	return l.txs.Flatten()
   401  }
   402  
   403  // LastElement returns the last element of a flattened list, thus, the
   404  // transaction with the highest nonce
   405  func (l *txList) LastElement() *types.Transaction {
   406  	return l.txs.LastElement()
   407  }
   408  
   409  // priceHeap is a heap.Interface implementation over transactions for retrieving
   410  // price-sorted transactions to discard when the pool fills up.
   411  type priceHeap []*types.Transaction
   412  
   413  func (h priceHeap) Len() int      { return len(h) }
   414  func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
   415  
   416  func (h priceHeap) Less(i, j int) bool {
   417  	// Sort primarily by price, returning the cheaper one
   418  	switch h[i].EnergyPriceCmp(h[j]) {
   419  	case -1:
   420  		return true
   421  	case 1:
   422  		return false
   423  	}
   424  	// If the prices match, stabilize via nonces (high nonce is worse)
   425  	return h[i].Nonce() > h[j].Nonce()
   426  }
   427  
   428  func (h *priceHeap) Push(x interface{}) {
   429  	*h = append(*h, x.(*types.Transaction))
   430  }
   431  
   432  func (h *priceHeap) Pop() interface{} {
   433  	old := *h
   434  	n := len(old)
   435  	x := old[n-1]
   436  	old[n-1] = nil
   437  	*h = old[0 : n-1]
   438  	return x
   439  }
   440  
   441  // txPricedList is a price-sorted heap to allow operating on transactions pool
   442  // contents in a price-incrementing way.
   443  type txPricedList struct {
   444  	all    *txLookup  // Pointer to the map of all transactions
   445  	items  *priceHeap // Heap of prices of all the stored transactions
   446  	stales int        // Number of stale price points to (re-heap trigger)
   447  }
   448  
   449  // newTxPricedList creates a new price-sorted transaction heap.
   450  func newTxPricedList(all *txLookup) *txPricedList {
   451  	return &txPricedList{
   452  		all:   all,
   453  		items: new(priceHeap),
   454  	}
   455  }
   456  
   457  // Put inserts a new transaction into the heap.
   458  func (l *txPricedList) Put(tx *types.Transaction) {
   459  	heap.Push(l.items, tx)
   460  }
   461  
   462  // Removed notifies the prices transaction list that an old transaction dropped
   463  // from the pool. The list will just keep a counter of stale objects and update
   464  // the heap if a large enough ratio of transactions go stale.
   465  func (l *txPricedList) Removed(count int) {
   466  	// Bump the stale counter, but exit if still too low (< 25%)
   467  	l.stales += count
   468  	if l.stales <= len(*l.items)/4 {
   469  		return
   470  	}
   471  	// Seems we've reached a critical number of stale transactions, reheap
   472  	reheap := make(priceHeap, 0, l.all.Count())
   473  
   474  	l.stales, l.items = 0, &reheap
   475  	l.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
   476  		*l.items = append(*l.items, tx)
   477  		return true
   478  	})
   479  	heap.Init(l.items)
   480  }
   481  
   482  // Cap finds all the transactions below the given price threshold, drops them
   483  // from the priced list and returns them for further removal from the entire pool.
   484  func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transactions {
   485  	drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop
   486  	save := make(types.Transactions, 0, 64)  // Local underpriced transactions to keep
   487  
   488  	for len(*l.items) > 0 {
   489  		// Discard stale transactions if found during cleanup
   490  		tx := heap.Pop(l.items).(*types.Transaction)
   491  		if l.all.Get(tx.Hash()) == nil {
   492  			l.stales--
   493  			continue
   494  		}
   495  		// Stop the discards if we've reached the threshold
   496  		if tx.EnergyPriceIntCmp(threshold) >= 0 {
   497  			save = append(save, tx)
   498  			break
   499  		}
   500  		// Non stale transaction found, discard unless local
   501  		if local.containsTx(tx) {
   502  			save = append(save, tx)
   503  		} else {
   504  			drop = append(drop, tx)
   505  		}
   506  	}
   507  	for _, tx := range save {
   508  		heap.Push(l.items, tx)
   509  	}
   510  	return drop
   511  }
   512  
   513  // Underpriced checks whether a transaction is cheaper than (or as cheap as) the
   514  // lowest priced transaction currently being tracked.
   515  func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) bool {
   516  	// Local transactions cannot be underpriced
   517  	if local.containsTx(tx) {
   518  		return false
   519  	}
   520  	// Discard stale price points if found at the heap start
   521  	for len(*l.items) > 0 {
   522  		head := []*types.Transaction(*l.items)[0]
   523  		if l.all.Get(head.Hash()) == nil {
   524  			l.stales--
   525  			heap.Pop(l.items)
   526  			continue
   527  		}
   528  		break
   529  	}
   530  	// Check if the transaction is underpriced or not
   531  	if len(*l.items) == 0 {
   532  		log.Error("Pricing query for empty pool") // This cannot happen, print to catch programming errors
   533  		return false
   534  	}
   535  	cheapest := []*types.Transaction(*l.items)[0]
   536  	return cheapest.EnergyPriceCmp(tx) >= 0
   537  }
   538  
   539  // Discard finds a number of most underpriced transactions, removes them from the
   540  // priced list and returns them for further removal from the entire pool.
   541  func (l *txPricedList) Discard(slots int, local *accountSet) types.Transactions {
   542  	// If we have some local accountset, those will not be discarded
   543  	if !local.empty() {
   544  		// In case the list is filled to the brim with 'local' txs, we do this
   545  		// little check to avoid unpacking / repacking the heap later on, which
   546  		// is very expensive
   547  		discardable := 0
   548  		for _, tx := range *l.items {
   549  			if !local.containsTx(tx) {
   550  				discardable++
   551  			}
   552  			if discardable >= slots {
   553  				break
   554  			}
   555  		}
   556  		if slots > discardable {
   557  			slots = discardable
   558  		}
   559  	}
   560  	if slots == 0 {
   561  		return nil
   562  	}
   563  	drop := make(types.Transactions, 0, slots)               // Remote underpriced transactions to drop
   564  	save := make(types.Transactions, 0, len(*l.items)-slots) // Local underpriced transactions to keep
   565  
   566  	for len(*l.items) > 0 && slots > 0 {
   567  		// Discard stale transactions if found during cleanup
   568  		tx := heap.Pop(l.items).(*types.Transaction)
   569  		if l.all.Get(tx.Hash()) == nil {
   570  			l.stales--
   571  			continue
   572  		}
   573  		// Non stale transaction found, discard unless local
   574  		if local.containsTx(tx) {
   575  			save = append(save, tx)
   576  		} else {
   577  			drop = append(drop, tx)
   578  			slots -= numSlots(tx)
   579  		}
   580  	}
   581  	for _, tx := range save {
   582  		heap.Push(l.items, tx)
   583  	}
   584  	return drop
   585  }