github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/core/tx_list.go (about)

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