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.

293 lines
7.7 KiB

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