github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/core/tx_list.go (about)

     1  // Copyright 2016 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum 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 Spectrum 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 Spectrum 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  	"sync"
    26  
    27  	"github.com/SmartMeshFoundation/Spectrum/common"
    28  	"github.com/SmartMeshFoundation/Spectrum/core/types"
    29  	"github.com/SmartMeshFoundation/Spectrum/log"
    30  )
    31  
    32  // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
    33  // retrieving sorted transactions from the possibly gapped future queue.
    34  type nonceHeap []uint64
    35  
    36  func (h nonceHeap) Len() int           { return len(h) }
    37  func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] }
    38  func (h nonceHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
    39  
    40  func (h *nonceHeap) Push(x interface{}) {
    41  	*h = append(*h, x.(uint64))
    42  }
    43  
    44  func (h *nonceHeap) Pop() interface{} {
    45  	old := *h
    46  	n := len(old)
    47  	x := old[n-1]
    48  	*h = old[0 : n-1]
    49  	return x
    50  }
    51  
    52  // txSortedMap is a nonce->transaction hash map with a heap based index to allow
    53  // iterating over the contents in a nonce-incrementing way.
    54  type txSortedMap struct {
    55  	sync.RWMutex                               // add by liangc
    56  	items        map[uint64]*types.Transaction // Hash map storing the transaction data
    57  	index        *nonceHeap                    // Heap of nonces of all the stored transactions (non-strict mode)
    58  	cache        types.Transactions            // Cache of the transactions already sorted
    59  }
    60  
    61  // newTxSortedMap creates a new nonce-sorted transaction map.
    62  func newTxSortedMap() *txSortedMap {
    63  	return &txSortedMap{
    64  		items: make(map[uint64]*types.Transaction),
    65  		index: new(nonceHeap),
    66  	}
    67  }
    68  
    69  // Get retrieves the current transactions associated with the given nonce.
    70  func (m *txSortedMap) Get(nonce uint64) *types.Transaction {
    71  	m.RLock()
    72  	defer m.RUnlock()
    73  	return m.items[nonce]
    74  }
    75  
    76  // Put inserts a new transaction into the map, also updating the map's nonce
    77  // index. If a transaction already exists with the same nonce, it's overwritten.
    78  func (m *txSortedMap) Put(tx *types.Transaction) {
    79  	m.Lock()
    80  	defer m.Unlock()
    81  	nonce := tx.Nonce()
    82  	if m.items[nonce] == nil {
    83  		heap.Push(m.index, nonce)
    84  	}
    85  	m.items[nonce], m.cache = tx, nil
    86  }
    87  
    88  // Forward removes all transactions from the map with a nonce lower than the
    89  // provided threshold. Every removed transaction is returned for any post-removal
    90  // maintenance.
    91  func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
    92  	m.Lock()
    93  	defer m.Unlock()
    94  	var removed types.Transactions
    95  
    96  	// Pop off heap items until the threshold is reached
    97  	for m.index.Len() > 0 && (*m.index)[0] < threshold {
    98  		nonce := heap.Pop(m.index).(uint64)
    99  		removed = append(removed, m.items[nonce])
   100  		delete(m.items, nonce)
   101  	}
   102  	// If we had a cached order, shift the front
   103  	if m.cache != nil {
   104  		m.cache = m.cache[len(removed):]
   105  	}
   106  	return removed
   107  }
   108  
   109  // Filter iterates over the list of transactions and removes all of them for which
   110  // the specified function evaluates to true.
   111  func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
   112  	m.Lock()
   113  	defer m.Unlock()
   114  	var removed types.Transactions
   115  
   116  	// Collect all the transactions to filter out
   117  	for nonce, tx := range m.items {
   118  		if filter(tx) {
   119  			removed = append(removed, tx)
   120  			delete(m.items, nonce)
   121  		}
   122  	}
   123  	// If transactions were removed, the heap and cache are ruined
   124  	if len(removed) > 0 {
   125  		*m.index = make([]uint64, 0, len(m.items))
   126  		for nonce := range m.items {
   127  			*m.index = append(*m.index, nonce)
   128  		}
   129  		heap.Init(m.index)
   130  
   131  		m.cache = nil
   132  	}
   133  	return removed
   134  }
   135  
   136  // Cap places a hard limit on the number of items, returning all transactions
   137  // exceeding that limit.
   138  func (m *txSortedMap) Cap(threshold int) types.Transactions {
   139  	m.Lock()
   140  	defer m.Unlock()
   141  	// Short circuit if the number of items is under the limit
   142  	if len(m.items) <= threshold {
   143  		return nil
   144  	}
   145  	// Otherwise gather and drop the highest nonce'd transactions
   146  	var drops types.Transactions
   147  
   148  	sort.Sort(*m.index)
   149  	for size := len(m.items); size > threshold; size-- {
   150  		drops = append(drops, m.items[(*m.index)[size-1]])
   151  		delete(m.items, (*m.index)[size-1])
   152  	}
   153  	*m.index = (*m.index)[:threshold]
   154  	heap.Init(m.index)
   155  
   156  	// If we had a cache, shift the back
   157  	if m.cache != nil {
   158  		m.cache = m.cache[:len(m.cache)-len(drops)]
   159  	}
   160  	return drops
   161  }
   162  
   163  // Remove deletes a transaction from the maintained map, returning whether the
   164  // transaction was found.
   165  func (m *txSortedMap) Remove(nonce uint64) bool {
   166  	m.Lock()
   167  	defer m.Unlock()
   168  	// Short circuit if no transaction is present
   169  	_, ok := m.items[nonce]
   170  	if !ok {
   171  		return false
   172  	}
   173  	// Otherwise delete the transaction and fix the heap index
   174  	for i := 0; i < m.index.Len(); i++ {
   175  		if (*m.index)[i] == nonce {
   176  			heap.Remove(m.index, i)
   177  			break
   178  		}
   179  	}
   180  	delete(m.items, nonce)
   181  	m.cache = nil
   182  
   183  	return true
   184  }
   185  
   186  // Ready retrieves a sequentially increasing list of transactions starting at the
   187  // provided nonce that is ready for processing. The returned transactions will be
   188  // removed from the list.
   189  //
   190  // Note, all transactions with nonces lower than start will also be returned to
   191  // prevent getting into and invalid state. This is not something that should ever
   192  // happen but better to be self correcting than failing!
   193  func (m *txSortedMap) Ready(start uint64) types.Transactions {
   194  	m.Lock()
   195  	defer m.Unlock()
   196  	// Short circuit if no transactions are available
   197  	if m.index.Len() == 0 || (*m.index)[0] > start {
   198  		return nil
   199  	}
   200  	// Otherwise start accumulating incremental transactions
   201  	var ready types.Transactions
   202  	for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ {
   203  		ready = append(ready, m.items[next])
   204  		delete(m.items, next)
   205  		heap.Pop(m.index)
   206  	}
   207  	m.cache = nil
   208  
   209  	return ready
   210  }
   211  
   212  // Len returns the length of the transaction map.
   213  func (m *txSortedMap) Len() int {
   214  	m.RLock()
   215  	defer m.RUnlock()
   216  	return len(m.items)
   217  }
   218  
   219  // Flatten creates a nonce-sorted slice of transactions based on the loosely
   220  // sorted internal representation. The result of the sorting is cached in case
   221  // it's requested again before any modifications are made to the contents.
   222  func (m *txSortedMap) Flatten() types.Transactions {
   223  	m.Lock()
   224  	defer m.Unlock()
   225  	// If the sorting was not cached yet, create and cache it
   226  	if m.cache == nil {
   227  		m.cache = make(types.Transactions, 0, len(m.items))
   228  		for _, tx := range m.items {
   229  			m.cache = append(m.cache, tx)
   230  		}
   231  		sort.Sort(types.TxByNonce(m.cache))
   232  	}
   233  	// Copy the cache to prevent accidental modifications
   234  	txs := make(types.Transactions, len(m.cache))
   235  	copy(txs, m.cache)
   236  	return txs
   237  }
   238  
   239  // txList is a "list" of transactions belonging to an account, sorted by account
   240  // nonce. The same type can be used both for storing contiguous transactions for
   241  // the executable/pending queue; and for storing gapped transactions for the non-
   242  // executable/future queue, with minor behavioral changes.
   243  type txList struct {
   244  	strict bool         // Whether nonces are strictly continuous or not
   245  	txs    *txSortedMap // Heap indexed sorted hash map of the transactions
   246  
   247  	costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
   248  	gascap  *big.Int // Gas limit of the highest spending transaction (reset only if exceeds block limit)
   249  }
   250  
   251  // newTxList create a new transaction list for maintaining nonce-indexable fast,
   252  // gapped, sortable transaction lists.
   253  func newTxList(strict bool) *txList {
   254  	return &txList{
   255  		strict:  strict,
   256  		txs:     newTxSortedMap(),
   257  		costcap: new(big.Int),
   258  		gascap:  new(big.Int),
   259  	}
   260  }
   261  
   262  // Overlaps returns whether the transaction specified has the same nonce as one
   263  // already contained within the list.
   264  func (l *txList) Overlaps(tx *types.Transaction) bool {
   265  	return l.txs.Get(tx.Nonce()) != nil
   266  }
   267  
   268  // Add tries to insert a new transaction into the list, returning whether the
   269  // transaction was accepted, and if yes, any previous transaction it replaced.
   270  //
   271  // If the new transaction is accepted into the list, the lists' cost and gas
   272  // thresholds are also potentially updated.
   273  func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
   274  	// If there's an older better transaction, abort
   275  	old := l.txs.Get(tx.Nonce())
   276  	if old != nil {
   277  		threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100))
   278  		// Have to ensure that the new gas price is higher than the old gas
   279  		// price as well as checking the percentage threshold to ensure that
   280  		// this is accurate for low (Wei-level) gas price replacements
   281  		if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 {
   282  			return false, nil
   283  		}
   284  	}
   285  	// Otherwise overwrite the old transaction with the current one
   286  	l.txs.Put(tx)
   287  	if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
   288  		l.costcap = cost
   289  	}
   290  	if gas := tx.Gas(); l.gascap.Cmp(gas) < 0 {
   291  		l.gascap = gas
   292  	}
   293  	return true, old
   294  }
   295  
   296  // Forward removes all transactions from the list with a nonce lower than the
   297  // provided threshold. Every removed transaction is returned for any post-removal
   298  // maintenance.
   299  func (l *txList) Forward(threshold uint64) types.Transactions {
   300  	return l.txs.Forward(threshold)
   301  }
   302  
   303  // Filter removes all transactions from the list with a cost or gas limit higher
   304  // than the provided thresholds. Every removed transaction is returned for any
   305  // post-removal maintenance. Strict-mode invalidated transactions are also
   306  // returned.
   307  //
   308  // This method uses the cached costcap and gascap to quickly decide if there's even
   309  // a point in calculating all the costs or if the balance covers all. If the threshold
   310  // is lower than the costgas cap, the caps will be reset to a new high after removing
   311  // the newly invalidated transactions.
   312  func (l *txList) Filter(costLimit, gasLimit *big.Int) (types.Transactions, types.Transactions) {
   313  	// If all transactions are below the threshold, short circuit
   314  	if l.costcap.Cmp(costLimit) <= 0 && l.gascap.Cmp(gasLimit) <= 0 {
   315  		return nil, nil
   316  	}
   317  	l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds
   318  	l.gascap = new(big.Int).Set(gasLimit)
   319  
   320  	// Filter out all the transactions above the account's funds
   321  	removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(costLimit) > 0 || tx.Gas().Cmp(gasLimit) > 0 })
   322  
   323  	// If the list was strict, filter anything above the lowest nonce
   324  	var invalids types.Transactions
   325  
   326  	if l.strict && len(removed) > 0 {
   327  		lowest := uint64(math.MaxUint64)
   328  		for _, tx := range removed {
   329  			if nonce := tx.Nonce(); lowest > nonce {
   330  				lowest = nonce
   331  			}
   332  		}
   333  		invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
   334  	}
   335  	return removed, invalids
   336  }
   337  
   338  // Cap places a hard limit on the number of items, returning all transactions
   339  // exceeding that limit.
   340  func (l *txList) Cap(threshold int) types.Transactions {
   341  	return l.txs.Cap(threshold)
   342  }
   343  
   344  // Remove deletes a transaction from the maintained list, returning whether the
   345  // transaction was found, and also returning any transaction invalidated due to
   346  // the deletion (strict mode only).
   347  func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
   348  	// Remove the transaction from the set
   349  	nonce := tx.Nonce()
   350  	if removed := l.txs.Remove(nonce); !removed {
   351  		return false, nil
   352  	}
   353  	// In strict mode, filter out non-executable transactions
   354  	if l.strict {
   355  		return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce })
   356  	}
   357  	return true, nil
   358  }
   359  
   360  // Ready retrieves a sequentially increasing list of transactions starting at the
   361  // provided nonce that is ready for processing. The returned transactions will be
   362  // removed from the list.
   363  //
   364  // Note, all transactions with nonces lower than start will also be returned to
   365  // prevent getting into and invalid state. This is not something that should ever
   366  // happen but better to be self correcting than failing!
   367  func (l *txList) Ready(start uint64) types.Transactions {
   368  	return l.txs.Ready(start)
   369  }
   370  
   371  // Len returns the length of the transaction list.
   372  func (l *txList) Len() int {
   373  	return l.txs.Len()
   374  }
   375  
   376  // Empty returns whether the list of transactions is empty or not.
   377  func (l *txList) Empty() bool {
   378  	return l.Len() == 0
   379  }
   380  
   381  // Flatten creates a nonce-sorted slice of transactions based on the loosely
   382  // sorted internal representation. The result of the sorting is cached in case
   383  // it's requested again before any modifications are made to the contents.
   384  func (l *txList) Flatten() types.Transactions {
   385  	return l.txs.Flatten()
   386  }
   387  
   388  // priceHeap is a heap.Interface implementation over transactions for retrieving
   389  // price-sorted transactions to discard when the pool fills up.
   390  type priceHeap []*types.Transaction
   391  
   392  func (h priceHeap) Len() int           { return len(h) }
   393  func (h priceHeap) Less(i, j int) bool { return h[i].GasPrice().Cmp(h[j].GasPrice()) < 0 }
   394  func (h priceHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
   395  
   396  func (h *priceHeap) Push(x interface{}) {
   397  	*h = append(*h, x.(*types.Transaction))
   398  }
   399  
   400  func (h *priceHeap) Pop() interface{} {
   401  	old := *h
   402  	n := len(old)
   403  	x := old[n-1]
   404  	*h = old[0 : n-1]
   405  	return x
   406  }
   407  
   408  // txPricedList is a price-sorted heap to allow operating on transactions pool
   409  // contents in a price-incrementing way.
   410  type txPricedList struct {
   411  	all    *map[common.Hash]*types.Transaction // Pointer to the map of all transactions
   412  	items  *priceHeap                          // Heap of prices of all the stored transactions
   413  	stales int                                 // Number of stale price points to (re-heap trigger)
   414  }
   415  
   416  // newTxPricedList creates a new price-sorted transaction heap.
   417  func newTxPricedList(all *map[common.Hash]*types.Transaction) *txPricedList {
   418  	return &txPricedList{
   419  		all:   all,
   420  		items: new(priceHeap),
   421  	}
   422  }
   423  
   424  // Put inserts a new transaction into the heap.
   425  func (l *txPricedList) Put(tx *types.Transaction) {
   426  	heap.Push(l.items, tx)
   427  }
   428  
   429  // Removed notifies the prices transaction list that an old transaction dropped
   430  // from the pool. The list will just keep a counter of stale objects and update
   431  // the heap if a large enough ratio of transactions go stale.
   432  func (l *txPricedList) Removed() {
   433  	// Bump the stale counter, but exit if still too low (< 25%)
   434  	l.stales++
   435  	if l.stales <= len(*l.items)/4 {
   436  		return
   437  	}
   438  	// Seems we've reached a critical number of stale transactions, reheap
   439  	reheap := make(priceHeap, 0, len(*l.all))
   440  
   441  	l.stales, l.items = 0, &reheap
   442  	for _, tx := range *l.all {
   443  		*l.items = append(*l.items, tx)
   444  	}
   445  	heap.Init(l.items)
   446  }
   447  
   448  // Cap finds all the transactions below the given price threshold, drops them
   449  // from the priced list and returs them for further removal from the entire pool.
   450  func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transactions {
   451  	drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop
   452  	save := make(types.Transactions, 0, 64)  // Local underpriced transactions to keep
   453  
   454  	for len(*l.items) > 0 {
   455  		// Discard stale transactions if found during cleanup
   456  		tx := heap.Pop(l.items).(*types.Transaction)
   457  		if _, ok := (*l.all)[tx.Hash()]; !ok {
   458  			l.stales--
   459  			continue
   460  		}
   461  		// Stop the discards if we've reached the threshold
   462  		if tx.GasPrice().Cmp(threshold) >= 0 {
   463  			save = append(save, tx)
   464  			break
   465  		}
   466  		// Non stale transaction found, discard unless local
   467  		if local.containsTx(tx) {
   468  			save = append(save, tx)
   469  		} else {
   470  			drop = append(drop, tx)
   471  		}
   472  	}
   473  	for _, tx := range save {
   474  		heap.Push(l.items, tx)
   475  	}
   476  	return drop
   477  }
   478  
   479  // Underpriced checks whether a transaction is cheaper than (or as cheap as) the
   480  // lowest priced transaction currently being tracked.
   481  func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) bool {
   482  	// Local transactions cannot be underpriced
   483  	if local.containsTx(tx) {
   484  		return false
   485  	}
   486  	// Discard stale price points if found at the heap start
   487  	for len(*l.items) > 0 {
   488  		head := []*types.Transaction(*l.items)[0]
   489  		if _, ok := (*l.all)[head.Hash()]; !ok {
   490  			l.stales--
   491  			heap.Pop(l.items)
   492  			continue
   493  		}
   494  		break
   495  	}
   496  	// Check if the transaction is underpriced or not
   497  	if len(*l.items) == 0 {
   498  		log.Error("Pricing query for empty pool") // This cannot happen, print to catch programming errors
   499  		return false
   500  	}
   501  	cheapest := []*types.Transaction(*l.items)[0]
   502  	return cheapest.GasPrice().Cmp(tx.GasPrice()) >= 0
   503  }
   504  
   505  // Discard finds a number of most underpriced transactions, removes them from the
   506  // priced list and returns them for further removal from the entire pool.
   507  func (l *txPricedList) Discard(count int, local *accountSet) types.Transactions {
   508  	drop := make(types.Transactions, 0, count) // Remote underpriced transactions to drop
   509  	save := make(types.Transactions, 0, 64)    // Local underpriced transactions to keep
   510  
   511  	for len(*l.items) > 0 && count > 0 {
   512  		// Discard stale transactions if found during cleanup
   513  		tx := heap.Pop(l.items).(*types.Transaction)
   514  		if _, ok := (*l.all)[tx.Hash()]; !ok {
   515  			l.stales--
   516  			continue
   517  		}
   518  		// Non stale transaction found, discard unless local
   519  		if local.containsTx(tx) {
   520  			save = append(save, tx)
   521  		} else {
   522  			drop = append(drop, tx)
   523  			count--
   524  		}
   525  	}
   526  	for _, tx := range save {
   527  		heap.Push(l.items, tx)
   528  	}
   529  	return drop
   530  }