github.com/number571/tendermint@v0.34.11-gost/internal/mempool/v1/tx.go (about)

     1  package v1
     2  
     3  import (
     4  	"sort"
     5  	"time"
     6  
     7  	"github.com/number571/tendermint/internal/libs/clist"
     8  	tmsync "github.com/number571/tendermint/internal/libs/sync"
     9  	"github.com/number571/tendermint/internal/mempool"
    10  	"github.com/number571/tendermint/types"
    11  )
    12  
    13  // WrappedTx defines a wrapper around a raw transaction with additional metadata
    14  // that is used for indexing.
    15  type WrappedTx struct {
    16  	// tx represents the raw binary transaction data
    17  	tx types.Tx
    18  
    19  	// hash defines the transaction hash and the primary key used in the mempool
    20  	hash [mempool.TxKeySize]byte
    21  
    22  	// height defines the height at which the transaction was validated at
    23  	height int64
    24  
    25  	// gasWanted defines the amount of gas the transaction sender requires
    26  	gasWanted int64
    27  
    28  	// priority defines the transaction's priority as specified by the application
    29  	// in the ResponseCheckTx response.
    30  	priority int64
    31  
    32  	// sender defines the transaction's sender as specified by the application in
    33  	// the ResponseCheckTx response.
    34  	sender string
    35  
    36  	// timestamp is the time at which the node first received the transaction from
    37  	// a peer. It is used as a second dimension is prioritizing transactions when
    38  	// two transactions have the same priority.
    39  	timestamp time.Time
    40  
    41  	// peers records a mapping of all peers that sent a given transaction
    42  	peers map[uint16]struct{}
    43  
    44  	// heapIndex defines the index of the item in the heap
    45  	heapIndex int
    46  
    47  	// gossipEl references the linked-list element in the gossip index
    48  	gossipEl *clist.CElement
    49  
    50  	// removed marks the transaction as removed from the mempool. This is set
    51  	// during RemoveTx and is needed due to the fact that a given existing
    52  	// transaction in the mempool can be evicted when it is simultaneously having
    53  	// a reCheckTx callback executed.
    54  	removed bool
    55  }
    56  
    57  func (wtx *WrappedTx) Size() int {
    58  	return len(wtx.tx)
    59  }
    60  
    61  // TxStore implements a thread-safe mapping of valid transaction(s).
    62  //
    63  // NOTE:
    64  // - Concurrent read-only access to a *WrappedTx object is OK. However, mutative
    65  //   access is not allowed. Regardless, it is not expected for the mempool to
    66  //   need mutative access.
    67  type TxStore struct {
    68  	mtx       tmsync.RWMutex
    69  	hashTxs   map[[mempool.TxKeySize]byte]*WrappedTx // primary index
    70  	senderTxs map[string]*WrappedTx                  // sender is defined by the ABCI application
    71  }
    72  
    73  func NewTxStore() *TxStore {
    74  	return &TxStore{
    75  		senderTxs: make(map[string]*WrappedTx),
    76  		hashTxs:   make(map[[mempool.TxKeySize]byte]*WrappedTx),
    77  	}
    78  }
    79  
    80  // Size returns the total number of transactions in the store.
    81  func (txs *TxStore) Size() int {
    82  	txs.mtx.RLock()
    83  	defer txs.mtx.RUnlock()
    84  
    85  	return len(txs.hashTxs)
    86  }
    87  
    88  // GetAllTxs returns all the transactions currently in the store.
    89  func (txs *TxStore) GetAllTxs() []*WrappedTx {
    90  	txs.mtx.RLock()
    91  	defer txs.mtx.RUnlock()
    92  
    93  	wTxs := make([]*WrappedTx, len(txs.hashTxs))
    94  	i := 0
    95  	for _, wtx := range txs.hashTxs {
    96  		wTxs[i] = wtx
    97  		i++
    98  	}
    99  
   100  	return wTxs
   101  }
   102  
   103  // GetTxBySender returns a *WrappedTx by the transaction's sender property
   104  // defined by the ABCI application.
   105  func (txs *TxStore) GetTxBySender(sender string) *WrappedTx {
   106  	txs.mtx.RLock()
   107  	defer txs.mtx.RUnlock()
   108  
   109  	return txs.senderTxs[sender]
   110  }
   111  
   112  // GetTxByHash returns a *WrappedTx by the transaction's hash.
   113  func (txs *TxStore) GetTxByHash(hash [mempool.TxKeySize]byte) *WrappedTx {
   114  	txs.mtx.RLock()
   115  	defer txs.mtx.RUnlock()
   116  
   117  	return txs.hashTxs[hash]
   118  }
   119  
   120  // IsTxRemoved returns true if a transaction by hash is marked as removed and
   121  // false otherwise.
   122  func (txs *TxStore) IsTxRemoved(hash [mempool.TxKeySize]byte) bool {
   123  	txs.mtx.RLock()
   124  	defer txs.mtx.RUnlock()
   125  
   126  	wtx, ok := txs.hashTxs[hash]
   127  	if ok {
   128  		return wtx.removed
   129  	}
   130  
   131  	return false
   132  }
   133  
   134  // SetTx stores a *WrappedTx by it's hash. If the transaction also contains a
   135  // non-empty sender, we additionally store the transaction by the sender as
   136  // defined by the ABCI application.
   137  func (txs *TxStore) SetTx(wtx *WrappedTx) {
   138  	txs.mtx.Lock()
   139  	defer txs.mtx.Unlock()
   140  
   141  	if len(wtx.sender) > 0 {
   142  		txs.senderTxs[wtx.sender] = wtx
   143  	}
   144  
   145  	txs.hashTxs[mempool.TxKey(wtx.tx)] = wtx
   146  }
   147  
   148  // RemoveTx removes a *WrappedTx from the transaction store. It deletes all
   149  // indexes of the transaction.
   150  func (txs *TxStore) RemoveTx(wtx *WrappedTx) {
   151  	txs.mtx.Lock()
   152  	defer txs.mtx.Unlock()
   153  
   154  	if len(wtx.sender) > 0 {
   155  		delete(txs.senderTxs, wtx.sender)
   156  	}
   157  
   158  	delete(txs.hashTxs, mempool.TxKey(wtx.tx))
   159  	wtx.removed = true
   160  }
   161  
   162  // TxHasPeer returns true if a transaction by hash has a given peer ID and false
   163  // otherwise. If the transaction does not exist, false is returned.
   164  func (txs *TxStore) TxHasPeer(hash [mempool.TxKeySize]byte, peerID uint16) bool {
   165  	txs.mtx.RLock()
   166  	defer txs.mtx.RUnlock()
   167  
   168  	wtx := txs.hashTxs[hash]
   169  	if wtx == nil {
   170  		return false
   171  	}
   172  
   173  	_, ok := wtx.peers[peerID]
   174  	return ok
   175  }
   176  
   177  // GetOrSetPeerByTxHash looks up a WrappedTx by transaction hash and adds the
   178  // given peerID to the WrappedTx's set of peers that sent us this transaction.
   179  // We return true if we've already recorded the given peer for this transaction
   180  // and false otherwise. If the transaction does not exist by hash, we return
   181  // (nil, false).
   182  func (txs *TxStore) GetOrSetPeerByTxHash(hash [mempool.TxKeySize]byte, peerID uint16) (*WrappedTx, bool) {
   183  	txs.mtx.Lock()
   184  	defer txs.mtx.Unlock()
   185  
   186  	wtx := txs.hashTxs[hash]
   187  	if wtx == nil {
   188  		return nil, false
   189  	}
   190  
   191  	if wtx.peers == nil {
   192  		wtx.peers = make(map[uint16]struct{})
   193  	}
   194  
   195  	if _, ok := wtx.peers[peerID]; ok {
   196  		return wtx, true
   197  	}
   198  
   199  	wtx.peers[peerID] = struct{}{}
   200  	return wtx, false
   201  }
   202  
   203  // WrappedTxList implements a thread-safe list of *WrappedTx objects that can be
   204  // used to build generic transaction indexes in the mempool. It accepts a
   205  // comparator function, less(a, b *WrappedTx) bool, that compares two WrappedTx
   206  // references which is used during Insert in order to determine sorted order. If
   207  // less returns true, a <= b.
   208  type WrappedTxList struct {
   209  	mtx  tmsync.RWMutex
   210  	txs  []*WrappedTx
   211  	less func(*WrappedTx, *WrappedTx) bool
   212  }
   213  
   214  func NewWrappedTxList(less func(*WrappedTx, *WrappedTx) bool) *WrappedTxList {
   215  	return &WrappedTxList{
   216  		txs:  make([]*WrappedTx, 0),
   217  		less: less,
   218  	}
   219  }
   220  
   221  // Size returns the number of WrappedTx objects in the list.
   222  func (wtl *WrappedTxList) Size() int {
   223  	wtl.mtx.RLock()
   224  	defer wtl.mtx.RUnlock()
   225  
   226  	return len(wtl.txs)
   227  }
   228  
   229  // Reset resets the list of transactions to an empty list.
   230  func (wtl *WrappedTxList) Reset() {
   231  	wtl.mtx.Lock()
   232  	defer wtl.mtx.Unlock()
   233  
   234  	wtl.txs = make([]*WrappedTx, 0)
   235  }
   236  
   237  // Insert inserts a WrappedTx reference into the sorted list based on the list's
   238  // comparator function.
   239  func (wtl *WrappedTxList) Insert(wtx *WrappedTx) {
   240  	wtl.mtx.Lock()
   241  	defer wtl.mtx.Unlock()
   242  
   243  	i := sort.Search(len(wtl.txs), func(i int) bool {
   244  		return wtl.less(wtl.txs[i], wtx)
   245  	})
   246  
   247  	if i == len(wtl.txs) {
   248  		// insert at the end
   249  		wtl.txs = append(wtl.txs, wtx)
   250  		return
   251  	}
   252  
   253  	// Make space for the inserted element by shifting values at the insertion
   254  	// index up one index.
   255  	//
   256  	// NOTE: The call to append does not allocate memory when cap(wtl.txs) > len(wtl.txs).
   257  	wtl.txs = append(wtl.txs[:i+1], wtl.txs[i:]...)
   258  	wtl.txs[i] = wtx
   259  }
   260  
   261  // Remove attempts to remove a WrappedTx from the sorted list.
   262  func (wtl *WrappedTxList) Remove(wtx *WrappedTx) {
   263  	wtl.mtx.Lock()
   264  	defer wtl.mtx.Unlock()
   265  
   266  	i := sort.Search(len(wtl.txs), func(i int) bool {
   267  		return wtl.less(wtl.txs[i], wtx)
   268  	})
   269  
   270  	// Since the list is sorted, we evaluate all elements starting at i. Note, if
   271  	// the element does not exist, we may potentially evaluate the entire remainder
   272  	// of the list. However, a caller should not be expected to call Remove with a
   273  	// non-existing element.
   274  	for i < len(wtl.txs) {
   275  		if wtl.txs[i] == wtx {
   276  			wtl.txs = append(wtl.txs[:i], wtl.txs[i+1:]...)
   277  			return
   278  		}
   279  
   280  		i++
   281  	}
   282  }