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.

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