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.

844 lines
25 KiB

8 years ago
mempool no gossip back (#2778) Closes #1798 This is done by making every mempool tx maintain a list of peers who its received the tx from. Instead of using the 20byte peer ID, it instead uses a local map from peerID to uint16 counter, so every peer adds 2 bytes. (Word aligned to probably make it 8 bytes) This also required resetting the callback function on every CheckTx. This likely has performance ramifications for instruction caching. The actual setting operation isn't costly with the removal of defers in this PR. * Make the mempool not gossip txs back to peers its received it from * Fix adversarial memleak * Don't break interface * Update changelog * Forgot to add a mtx * forgot a mutex * Update mempool/reactor.go Co-Authored-By: ValarDragon <ValarDragon@users.noreply.github.com> * Update mempool/mempool.go Co-Authored-By: ValarDragon <ValarDragon@users.noreply.github.com> * Use unknown peer ID Co-Authored-By: ValarDragon <ValarDragon@users.noreply.github.com> * fix compilation * use next wait chan logic when skipping * Minor fixes * Add TxInfo * Add reverse map * Make activeID's auto-reserve 0 * 0 -> UnknownPeerID Co-Authored-By: ValarDragon <ValarDragon@users.noreply.github.com> * Switch to making the normal case set a callback on the reqres object The recheck case is still done via the global callback, and stats are also set via global callback * fix merge conflict * Addres comments * Add cache tests * add cache tests * minor fixes * update metrics in reqResCb and reformat code * goimport -w mempool/reactor.go * mempool: update memTx senders I had to introduce txsMap for quick mempoolTx lookups. * change senders type from []uint16 to sync.Map Fixes DATA RACE: ``` Read at 0x00c0013fcd3a by goroutine 183: github.com/tendermint/tendermint/mempool.(*MempoolReactor).broadcastTxRoutine() /go/src/github.com/tendermint/tendermint/mempool/reactor.go:195 +0x3c7 Previous write at 0x00c0013fcd3a by D[2019-02-27|10:10:49.058] Read PacketMsg switch=3 peer=35bc1e3558c182927b31987eeff3feb3d58a0fc5@127.0.0.1 :46552 conn=MConn{pipe} packet="PacketMsg{30:2B06579D0A143EB78F3D3299DE8213A51D4E11FB05ACE4D6A14F T:1}" goroutine 190: github.com/tendermint/tendermint/mempool.(*Mempool).CheckTxWithInfo() /go/src/github.com/tendermint/tendermint/mempool/mempool.go:387 +0xdc1 github.com/tendermint/tendermint/mempool.(*MempoolReactor).Receive() /go/src/github.com/tendermint/tendermint/mempool/reactor.go:134 +0xb04 github.com/tendermint/tendermint/p2p.createMConnection.func1() /go/src/github.com/tendermint/tendermint/p2p/peer.go:374 +0x25b github.com/tendermint/tendermint/p2p/conn.(*MConnection).recvRoutine() /go/src/github.com/tendermint/tendermint/p2p/conn/connection.go:599 +0xcce Goroutine 183 (running) created at: D[2019-02-27|10:10:49.058] Send switch=2 peer=1efafad5443abeea4b7a8155218e4369525d987e@127.0.0.1:46193 channel=48 conn=MConn{pipe} m sgBytes=2B06579D0A146194480ADAE00C2836ED7125FEE65C1D9DD51049 github.com/tendermint/tendermint/mempool.(*MempoolReactor).AddPeer() /go/src/github.com/tendermint/tendermint/mempool/reactor.go:105 +0x1b1 github.com/tendermint/tendermint/p2p.(*Switch).startInitPeer() /go/src/github.com/tendermint/tendermint/p2p/switch.go:683 +0x13b github.com/tendermint/tendermint/p2p.(*Switch).addPeer() /go/src/github.com/tendermint/tendermint/p2p/switch.go:650 +0x585 github.com/tendermint/tendermint/p2p.(*Switch).addPeerWithConnection() /go/src/github.com/tendermint/tendermint/p2p/test_util.go:145 +0x939 github.com/tendermint/tendermint/p2p.Connect2Switches.func2() /go/src/github.com/tendermint/tendermint/p2p/test_util.go:109 +0x50 I[2019-02-27|10:10:49.058] Added good transaction validator=0 tx=43B4D1F0F03460BD262835C4AA560DB860CFBBE85BD02386D83DAC38C67B3AD7 res="&{CheckTx:gas_w anted:1 }" height=0 total=375 Goroutine 190 (running) created at: github.com/tendermint/tendermint/p2p/conn.(*MConnection).OnStart() /go/src/github.com/tendermint/tendermint/p2p/conn/connection.go:210 +0x313 github.com/tendermint/tendermint/libs/common.(*BaseService).Start() /go/src/github.com/tendermint/tendermint/libs/common/service.go:139 +0x4df github.com/tendermint/tendermint/p2p.(*peer).OnStart() /go/src/github.com/tendermint/tendermint/p2p/peer.go:179 +0x56 github.com/tendermint/tendermint/libs/common.(*BaseService).Start() /go/src/github.com/tendermint/tendermint/libs/common/service.go:139 +0x4df github.com/tendermint/tendermint/p2p.(*peer).Start() <autogenerated>:1 +0x43 github.com/tendermint/tendermint/p2p.(*Switch).startInitPeer() ``` * explain the choice of a map DS for senders * extract ids pool/mapper to a separate struct * fix literal copies lock value from senders: sync.Map contains sync.Mutex * use sync.Map#LoadOrStore instead of Load * fixes after Ismail's review * rename resCbNormal to resCbFirstTime
6 years ago
9 years ago
  1. package mempool
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "sync"
  8. "sync/atomic"
  9. "time"
  10. abciclient "github.com/tendermint/tendermint/abci/client"
  11. abci "github.com/tendermint/tendermint/abci/types"
  12. "github.com/tendermint/tendermint/config"
  13. "github.com/tendermint/tendermint/internal/libs/clist"
  14. "github.com/tendermint/tendermint/libs/log"
  15. tmmath "github.com/tendermint/tendermint/libs/math"
  16. "github.com/tendermint/tendermint/types"
  17. )
  18. var _ Mempool = (*TxMempool)(nil)
  19. // TxMempoolOption sets an optional parameter on the TxMempool.
  20. type TxMempoolOption func(*TxMempool)
  21. // TxMempool defines a prioritized mempool data structure used by the v1 mempool
  22. // reactor. It keeps a thread-safe priority queue of transactions that is used
  23. // when a block proposer constructs a block and a thread-safe linked-list that
  24. // is used to gossip transactions to peers in a FIFO manner.
  25. type TxMempool struct {
  26. logger log.Logger
  27. metrics *Metrics
  28. config *config.MempoolConfig
  29. proxyAppConn abciclient.Client
  30. // txsAvailable fires once for each height when the mempool is not empty
  31. txsAvailable chan struct{}
  32. notifiedTxsAvailable bool
  33. // height defines the last block height process during Update()
  34. height int64
  35. // sizeBytes defines the total size of the mempool (sum of all tx bytes)
  36. sizeBytes int64
  37. // cache defines a fixed-size cache of already seen transactions as this
  38. // reduces pressure on the proxyApp.
  39. cache TxCache
  40. // txStore defines the main storage of valid transactions. Indexes are built
  41. // on top of this store.
  42. txStore *TxStore
  43. // gossipIndex defines the gossiping index of valid transactions via a
  44. // thread-safe linked-list. We also use the gossip index as a cursor for
  45. // rechecking transactions already in the mempool.
  46. gossipIndex *clist.CList
  47. // recheckCursor and recheckEnd are used as cursors based on the gossip index
  48. // to recheck transactions that are already in the mempool. Iteration is not
  49. // thread-safe and transaction may be mutated in serial order.
  50. //
  51. // XXX/TODO: It might be somewhat of a codesmell to use the gossip index for
  52. // iterator and cursor management when rechecking transactions. If the gossip
  53. // index changes or is removed in a future refactor, this will have to be
  54. // refactored. Instead, we should consider just keeping a slice of a snapshot
  55. // of the mempool's current transactions during Update and an integer cursor
  56. // into that slice. This, however, requires additional O(n) space complexity.
  57. recheckCursor *clist.CElement // next expected response
  58. recheckEnd *clist.CElement // re-checking stops here
  59. // priorityIndex defines the priority index of valid transactions via a
  60. // thread-safe priority queue.
  61. priorityIndex *TxPriorityQueue
  62. // heightIndex defines a height-based, in ascending order, transaction index.
  63. // i.e. older transactions are first.
  64. heightIndex *WrappedTxList
  65. // timestampIndex defines a timestamp-based, in ascending order, transaction
  66. // index. i.e. older transactions are first.
  67. timestampIndex *WrappedTxList
  68. // A read/write lock is used to safe guard updates, insertions and deletions
  69. // from the mempool. A read-lock is implicitly acquired when executing CheckTx,
  70. // however, a caller must explicitly grab a write-lock via Lock when updating
  71. // the mempool via Update().
  72. mtx sync.RWMutex
  73. preCheck PreCheckFunc
  74. postCheck PostCheckFunc
  75. }
  76. func NewTxMempool(
  77. logger log.Logger,
  78. cfg *config.MempoolConfig,
  79. proxyAppConn abciclient.Client,
  80. height int64,
  81. options ...TxMempoolOption,
  82. ) *TxMempool {
  83. txmp := &TxMempool{
  84. logger: logger,
  85. config: cfg,
  86. proxyAppConn: proxyAppConn,
  87. height: height,
  88. cache: NopTxCache{},
  89. metrics: NopMetrics(),
  90. txStore: NewTxStore(),
  91. gossipIndex: clist.New(),
  92. priorityIndex: NewTxPriorityQueue(),
  93. heightIndex: NewWrappedTxList(func(wtx1, wtx2 *WrappedTx) bool {
  94. return wtx1.height >= wtx2.height
  95. }),
  96. timestampIndex: NewWrappedTxList(func(wtx1, wtx2 *WrappedTx) bool {
  97. return wtx1.timestamp.After(wtx2.timestamp) || wtx1.timestamp.Equal(wtx2.timestamp)
  98. }),
  99. }
  100. if cfg.CacheSize > 0 {
  101. txmp.cache = NewLRUTxCache(cfg.CacheSize)
  102. }
  103. for _, opt := range options {
  104. opt(txmp)
  105. }
  106. return txmp
  107. }
  108. // WithPreCheck sets a filter for the mempool to reject a transaction if f(tx)
  109. // returns an error. This is executed before CheckTx. It only applies to the
  110. // first created block. After that, Update() overwrites the existing value.
  111. func WithPreCheck(f PreCheckFunc) TxMempoolOption {
  112. return func(txmp *TxMempool) { txmp.preCheck = f }
  113. }
  114. // WithPostCheck sets a filter for the mempool to reject a transaction if
  115. // f(tx, resp) returns an error. This is executed after CheckTx. It only applies
  116. // to the first created block. After that, Update overwrites the existing value.
  117. func WithPostCheck(f PostCheckFunc) TxMempoolOption {
  118. return func(txmp *TxMempool) { txmp.postCheck = f }
  119. }
  120. // WithMetrics sets the mempool's metrics collector.
  121. func WithMetrics(metrics *Metrics) TxMempoolOption {
  122. return func(txmp *TxMempool) { txmp.metrics = metrics }
  123. }
  124. // Lock obtains a write-lock on the mempool. A caller must be sure to explicitly
  125. // release the lock when finished.
  126. func (txmp *TxMempool) Lock() {
  127. txmp.mtx.Lock()
  128. }
  129. // Unlock releases a write-lock on the mempool.
  130. func (txmp *TxMempool) Unlock() {
  131. txmp.mtx.Unlock()
  132. }
  133. // Size returns the number of valid transactions in the mempool. It is
  134. // thread-safe.
  135. func (txmp *TxMempool) Size() int {
  136. return txmp.txStore.Size()
  137. }
  138. // SizeBytes return the total sum in bytes of all the valid transactions in the
  139. // mempool. It is thread-safe.
  140. func (txmp *TxMempool) SizeBytes() int64 {
  141. return atomic.LoadInt64(&txmp.sizeBytes)
  142. }
  143. // FlushAppConn executes FlushSync on the mempool's proxyAppConn.
  144. //
  145. // NOTE: The caller must obtain a write-lock prior to execution.
  146. func (txmp *TxMempool) FlushAppConn(ctx context.Context) error {
  147. return txmp.proxyAppConn.Flush(ctx)
  148. }
  149. // WaitForNextTx returns a blocking channel that will be closed when the next
  150. // valid transaction is available to gossip. It is thread-safe.
  151. func (txmp *TxMempool) WaitForNextTx() <-chan struct{} {
  152. return txmp.gossipIndex.WaitChan()
  153. }
  154. // NextGossipTx returns the next valid transaction to gossip. A caller must wait
  155. // for WaitForNextTx to signal a transaction is available to gossip first. It is
  156. // thread-safe.
  157. func (txmp *TxMempool) NextGossipTx() *clist.CElement {
  158. return txmp.gossipIndex.Front()
  159. }
  160. // EnableTxsAvailable enables the mempool to trigger events when transactions
  161. // are available on a block by block basis.
  162. func (txmp *TxMempool) EnableTxsAvailable() {
  163. txmp.mtx.Lock()
  164. defer txmp.mtx.Unlock()
  165. txmp.txsAvailable = make(chan struct{}, 1)
  166. }
  167. // TxsAvailable returns a channel which fires once for every height, and only
  168. // when transactions are available in the mempool. It is thread-safe.
  169. func (txmp *TxMempool) TxsAvailable() <-chan struct{} {
  170. return txmp.txsAvailable
  171. }
  172. // CheckTx executes the ABCI CheckTx method for a given transaction.
  173. // It acquires a read-lock and attempts to execute the application's
  174. // CheckTx ABCI method synchronously. We return an error if any of
  175. // the following happen:
  176. //
  177. // - The CheckTx execution fails.
  178. // - The transaction already exists in the cache and we've already received the
  179. // transaction from the peer. Otherwise, if it solely exists in the cache, we
  180. // return nil.
  181. // - The transaction size exceeds the maximum transaction size as defined by the
  182. // configuration provided to the mempool.
  183. // - The transaction fails Pre-Check (if it is defined).
  184. // - The proxyAppConn fails, e.g. the buffer is full.
  185. //
  186. // If the mempool is full, we still execute CheckTx and attempt to find a lower
  187. // priority transaction to evict. If such a transaction exists, we remove the
  188. // lower priority transaction and add the new one with higher priority.
  189. //
  190. // NOTE:
  191. // - The applications' CheckTx implementation may panic.
  192. // - The caller is not to explicitly require any locks for executing CheckTx.
  193. func (txmp *TxMempool) CheckTx(
  194. ctx context.Context,
  195. tx types.Tx,
  196. cb func(*abci.ResponseCheckTx),
  197. txInfo TxInfo,
  198. ) error {
  199. txmp.mtx.RLock()
  200. defer txmp.mtx.RUnlock()
  201. if txSize := len(tx); txSize > txmp.config.MaxTxBytes {
  202. return types.ErrTxTooLarge{
  203. Max: txmp.config.MaxTxBytes,
  204. Actual: txSize,
  205. }
  206. }
  207. if txmp.preCheck != nil {
  208. if err := txmp.preCheck(tx); err != nil {
  209. return types.ErrPreCheck{Reason: err}
  210. }
  211. }
  212. if err := txmp.proxyAppConn.Error(); err != nil {
  213. return err
  214. }
  215. txHash := tx.Key()
  216. // We add the transaction to the mempool's cache and if the
  217. // transaction is already present in the cache, i.e. false is returned, then we
  218. // check if we've seen this transaction and error if we have.
  219. if !txmp.cache.Push(tx) {
  220. txmp.txStore.GetOrSetPeerByTxHash(txHash, txInfo.SenderID)
  221. return types.ErrTxInCache
  222. }
  223. res, err := txmp.proxyAppConn.CheckTx(ctx, abci.RequestCheckTx{Tx: tx})
  224. if err != nil {
  225. txmp.cache.Remove(tx)
  226. return err
  227. }
  228. if txmp.recheckCursor != nil {
  229. return errors.New("recheck cursor is non-nil")
  230. }
  231. wtx := &WrappedTx{
  232. tx: tx,
  233. hash: txHash,
  234. timestamp: time.Now().UTC(),
  235. height: txmp.height,
  236. }
  237. txmp.defaultTxCallback(tx, res)
  238. txmp.initTxCallback(wtx, res, txInfo)
  239. if cb != nil {
  240. cb(res)
  241. }
  242. return nil
  243. }
  244. func (txmp *TxMempool) RemoveTxByKey(txKey types.TxKey) error {
  245. txmp.Lock()
  246. defer txmp.Unlock()
  247. // remove the committed transaction from the transaction store and indexes
  248. if wtx := txmp.txStore.GetTxByHash(txKey); wtx != nil {
  249. txmp.removeTx(wtx, false)
  250. return nil
  251. }
  252. return errors.New("transaction not found")
  253. }
  254. // Flush empties the mempool. It acquires a read-lock, fetches all the
  255. // transactions currently in the transaction store and removes each transaction
  256. // from the store and all indexes and finally resets the cache.
  257. //
  258. // NOTE:
  259. // - Flushing the mempool may leave the mempool in an inconsistent state.
  260. func (txmp *TxMempool) Flush() {
  261. txmp.mtx.RLock()
  262. defer txmp.mtx.RUnlock()
  263. txmp.heightIndex.Reset()
  264. txmp.timestampIndex.Reset()
  265. for _, wtx := range txmp.txStore.GetAllTxs() {
  266. txmp.removeTx(wtx, false)
  267. }
  268. atomic.SwapInt64(&txmp.sizeBytes, 0)
  269. txmp.cache.Reset()
  270. }
  271. // ReapMaxBytesMaxGas returns a list of transactions within the provided size
  272. // and gas constraints. Transaction are retrieved in priority order.
  273. //
  274. // NOTE:
  275. // - Transactions returned are not removed from the mempool transaction
  276. // store or indexes.
  277. func (txmp *TxMempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs {
  278. txmp.mtx.RLock()
  279. defer txmp.mtx.RUnlock()
  280. var (
  281. totalGas int64
  282. totalSize int64
  283. )
  284. // wTxs contains a list of *WrappedTx retrieved from the priority queue that
  285. // need to be re-enqueued prior to returning.
  286. wTxs := make([]*WrappedTx, 0, txmp.priorityIndex.NumTxs())
  287. defer func() {
  288. for _, wtx := range wTxs {
  289. txmp.priorityIndex.PushTx(wtx)
  290. }
  291. }()
  292. txs := make([]types.Tx, 0, txmp.priorityIndex.NumTxs())
  293. for txmp.priorityIndex.NumTxs() > 0 {
  294. wtx := txmp.priorityIndex.PopTx()
  295. txs = append(txs, wtx.tx)
  296. wTxs = append(wTxs, wtx)
  297. size := types.ComputeProtoSizeForTxs([]types.Tx{wtx.tx})
  298. // Ensure we have capacity for the transaction with respect to the
  299. // transaction size.
  300. if maxBytes > -1 && totalSize+size > maxBytes {
  301. return txs[:len(txs)-1]
  302. }
  303. totalSize += size
  304. // ensure we have capacity for the transaction with respect to total gas
  305. gas := totalGas + wtx.gasWanted
  306. if maxGas > -1 && gas > maxGas {
  307. return txs[:len(txs)-1]
  308. }
  309. totalGas = gas
  310. }
  311. return txs
  312. }
  313. // ReapMaxTxs returns a list of transactions within the provided number of
  314. // transactions bound. Transaction are retrieved in priority order.
  315. //
  316. // NOTE:
  317. // - Transactions returned are not removed from the mempool transaction
  318. // store or indexes.
  319. func (txmp *TxMempool) ReapMaxTxs(max int) types.Txs {
  320. txmp.mtx.RLock()
  321. defer txmp.mtx.RUnlock()
  322. numTxs := txmp.priorityIndex.NumTxs()
  323. if max < 0 {
  324. max = numTxs
  325. }
  326. cap := tmmath.MinInt(numTxs, max)
  327. // wTxs contains a list of *WrappedTx retrieved from the priority queue that
  328. // need to be re-enqueued prior to returning.
  329. wTxs := make([]*WrappedTx, 0, cap)
  330. txs := make([]types.Tx, 0, cap)
  331. for txmp.priorityIndex.NumTxs() > 0 && len(txs) < max {
  332. wtx := txmp.priorityIndex.PopTx()
  333. txs = append(txs, wtx.tx)
  334. wTxs = append(wTxs, wtx)
  335. }
  336. for _, wtx := range wTxs {
  337. txmp.priorityIndex.PushTx(wtx)
  338. }
  339. return txs
  340. }
  341. // Update iterates over all the transactions provided by the block producer,
  342. // removes them from the cache (if applicable), and removes
  343. // the transactions from the main transaction store and associated indexes.
  344. // If there are transactions remaining in the mempool, we initiate a
  345. // re-CheckTx for them (if applicable), otherwise, we notify the caller more
  346. // transactions are available.
  347. //
  348. // NOTE:
  349. // - The caller must explicitly acquire a write-lock.
  350. func (txmp *TxMempool) Update(
  351. ctx context.Context,
  352. blockHeight int64,
  353. blockTxs types.Txs,
  354. deliverTxResponses []*abci.ResponseDeliverTx,
  355. newPreFn PreCheckFunc,
  356. newPostFn PostCheckFunc,
  357. ) error {
  358. txmp.height = blockHeight
  359. txmp.notifiedTxsAvailable = false
  360. if newPreFn != nil {
  361. txmp.preCheck = newPreFn
  362. }
  363. if newPostFn != nil {
  364. txmp.postCheck = newPostFn
  365. }
  366. for i, tx := range blockTxs {
  367. if deliverTxResponses[i].Code == abci.CodeTypeOK {
  368. // add the valid committed transaction to the cache (if missing)
  369. _ = txmp.cache.Push(tx)
  370. } else if !txmp.config.KeepInvalidTxsInCache {
  371. // allow invalid transactions to be re-submitted
  372. txmp.cache.Remove(tx)
  373. }
  374. // remove the committed transaction from the transaction store and indexes
  375. if wtx := txmp.txStore.GetTxByHash(tx.Key()); wtx != nil {
  376. txmp.removeTx(wtx, false)
  377. }
  378. }
  379. txmp.purgeExpiredTxs(blockHeight)
  380. // If there any uncommitted transactions left in the mempool, we either
  381. // initiate re-CheckTx per remaining transaction or notify that remaining
  382. // transactions are left.
  383. if txmp.Size() > 0 {
  384. if txmp.config.Recheck {
  385. txmp.logger.Debug(
  386. "executing re-CheckTx for all remaining transactions",
  387. "num_txs", txmp.Size(),
  388. "height", blockHeight,
  389. )
  390. txmp.updateReCheckTxs(ctx)
  391. } else {
  392. txmp.notifyTxsAvailable()
  393. }
  394. }
  395. txmp.metrics.Size.Set(float64(txmp.Size()))
  396. return nil
  397. }
  398. // initTxCallback is the callback invoked for a new unique transaction after CheckTx
  399. // has been executed by the ABCI application for the first time on that transaction.
  400. // CheckTx can be called again for the same transaction later when re-checking;
  401. // however, this callback will not be called.
  402. //
  403. // initTxCallback runs after the ABCI application executes CheckTx.
  404. // It runs the postCheck hook if one is defined on the mempool.
  405. // If the CheckTx response response code is not OK, or if the postCheck hook
  406. // reports an error, the transaction is rejected. Otherwise, we attempt to insert
  407. // the transaction into the mempool.
  408. //
  409. // When inserting a transaction, we first check if there is sufficient capacity.
  410. // If there is, the transaction is added to the txStore and all indexes.
  411. // Otherwise, if the mempool is full, we attempt to find a lower priority transaction
  412. // to evict in place of the new incoming transaction. If no such transaction exists,
  413. // the new incoming transaction is rejected.
  414. //
  415. // NOTE:
  416. // - An explicit lock is NOT required.
  417. func (txmp *TxMempool) initTxCallback(wtx *WrappedTx, res *abci.ResponseCheckTx, txInfo TxInfo) {
  418. var err error
  419. if txmp.postCheck != nil {
  420. err = txmp.postCheck(wtx.tx, res)
  421. }
  422. if err != nil || res.Code != abci.CodeTypeOK {
  423. // ignore bad transactions
  424. txmp.logger.Info(
  425. "rejected bad transaction",
  426. "priority", wtx.priority,
  427. "tx", fmt.Sprintf("%X", wtx.tx.Hash()),
  428. "peer_id", txInfo.SenderNodeID,
  429. "code", res.Code,
  430. "post_check_err", err,
  431. )
  432. txmp.metrics.FailedTxs.Add(1)
  433. if !txmp.config.KeepInvalidTxsInCache {
  434. txmp.cache.Remove(wtx.tx)
  435. }
  436. if err != nil {
  437. res.MempoolError = err.Error()
  438. }
  439. return
  440. }
  441. sender := res.Sender
  442. priority := res.Priority
  443. if len(sender) > 0 {
  444. if wtx := txmp.txStore.GetTxBySender(sender); wtx != nil {
  445. txmp.logger.Error(
  446. "rejected incoming good transaction; tx already exists for sender",
  447. "tx", fmt.Sprintf("%X", wtx.tx.Hash()),
  448. "sender", sender,
  449. )
  450. txmp.metrics.RejectedTxs.Add(1)
  451. return
  452. }
  453. }
  454. if err := txmp.canAddTx(wtx); err != nil {
  455. evictTxs := txmp.priorityIndex.GetEvictableTxs(
  456. priority,
  457. int64(wtx.Size()),
  458. txmp.SizeBytes(),
  459. txmp.config.MaxTxsBytes,
  460. )
  461. if len(evictTxs) == 0 {
  462. // No room for the new incoming transaction so we just remove it from
  463. // the cache.
  464. txmp.cache.Remove(wtx.tx)
  465. txmp.logger.Error(
  466. "rejected incoming good transaction; mempool full",
  467. "tx", fmt.Sprintf("%X", wtx.tx.Hash()),
  468. "err", err.Error(),
  469. )
  470. txmp.metrics.RejectedTxs.Add(1)
  471. return
  472. }
  473. // evict an existing transaction(s)
  474. //
  475. // NOTE:
  476. // - The transaction, toEvict, can be removed while a concurrent
  477. // reCheckTx callback is being executed for the same transaction.
  478. for _, toEvict := range evictTxs {
  479. txmp.removeTx(toEvict, true)
  480. txmp.logger.Debug(
  481. "evicted existing good transaction; mempool full",
  482. "old_tx", fmt.Sprintf("%X", toEvict.tx.Hash()),
  483. "old_priority", toEvict.priority,
  484. "new_tx", fmt.Sprintf("%X", wtx.tx.Hash()),
  485. "new_priority", wtx.priority,
  486. )
  487. txmp.metrics.EvictedTxs.Add(1)
  488. }
  489. }
  490. wtx.gasWanted = res.GasWanted
  491. wtx.priority = priority
  492. wtx.sender = sender
  493. wtx.peers = map[uint16]struct{}{
  494. txInfo.SenderID: {},
  495. }
  496. txmp.metrics.TxSizeBytes.Observe(float64(wtx.Size()))
  497. txmp.metrics.Size.Set(float64(txmp.Size()))
  498. txmp.insertTx(wtx)
  499. txmp.logger.Debug(
  500. "inserted good transaction",
  501. "priority", wtx.priority,
  502. "tx", fmt.Sprintf("%X", wtx.tx.Hash()),
  503. "height", txmp.height,
  504. "num_txs", txmp.Size(),
  505. )
  506. txmp.notifyTxsAvailable()
  507. }
  508. // defaultTxCallback is the CheckTx application callback used when a
  509. // transaction is being re-checked (if re-checking is enabled). The
  510. // caller must hold a mempool write-lock (via Lock()) and when
  511. // executing Update(), if the mempool is non-empty and Recheck is
  512. // enabled, then all remaining transactions will be rechecked via
  513. // CheckTx. The order transactions are rechecked must be the same as
  514. // the order in which this callback is called.
  515. func (txmp *TxMempool) defaultTxCallback(tx types.Tx, res *abci.ResponseCheckTx) {
  516. if txmp.recheckCursor == nil {
  517. return
  518. }
  519. txmp.metrics.RecheckTimes.Add(1)
  520. wtx := txmp.recheckCursor.Value.(*WrappedTx)
  521. // Search through the remaining list of tx to recheck for a transaction that matches
  522. // the one we received from the ABCI application.
  523. for {
  524. if bytes.Equal(tx, wtx.tx) {
  525. // We've found a tx in the recheck list that matches the tx that we
  526. // received from the ABCI application.
  527. // Break, and use this transaction for further checks.
  528. break
  529. }
  530. txmp.logger.Error(
  531. "re-CheckTx transaction mismatch",
  532. "got", wtx.tx.Hash(),
  533. "expected", tx.Key(),
  534. )
  535. if txmp.recheckCursor == txmp.recheckEnd {
  536. // we reached the end of the recheckTx list without finding a tx
  537. // matching the one we received from the ABCI application.
  538. // Return without processing any tx.
  539. txmp.recheckCursor = nil
  540. return
  541. }
  542. txmp.recheckCursor = txmp.recheckCursor.Next()
  543. wtx = txmp.recheckCursor.Value.(*WrappedTx)
  544. }
  545. // Only evaluate transactions that have not been removed. This can happen
  546. // if an existing transaction is evicted during CheckTx and while this
  547. // callback is being executed for the same evicted transaction.
  548. if !txmp.txStore.IsTxRemoved(wtx.hash) {
  549. var err error
  550. if txmp.postCheck != nil {
  551. err = txmp.postCheck(tx, res)
  552. }
  553. if res.Code == abci.CodeTypeOK && err == nil {
  554. wtx.priority = res.Priority
  555. } else {
  556. txmp.logger.Debug(
  557. "existing transaction no longer valid; failed re-CheckTx callback",
  558. "priority", wtx.priority,
  559. "tx", fmt.Sprintf("%X", wtx.tx.Hash()),
  560. "err", err,
  561. "code", res.Code,
  562. )
  563. if wtx.gossipEl != txmp.recheckCursor {
  564. panic("corrupted reCheckTx cursor")
  565. }
  566. txmp.removeTx(wtx, !txmp.config.KeepInvalidTxsInCache)
  567. }
  568. }
  569. // move reCheckTx cursor to next element
  570. if txmp.recheckCursor == txmp.recheckEnd {
  571. txmp.recheckCursor = nil
  572. } else {
  573. txmp.recheckCursor = txmp.recheckCursor.Next()
  574. }
  575. if txmp.recheckCursor == nil {
  576. txmp.logger.Debug("finished rechecking transactions")
  577. if txmp.Size() > 0 {
  578. txmp.notifyTxsAvailable()
  579. }
  580. }
  581. txmp.metrics.Size.Set(float64(txmp.Size()))
  582. }
  583. // updateReCheckTxs updates the recheck cursors using the gossipIndex. For
  584. // each transaction, it executes CheckTx. The global callback defined on
  585. // the proxyAppConn will be executed for each transaction after CheckTx is
  586. // executed.
  587. //
  588. // NOTE:
  589. // - The caller must have a write-lock when executing updateReCheckTxs.
  590. func (txmp *TxMempool) updateReCheckTxs(ctx context.Context) {
  591. if txmp.Size() == 0 {
  592. panic("attempted to update re-CheckTx txs when mempool is empty")
  593. }
  594. txmp.recheckCursor = txmp.gossipIndex.Front()
  595. txmp.recheckEnd = txmp.gossipIndex.Back()
  596. for e := txmp.gossipIndex.Front(); e != nil; e = e.Next() {
  597. wtx := e.Value.(*WrappedTx)
  598. // Only execute CheckTx if the transaction is not marked as removed which
  599. // could happen if the transaction was evicted.
  600. if !txmp.txStore.IsTxRemoved(wtx.hash) {
  601. res, err := txmp.proxyAppConn.CheckTx(ctx, abci.RequestCheckTx{
  602. Tx: wtx.tx,
  603. Type: abci.CheckTxType_Recheck,
  604. })
  605. if err != nil {
  606. // no need in retrying since the tx will be rechecked after the next block
  607. txmp.logger.Error("failed to execute CheckTx during rechecking", "err", err)
  608. continue
  609. }
  610. txmp.defaultTxCallback(wtx.tx, res)
  611. }
  612. }
  613. if err := txmp.proxyAppConn.Flush(ctx); err != nil {
  614. txmp.logger.Error("failed to flush transactions during rechecking", "err", err)
  615. }
  616. }
  617. // canAddTx returns an error if we cannot insert the provided *WrappedTx into
  618. // the mempool due to mempool configured constraints. If it returns nil,
  619. // the transaction can be inserted into the mempool.
  620. func (txmp *TxMempool) canAddTx(wtx *WrappedTx) error {
  621. var (
  622. numTxs = txmp.Size()
  623. sizeBytes = txmp.SizeBytes()
  624. )
  625. if numTxs >= txmp.config.Size || int64(wtx.Size())+sizeBytes > txmp.config.MaxTxsBytes {
  626. return types.ErrMempoolIsFull{
  627. NumTxs: numTxs,
  628. MaxTxs: txmp.config.Size,
  629. TxsBytes: sizeBytes,
  630. MaxTxsBytes: txmp.config.MaxTxsBytes,
  631. }
  632. }
  633. return nil
  634. }
  635. func (txmp *TxMempool) insertTx(wtx *WrappedTx) {
  636. txmp.txStore.SetTx(wtx)
  637. txmp.priorityIndex.PushTx(wtx)
  638. txmp.heightIndex.Insert(wtx)
  639. txmp.timestampIndex.Insert(wtx)
  640. // Insert the transaction into the gossip index and mark the reference to the
  641. // linked-list element, which will be needed at a later point when the
  642. // transaction is removed.
  643. gossipEl := txmp.gossipIndex.PushBack(wtx)
  644. wtx.gossipEl = gossipEl
  645. atomic.AddInt64(&txmp.sizeBytes, int64(wtx.Size()))
  646. }
  647. func (txmp *TxMempool) removeTx(wtx *WrappedTx, removeFromCache bool) {
  648. if txmp.txStore.IsTxRemoved(wtx.hash) {
  649. return
  650. }
  651. txmp.txStore.RemoveTx(wtx)
  652. txmp.priorityIndex.RemoveTx(wtx)
  653. txmp.heightIndex.Remove(wtx)
  654. txmp.timestampIndex.Remove(wtx)
  655. // Remove the transaction from the gossip index and cleanup the linked-list
  656. // element so it can be garbage collected.
  657. txmp.gossipIndex.Remove(wtx.gossipEl)
  658. wtx.gossipEl.DetachPrev()
  659. atomic.AddInt64(&txmp.sizeBytes, int64(-wtx.Size()))
  660. if removeFromCache {
  661. txmp.cache.Remove(wtx.tx)
  662. }
  663. }
  664. // purgeExpiredTxs removes all transactions that have exceeded their respective
  665. // height- and/or time-based TTLs from their respective indexes. Every expired
  666. // transaction will be removed from the mempool, but preserved in the cache.
  667. //
  668. // NOTE: purgeExpiredTxs must only be called during TxMempool#Update in which
  669. // the caller has a write-lock on the mempool and so we can safely iterate over
  670. // the height and time based indexes.
  671. func (txmp *TxMempool) purgeExpiredTxs(blockHeight int64) {
  672. now := time.Now()
  673. expiredTxs := make(map[types.TxKey]*WrappedTx)
  674. if txmp.config.TTLNumBlocks > 0 {
  675. purgeIdx := -1
  676. for i, wtx := range txmp.heightIndex.txs {
  677. if (blockHeight - wtx.height) > txmp.config.TTLNumBlocks {
  678. expiredTxs[wtx.tx.Key()] = wtx
  679. purgeIdx = i
  680. } else {
  681. // since the index is sorted, we know no other txs can be be purged
  682. break
  683. }
  684. }
  685. if purgeIdx >= 0 {
  686. txmp.heightIndex.txs = txmp.heightIndex.txs[purgeIdx+1:]
  687. }
  688. }
  689. if txmp.config.TTLDuration > 0 {
  690. purgeIdx := -1
  691. for i, wtx := range txmp.timestampIndex.txs {
  692. if now.Sub(wtx.timestamp) > txmp.config.TTLDuration {
  693. expiredTxs[wtx.tx.Key()] = wtx
  694. purgeIdx = i
  695. } else {
  696. // since the index is sorted, we know no other txs can be be purged
  697. break
  698. }
  699. }
  700. if purgeIdx >= 0 {
  701. txmp.timestampIndex.txs = txmp.timestampIndex.txs[purgeIdx+1:]
  702. }
  703. }
  704. for _, wtx := range expiredTxs {
  705. txmp.removeTx(wtx, false)
  706. }
  707. }
  708. func (txmp *TxMempool) notifyTxsAvailable() {
  709. if txmp.Size() == 0 {
  710. panic("attempt to notify txs available but mempool is empty!")
  711. }
  712. if txmp.txsAvailable != nil && !txmp.notifiedTxsAvailable {
  713. // channel cap is 1, so this will send once
  714. txmp.notifiedTxsAvailable = true
  715. select {
  716. case txmp.txsAvailable <- struct{}{}:
  717. default:
  718. }
  719. }
  720. }