github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/mempool/v0/clist_mempool.go (about)

     1  package v0
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"sync"
     7  	"sync/atomic"
     8  
     9  	abci "github.com/badrootd/nibiru-cometbft/abci/types"
    10  	"github.com/badrootd/nibiru-cometbft/config"
    11  	"github.com/badrootd/nibiru-cometbft/libs/clist"
    12  	"github.com/badrootd/nibiru-cometbft/libs/log"
    13  	cmtmath "github.com/badrootd/nibiru-cometbft/libs/math"
    14  	cmtsync "github.com/badrootd/nibiru-cometbft/libs/sync"
    15  	"github.com/badrootd/nibiru-cometbft/mempool"
    16  	"github.com/badrootd/nibiru-cometbft/p2p"
    17  	"github.com/badrootd/nibiru-cometbft/proxy"
    18  	"github.com/badrootd/nibiru-cometbft/types"
    19  )
    20  
    21  // CListMempool is an ordered in-memory pool for transactions before they are
    22  // proposed in a consensus round. Transaction validity is checked using the
    23  // CheckTx abci message before the transaction is added to the pool. The
    24  // mempool uses a concurrent list structure for storing transactions that can
    25  // be efficiently accessed by multiple concurrent readers.
    26  type CListMempool struct {
    27  	// Atomic integers
    28  	height   int64 // the last block Update()'d to
    29  	txsBytes int64 // total size of mempool, in bytes
    30  
    31  	// notify listeners (ie. consensus) when txs are available
    32  	notifiedTxsAvailable bool
    33  	txsAvailable         chan struct{} // fires once for each height, when the mempool is not empty
    34  
    35  	config *config.MempoolConfig
    36  
    37  	// Exclusive mutex for Update method to prevent concurrent execution of
    38  	// CheckTx or ReapMaxBytesMaxGas(ReapMaxTxs) methods.
    39  	updateMtx cmtsync.RWMutex
    40  	preCheck  mempool.PreCheckFunc
    41  	postCheck mempool.PostCheckFunc
    42  
    43  	txs          *clist.CList // concurrent linked-list of good txs
    44  	proxyAppConn proxy.AppConnMempool
    45  
    46  	// Track whether we're rechecking txs.
    47  	// These are not protected by a mutex and are expected to be mutated in
    48  	// serial (ie. by abci responses which are called in serial).
    49  	recheckCursor *clist.CElement // next expected response
    50  	recheckEnd    *clist.CElement // re-checking stops here
    51  
    52  	// Map for quick access to txs to record sender in CheckTx.
    53  	// txsMap: txKey -> CElement
    54  	txsMap sync.Map
    55  
    56  	// Keep a cache of already-seen txs.
    57  	// This reduces the pressure on the proxyApp.
    58  	cache mempool.TxCache
    59  
    60  	logger  log.Logger
    61  	metrics *mempool.Metrics
    62  }
    63  
    64  var _ mempool.Mempool = &CListMempool{}
    65  
    66  // CListMempoolOption sets an optional parameter on the mempool.
    67  type CListMempoolOption func(*CListMempool)
    68  
    69  // NewCListMempool returns a new mempool with the given configuration and
    70  // connection to an application.
    71  func NewCListMempool(
    72  	cfg *config.MempoolConfig,
    73  	proxyAppConn proxy.AppConnMempool,
    74  	height int64,
    75  	options ...CListMempoolOption,
    76  ) *CListMempool {
    77  
    78  	mp := &CListMempool{
    79  		config:        cfg,
    80  		proxyAppConn:  proxyAppConn,
    81  		txs:           clist.New(),
    82  		height:        height,
    83  		recheckCursor: nil,
    84  		recheckEnd:    nil,
    85  		logger:        log.NewNopLogger(),
    86  		metrics:       mempool.NopMetrics(),
    87  	}
    88  
    89  	if cfg.CacheSize > 0 {
    90  		mp.cache = mempool.NewLRUTxCache(cfg.CacheSize)
    91  	} else {
    92  		mp.cache = mempool.NopTxCache{}
    93  	}
    94  
    95  	proxyAppConn.SetResponseCallback(mp.globalCb)
    96  
    97  	for _, option := range options {
    98  		option(mp)
    99  	}
   100  
   101  	return mp
   102  }
   103  
   104  // NOTE: not thread safe - should only be called once, on startup
   105  func (mem *CListMempool) EnableTxsAvailable() {
   106  	mem.txsAvailable = make(chan struct{}, 1)
   107  }
   108  
   109  // SetLogger sets the Logger.
   110  func (mem *CListMempool) SetLogger(l log.Logger) {
   111  	mem.logger = l
   112  }
   113  
   114  // WithPreCheck sets a filter for the mempool to reject a tx if f(tx) returns
   115  // false. This is ran before CheckTx. Only applies to the first created block.
   116  // After that, Update overwrites the existing value.
   117  func WithPreCheck(f mempool.PreCheckFunc) CListMempoolOption {
   118  	return func(mem *CListMempool) { mem.preCheck = f }
   119  }
   120  
   121  // WithPostCheck sets a filter for the mempool to reject a tx if f(tx) returns
   122  // false. This is ran after CheckTx. Only applies to the first created block.
   123  // After that, Update overwrites the existing value.
   124  func WithPostCheck(f mempool.PostCheckFunc) CListMempoolOption {
   125  	return func(mem *CListMempool) { mem.postCheck = f }
   126  }
   127  
   128  // WithMetrics sets the metrics.
   129  func WithMetrics(metrics *mempool.Metrics) CListMempoolOption {
   130  	return func(mem *CListMempool) { mem.metrics = metrics }
   131  }
   132  
   133  // Safe for concurrent use by multiple goroutines.
   134  func (mem *CListMempool) Lock() {
   135  	mem.updateMtx.Lock()
   136  }
   137  
   138  // Safe for concurrent use by multiple goroutines.
   139  func (mem *CListMempool) Unlock() {
   140  	mem.updateMtx.Unlock()
   141  }
   142  
   143  // Safe for concurrent use by multiple goroutines.
   144  func (mem *CListMempool) Size() int {
   145  	return mem.txs.Len()
   146  }
   147  
   148  // Safe for concurrent use by multiple goroutines.
   149  func (mem *CListMempool) SizeBytes() int64 {
   150  	return atomic.LoadInt64(&mem.txsBytes)
   151  }
   152  
   153  // Lock() must be help by the caller during execution.
   154  func (mem *CListMempool) FlushAppConn() error {
   155  	return mem.proxyAppConn.FlushSync()
   156  }
   157  
   158  // XXX: Unsafe! Calling Flush may leave mempool in inconsistent state.
   159  func (mem *CListMempool) Flush() {
   160  	mem.updateMtx.RLock()
   161  	defer mem.updateMtx.RUnlock()
   162  
   163  	_ = atomic.SwapInt64(&mem.txsBytes, 0)
   164  	mem.cache.Reset()
   165  
   166  	for e := mem.txs.Front(); e != nil; e = e.Next() {
   167  		mem.txs.Remove(e)
   168  		e.DetachPrev()
   169  	}
   170  
   171  	mem.txsMap.Range(func(key, _ interface{}) bool {
   172  		mem.txsMap.Delete(key)
   173  		return true
   174  	})
   175  }
   176  
   177  // TxsFront returns the first transaction in the ordered list for peer
   178  // goroutines to call .NextWait() on.
   179  // FIXME: leaking implementation details!
   180  //
   181  // Safe for concurrent use by multiple goroutines.
   182  func (mem *CListMempool) TxsFront() *clist.CElement {
   183  	return mem.txs.Front()
   184  }
   185  
   186  // TxsWaitChan returns a channel to wait on transactions. It will be closed
   187  // once the mempool is not empty (ie. the internal `mem.txs` has at least one
   188  // element)
   189  //
   190  // Safe for concurrent use by multiple goroutines.
   191  func (mem *CListMempool) TxsWaitChan() <-chan struct{} {
   192  	return mem.txs.WaitChan()
   193  }
   194  
   195  // It blocks if we're waiting on Update() or Reap().
   196  // cb: A callback from the CheckTx command.
   197  //
   198  //	It gets called from another goroutine.
   199  //
   200  // CONTRACT: Either cb will get called, or err returned.
   201  //
   202  // Safe for concurrent use by multiple goroutines.
   203  func (mem *CListMempool) CheckTx(
   204  	tx types.Tx,
   205  	cb func(*abci.Response),
   206  	txInfo mempool.TxInfo,
   207  ) error {
   208  
   209  	mem.updateMtx.RLock()
   210  	// use defer to unlock mutex because application (*local client*) might panic
   211  	defer mem.updateMtx.RUnlock()
   212  
   213  	txSize := len(tx)
   214  
   215  	if err := mem.isFull(txSize); err != nil {
   216  		return err
   217  	}
   218  
   219  	if txSize > mem.config.MaxTxBytes {
   220  		return mempool.ErrTxTooLarge{
   221  			Max:    mem.config.MaxTxBytes,
   222  			Actual: txSize,
   223  		}
   224  	}
   225  
   226  	if mem.preCheck != nil {
   227  		if err := mem.preCheck(tx); err != nil {
   228  			return mempool.ErrPreCheck{
   229  				Reason: err,
   230  			}
   231  		}
   232  	}
   233  
   234  	// NOTE: proxyAppConn may error if tx buffer is full
   235  	if err := mem.proxyAppConn.Error(); err != nil {
   236  		return err
   237  	}
   238  
   239  	if !mem.cache.Push(tx) { // if the transaction already exists in the cache
   240  		// Record a new sender for a tx we've already seen.
   241  		// Note it's possible a tx is still in the cache but no longer in the mempool
   242  		// (eg. after committing a block, txs are removed from mempool but not cache),
   243  		// so we only record the sender for txs still in the mempool.
   244  		if e, ok := mem.txsMap.Load(tx.Key()); ok {
   245  			memTx := e.(*clist.CElement).Value.(*mempoolTx)
   246  			memTx.senders.LoadOrStore(txInfo.SenderID, true)
   247  			// TODO: consider punishing peer for dups,
   248  			// its non-trivial since invalid txs can become valid,
   249  			// but they can spam the same tx with little cost to them atm.
   250  		}
   251  		return mempool.ErrTxInCache
   252  	}
   253  
   254  	reqRes := mem.proxyAppConn.CheckTxAsync(abci.RequestCheckTx{Tx: tx})
   255  	reqRes.SetCallback(mem.reqResCb(tx, txInfo.SenderID, txInfo.SenderP2PID, cb))
   256  
   257  	return nil
   258  }
   259  
   260  // Global callback that will be called after every ABCI response.
   261  // Having a single global callback avoids needing to set a callback for each request.
   262  // However, processing the checkTx response requires the peerID (so we can track which txs we heard from who),
   263  // and peerID is not included in the ABCI request, so we have to set request-specific callbacks that
   264  // include this information. If we're not in the midst of a recheck, this function will just return,
   265  // so the request specific callback can do the work.
   266  //
   267  // When rechecking, we don't need the peerID, so the recheck callback happens
   268  // here.
   269  func (mem *CListMempool) globalCb(req *abci.Request, res *abci.Response) {
   270  	if mem.recheckCursor == nil {
   271  		return
   272  	}
   273  
   274  	mem.metrics.RecheckTimes.Add(1)
   275  	mem.resCbRecheck(req, res)
   276  
   277  	// update metrics
   278  	mem.metrics.Size.Set(float64(mem.Size()))
   279  }
   280  
   281  // Request specific callback that should be set on individual reqRes objects
   282  // to incorporate local information when processing the response.
   283  // This allows us to track the peer that sent us this tx, so we can avoid sending it back to them.
   284  // NOTE: alternatively, we could include this information in the ABCI request itself.
   285  //
   286  // External callers of CheckTx, like the RPC, can also pass an externalCb through here that is called
   287  // when all other response processing is complete.
   288  //
   289  // Used in CheckTx to record PeerID who sent us the tx.
   290  func (mem *CListMempool) reqResCb(
   291  	tx []byte,
   292  	peerID uint16,
   293  	peerP2PID p2p.ID,
   294  	externalCb func(*abci.Response),
   295  ) func(res *abci.Response) {
   296  	return func(res *abci.Response) {
   297  		if mem.recheckCursor != nil {
   298  			// this should never happen
   299  			panic("recheck cursor is not nil in reqResCb")
   300  		}
   301  
   302  		mem.resCbFirstTime(tx, peerID, peerP2PID, res)
   303  
   304  		// update metrics
   305  		mem.metrics.Size.Set(float64(mem.Size()))
   306  		mem.metrics.SizeBytes.Set(float64(mem.SizeBytes()))
   307  
   308  		// passed in by the caller of CheckTx, eg. the RPC
   309  		if externalCb != nil {
   310  			externalCb(res)
   311  		}
   312  	}
   313  }
   314  
   315  // Called from:
   316  //   - resCbFirstTime (lock not held) if tx is valid
   317  func (mem *CListMempool) addTx(memTx *mempoolTx) {
   318  	e := mem.txs.PushBack(memTx)
   319  	mem.txsMap.Store(memTx.tx.Key(), e)
   320  	atomic.AddInt64(&mem.txsBytes, int64(len(memTx.tx)))
   321  	mem.metrics.TxSizeBytes.Observe(float64(len(memTx.tx)))
   322  }
   323  
   324  // Called from:
   325  //   - Update (lock held) if tx was committed
   326  //   - resCbRecheck (lock not held) if tx was invalidated
   327  func (mem *CListMempool) removeTx(tx types.Tx, elem *clist.CElement, removeFromCache bool) {
   328  	mem.txs.Remove(elem)
   329  	elem.DetachPrev()
   330  	mem.txsMap.Delete(tx.Key())
   331  	atomic.AddInt64(&mem.txsBytes, int64(-len(tx)))
   332  
   333  	if removeFromCache {
   334  		mem.cache.Remove(tx)
   335  	}
   336  }
   337  
   338  // RemoveTxByKey removes a transaction from the mempool by its TxKey index.
   339  func (mem *CListMempool) RemoveTxByKey(txKey types.TxKey) error {
   340  	if e, ok := mem.txsMap.Load(txKey); ok {
   341  		memTx := e.(*clist.CElement).Value.(*mempoolTx)
   342  		if memTx != nil {
   343  			mem.removeTx(memTx.tx, e.(*clist.CElement), false)
   344  			return nil
   345  		}
   346  		return errors.New("transaction not found")
   347  	}
   348  	return errors.New("invalid transaction found")
   349  }
   350  
   351  func (mem *CListMempool) isFull(txSize int) error {
   352  	var (
   353  		memSize  = mem.Size()
   354  		txsBytes = mem.SizeBytes()
   355  	)
   356  
   357  	if memSize >= mem.config.Size || int64(txSize)+txsBytes > mem.config.MaxTxsBytes {
   358  		return mempool.ErrMempoolIsFull{
   359  			NumTxs:      memSize,
   360  			MaxTxs:      mem.config.Size,
   361  			TxsBytes:    txsBytes,
   362  			MaxTxsBytes: mem.config.MaxTxsBytes,
   363  		}
   364  	}
   365  
   366  	return nil
   367  }
   368  
   369  // callback, which is called after the app checked the tx for the first time.
   370  //
   371  // The case where the app checks the tx for the second and subsequent times is
   372  // handled by the resCbRecheck callback.
   373  func (mem *CListMempool) resCbFirstTime(
   374  	tx []byte,
   375  	peerID uint16,
   376  	peerP2PID p2p.ID,
   377  	res *abci.Response,
   378  ) {
   379  	switch r := res.Value.(type) {
   380  	case *abci.Response_CheckTx:
   381  		var postCheckErr error
   382  		if mem.postCheck != nil {
   383  			postCheckErr = mem.postCheck(tx, r.CheckTx)
   384  		}
   385  		if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil {
   386  			// Check mempool isn't full again to reduce the chance of exceeding the
   387  			// limits.
   388  			if err := mem.isFull(len(tx)); err != nil {
   389  				// remove from cache (mempool might have a space later)
   390  				mem.cache.Remove(tx)
   391  				mem.logger.Error(err.Error())
   392  				return
   393  			}
   394  
   395  			// Check transaction not already in the mempool
   396  			if e, ok := mem.txsMap.Load(types.Tx(tx).Key()); ok {
   397  				memTx := e.(*clist.CElement).Value.(*mempoolTx)
   398  				memTx.senders.LoadOrStore(peerID, true)
   399  				mem.logger.Debug(
   400  					"transaction already there, not adding it again",
   401  					"tx", types.Tx(tx).Hash(),
   402  					"res", r,
   403  					"height", mem.height,
   404  					"total", mem.Size(),
   405  				)
   406  				return
   407  			}
   408  
   409  			memTx := &mempoolTx{
   410  				height:    mem.height,
   411  				gasWanted: r.CheckTx.GasWanted,
   412  				tx:        tx,
   413  			}
   414  			memTx.senders.Store(peerID, true)
   415  			mem.addTx(memTx)
   416  			mem.logger.Debug(
   417  				"added good transaction",
   418  				"tx", types.Tx(tx).Hash(),
   419  				"res", r,
   420  				"height", memTx.height,
   421  				"total", mem.Size(),
   422  			)
   423  			mem.notifyTxsAvailable()
   424  		} else {
   425  			// ignore bad transaction
   426  			mem.logger.Debug(
   427  				"rejected bad transaction",
   428  				"tx", types.Tx(tx).Hash(),
   429  				"peerID", peerP2PID,
   430  				"res", r,
   431  				"err", postCheckErr,
   432  			)
   433  			mem.metrics.FailedTxs.Add(1)
   434  
   435  			if !mem.config.KeepInvalidTxsInCache {
   436  				// remove from cache (it might be good later)
   437  				mem.cache.Remove(tx)
   438  			}
   439  		}
   440  
   441  	default:
   442  		// ignore other messages
   443  	}
   444  }
   445  
   446  // callback, which is called after the app rechecked the tx.
   447  //
   448  // The case where the app checks the tx for the first time is handled by the
   449  // resCbFirstTime callback.
   450  func (mem *CListMempool) resCbRecheck(req *abci.Request, res *abci.Response) {
   451  	switch r := res.Value.(type) {
   452  	case *abci.Response_CheckTx:
   453  		tx := req.GetCheckTx().Tx
   454  		memTx := mem.recheckCursor.Value.(*mempoolTx)
   455  
   456  		// Search through the remaining list of tx to recheck for a transaction that matches
   457  		// the one we received from the ABCI application.
   458  		for {
   459  			if bytes.Equal(tx, memTx.tx) {
   460  				// We've found a tx in the recheck list that matches the tx that we
   461  				// received from the ABCI application.
   462  				// Break, and use this transaction for further checks.
   463  				break
   464  			}
   465  
   466  			mem.logger.Error(
   467  				"re-CheckTx transaction mismatch",
   468  				"got", types.Tx(tx),
   469  				"expected", memTx.tx,
   470  			)
   471  
   472  			if mem.recheckCursor == mem.recheckEnd {
   473  				// we reached the end of the recheckTx list without finding a tx
   474  				// matching the one we received from the ABCI application.
   475  				// Return without processing any tx.
   476  				mem.recheckCursor = nil
   477  				return
   478  			}
   479  
   480  			mem.recheckCursor = mem.recheckCursor.Next()
   481  			memTx = mem.recheckCursor.Value.(*mempoolTx)
   482  		}
   483  
   484  		var postCheckErr error
   485  		if mem.postCheck != nil {
   486  			postCheckErr = mem.postCheck(tx, r.CheckTx)
   487  		}
   488  
   489  		if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil {
   490  			// Good, nothing to do.
   491  		} else {
   492  			// Tx became invalidated due to newly committed block.
   493  			mem.logger.Debug("tx is no longer valid", "tx", types.Tx(tx).Hash(), "res", r, "err", postCheckErr)
   494  			// NOTE: we remove tx from the cache because it might be good later
   495  			mem.removeTx(tx, mem.recheckCursor, !mem.config.KeepInvalidTxsInCache)
   496  		}
   497  		if mem.recheckCursor == mem.recheckEnd {
   498  			mem.recheckCursor = nil
   499  		} else {
   500  			mem.recheckCursor = mem.recheckCursor.Next()
   501  		}
   502  		if mem.recheckCursor == nil {
   503  			// Done!
   504  			mem.logger.Debug("done rechecking txs")
   505  
   506  			// incase the recheck removed all txs
   507  			if mem.Size() > 0 {
   508  				mem.notifyTxsAvailable()
   509  			}
   510  		}
   511  	default:
   512  		// ignore other messages
   513  	}
   514  }
   515  
   516  // Safe for concurrent use by multiple goroutines.
   517  func (mem *CListMempool) TxsAvailable() <-chan struct{} {
   518  	return mem.txsAvailable
   519  }
   520  
   521  func (mem *CListMempool) notifyTxsAvailable() {
   522  	if mem.Size() == 0 {
   523  		panic("notified txs available but mempool is empty!")
   524  	}
   525  	if mem.txsAvailable != nil && !mem.notifiedTxsAvailable {
   526  		// channel cap is 1, so this will send once
   527  		mem.notifiedTxsAvailable = true
   528  		select {
   529  		case mem.txsAvailable <- struct{}{}:
   530  		default:
   531  		}
   532  	}
   533  }
   534  
   535  // Safe for concurrent use by multiple goroutines.
   536  func (mem *CListMempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs {
   537  	mem.updateMtx.RLock()
   538  	defer mem.updateMtx.RUnlock()
   539  
   540  	var (
   541  		totalGas    int64
   542  		runningSize int64
   543  	)
   544  
   545  	// TODO: we will get a performance boost if we have a good estimate of avg
   546  	// size per tx, and set the initial capacity based off of that.
   547  	// txs := make([]types.Tx, 0, cmtmath.MinInt(mem.txs.Len(), max/mem.avgTxSize))
   548  	txs := make([]types.Tx, 0, mem.txs.Len())
   549  	for e := mem.txs.Front(); e != nil; e = e.Next() {
   550  		memTx := e.Value.(*mempoolTx)
   551  
   552  		txs = append(txs, memTx.tx)
   553  
   554  		dataSize := types.ComputeProtoSizeForTxs([]types.Tx{memTx.tx})
   555  
   556  		// Check total size requirement
   557  		if maxBytes > -1 && runningSize+dataSize > maxBytes {
   558  			return txs[:len(txs)-1]
   559  		}
   560  
   561  		runningSize += dataSize
   562  
   563  		// Check total gas requirement.
   564  		// If maxGas is negative, skip this check.
   565  		// Since newTotalGas < masGas, which
   566  		// must be non-negative, it follows that this won't overflow.
   567  		newTotalGas := totalGas + memTx.gasWanted
   568  		if maxGas > -1 && newTotalGas > maxGas {
   569  			return txs[:len(txs)-1]
   570  		}
   571  		totalGas = newTotalGas
   572  	}
   573  	return txs
   574  }
   575  
   576  // Safe for concurrent use by multiple goroutines.
   577  func (mem *CListMempool) ReapMaxTxs(max int) types.Txs {
   578  	mem.updateMtx.RLock()
   579  	defer mem.updateMtx.RUnlock()
   580  
   581  	if max < 0 {
   582  		max = mem.txs.Len()
   583  	}
   584  
   585  	txs := make([]types.Tx, 0, cmtmath.MinInt(mem.txs.Len(), max))
   586  	for e := mem.txs.Front(); e != nil && len(txs) <= max; e = e.Next() {
   587  		memTx := e.Value.(*mempoolTx)
   588  		txs = append(txs, memTx.tx)
   589  	}
   590  	return txs
   591  }
   592  
   593  // Lock() must be help by the caller during execution.
   594  func (mem *CListMempool) Update(
   595  	height int64,
   596  	txs types.Txs,
   597  	deliverTxResponses []*abci.ResponseDeliverTx,
   598  	preCheck mempool.PreCheckFunc,
   599  	postCheck mempool.PostCheckFunc,
   600  ) error {
   601  	// Set height
   602  	mem.height = height
   603  	mem.notifiedTxsAvailable = false
   604  
   605  	if preCheck != nil {
   606  		mem.preCheck = preCheck
   607  	}
   608  	if postCheck != nil {
   609  		mem.postCheck = postCheck
   610  	}
   611  
   612  	for i, tx := range txs {
   613  		if deliverTxResponses[i].Code == abci.CodeTypeOK {
   614  			// Add valid committed tx to the cache (if missing).
   615  			_ = mem.cache.Push(tx)
   616  		} else if !mem.config.KeepInvalidTxsInCache {
   617  			// Allow invalid transactions to be resubmitted.
   618  			mem.cache.Remove(tx)
   619  		}
   620  
   621  		// Remove committed tx from the mempool.
   622  		//
   623  		// Note an evil proposer can drop valid txs!
   624  		// Mempool before:
   625  		//   100 -> 101 -> 102
   626  		// Block, proposed by an evil proposer:
   627  		//   101 -> 102
   628  		// Mempool after:
   629  		//   100
   630  		// https://github.com/tendermint/tendermint/issues/3322.
   631  		if e, ok := mem.txsMap.Load(tx.Key()); ok {
   632  			mem.removeTx(tx, e.(*clist.CElement), false)
   633  		}
   634  	}
   635  
   636  	// Either recheck non-committed txs to see if they became invalid
   637  	// or just notify there're some txs left.
   638  	if mem.Size() > 0 {
   639  		if mem.config.Recheck {
   640  			mem.logger.Debug("recheck txs", "numtxs", mem.Size(), "height", height)
   641  			mem.recheckTxs()
   642  			// At this point, mem.txs are being rechecked.
   643  			// mem.recheckCursor re-scans mem.txs and possibly removes some txs.
   644  			// Before mem.Reap(), we should wait for mem.recheckCursor to be nil.
   645  		} else {
   646  			mem.notifyTxsAvailable()
   647  		}
   648  	}
   649  
   650  	// Update metrics
   651  	mem.metrics.Size.Set(float64(mem.Size()))
   652  	mem.metrics.SizeBytes.Set(float64(mem.SizeBytes()))
   653  
   654  	return nil
   655  }
   656  
   657  func (mem *CListMempool) recheckTxs() {
   658  	if mem.Size() == 0 {
   659  		panic("recheckTxs is called, but the mempool is empty")
   660  	}
   661  
   662  	mem.recheckCursor = mem.txs.Front()
   663  	mem.recheckEnd = mem.txs.Back()
   664  
   665  	// Push txs to proxyAppConn
   666  	// NOTE: globalCb may be called concurrently.
   667  	for e := mem.txs.Front(); e != nil; e = e.Next() {
   668  		memTx := e.Value.(*mempoolTx)
   669  		mem.proxyAppConn.CheckTxAsync(abci.RequestCheckTx{
   670  			Tx:   memTx.tx,
   671  			Type: abci.CheckTxType_Recheck,
   672  		})
   673  	}
   674  
   675  	mem.proxyAppConn.FlushAsync()
   676  }
   677  
   678  //--------------------------------------------------------------------------------
   679  
   680  // mempoolTx is a transaction that successfully ran
   681  type mempoolTx struct {
   682  	height    int64    // height that this tx had been validated in
   683  	gasWanted int64    // amount of gas this tx states it will require
   684  	tx        types.Tx //
   685  
   686  	// ids of peers who've sent us this tx (as a map for quick lookups).
   687  	// senders: PeerID -> bool
   688  	senders sync.Map
   689  }
   690  
   691  // Height returns the height for this transaction
   692  func (memTx *mempoolTx) Height() int64 {
   693  	return atomic.LoadInt64(&memTx.height)
   694  }