github.com/fiagdao/tendermint@v0.32.11-0.20220824195748-2087fcc480c1/mempool/clist_mempool.go (about)

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