You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
7.4 KiB

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