github.com/vipernet-xyz/tm@v0.34.24/mempool/v1/mempool.go (about)

     1  package v1
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"sort"
     7  	"sync"
     8  	"sync/atomic"
     9  	"time"
    10  
    11  	"github.com/creachadair/taskgroup"
    12  
    13  	abci "github.com/vipernet-xyz/tm/abci/types"
    14  	"github.com/vipernet-xyz/tm/config"
    15  	"github.com/vipernet-xyz/tm/libs/clist"
    16  	"github.com/vipernet-xyz/tm/libs/log"
    17  	"github.com/vipernet-xyz/tm/mempool"
    18  	"github.com/vipernet-xyz/tm/proxy"
    19  	"github.com/vipernet-xyz/tm/types"
    20  )
    21  
    22  var _ mempool.Mempool = (*TxMempool)(nil)
    23  
    24  // TxMempoolOption sets an optional parameter on the TxMempool.
    25  type TxMempoolOption func(*TxMempool)
    26  
    27  // TxMempool implemements the Mempool interface and allows the application to
    28  // set priority values on transactions in the CheckTx response. When selecting
    29  // transactions to include in a block, higher-priority transactions are chosen
    30  // first.  When evicting transactions from the mempool for size constraints,
    31  // lower-priority transactions are evicted sooner.
    32  //
    33  // Within the mempool, transactions are ordered by time of arrival, and are
    34  // gossiped to the rest of the network based on that order (gossip order does
    35  // not take priority into account).
    36  type TxMempool struct {
    37  	// Immutable fields
    38  	logger       log.Logger
    39  	config       *config.MempoolConfig
    40  	proxyAppConn proxy.AppConnMempool
    41  	metrics      *mempool.Metrics
    42  	cache        mempool.TxCache // seen transactions
    43  
    44  	// Atomically-updated fields
    45  	txsBytes int64 // atomic: the total size of all transactions in the mempool, in bytes
    46  
    47  	// Synchronized fields, protected by mtx.
    48  	mtx                  *sync.RWMutex
    49  	notifiedTxsAvailable bool
    50  	txsAvailable         chan struct{} // one value sent per height when mempool is not empty
    51  	preCheck             mempool.PreCheckFunc
    52  	postCheck            mempool.PostCheckFunc
    53  	height               int64 // the latest height passed to Update
    54  
    55  	txs        *clist.CList // valid transactions (passed CheckTx)
    56  	txByKey    map[types.TxKey]*clist.CElement
    57  	txBySender map[string]*clist.CElement // for sender != ""
    58  }
    59  
    60  // NewTxMempool constructs a new, empty priority mempool at the specified
    61  // initial height and using the given config and options.
    62  func NewTxMempool(
    63  	logger log.Logger,
    64  	cfg *config.MempoolConfig,
    65  	proxyAppConn proxy.AppConnMempool,
    66  	height int64,
    67  	options ...TxMempoolOption,
    68  ) *TxMempool {
    69  
    70  	txmp := &TxMempool{
    71  		logger:       logger,
    72  		config:       cfg,
    73  		proxyAppConn: proxyAppConn,
    74  		metrics:      mempool.NopMetrics(),
    75  		cache:        mempool.NopTxCache{},
    76  		txs:          clist.New(),
    77  		mtx:          new(sync.RWMutex),
    78  		height:       height,
    79  		txByKey:      make(map[types.TxKey]*clist.CElement),
    80  		txBySender:   make(map[string]*clist.CElement),
    81  	}
    82  	if cfg.CacheSize > 0 {
    83  		txmp.cache = mempool.NewLRUTxCache(cfg.CacheSize)
    84  	}
    85  
    86  	for _, opt := range options {
    87  		opt(txmp)
    88  	}
    89  
    90  	return txmp
    91  }
    92  
    93  // WithPreCheck sets a filter for the mempool to reject a transaction if f(tx)
    94  // returns an error. This is executed before CheckTx. It only applies to the
    95  // first created block. After that, Update() overwrites the existing value.
    96  func WithPreCheck(f mempool.PreCheckFunc) TxMempoolOption {
    97  	return func(txmp *TxMempool) { txmp.preCheck = f }
    98  }
    99  
   100  // WithPostCheck sets a filter for the mempool to reject a transaction if
   101  // f(tx, resp) returns an error. This is executed after CheckTx. It only applies
   102  // to the first created block. After that, Update overwrites the existing value.
   103  func WithPostCheck(f mempool.PostCheckFunc) TxMempoolOption {
   104  	return func(txmp *TxMempool) { txmp.postCheck = f }
   105  }
   106  
   107  // WithMetrics sets the mempool's metrics collector.
   108  func WithMetrics(metrics *mempool.Metrics) TxMempoolOption {
   109  	return func(txmp *TxMempool) { txmp.metrics = metrics }
   110  }
   111  
   112  // Lock obtains a write-lock on the mempool. A caller must be sure to explicitly
   113  // release the lock when finished.
   114  func (txmp *TxMempool) Lock() { txmp.mtx.Lock() }
   115  
   116  // Unlock releases a write-lock on the mempool.
   117  func (txmp *TxMempool) Unlock() { txmp.mtx.Unlock() }
   118  
   119  // Size returns the number of valid transactions in the mempool. It is
   120  // thread-safe.
   121  func (txmp *TxMempool) Size() int { return txmp.txs.Len() }
   122  
   123  // SizeBytes return the total sum in bytes of all the valid transactions in the
   124  // mempool. It is thread-safe.
   125  func (txmp *TxMempool) SizeBytes() int64 { return atomic.LoadInt64(&txmp.txsBytes) }
   126  
   127  // FlushAppConn executes FlushSync on the mempool's proxyAppConn.
   128  //
   129  // The caller must hold an exclusive mempool lock (by calling txmp.Lock) before
   130  // calling FlushAppConn.
   131  func (txmp *TxMempool) FlushAppConn() error {
   132  	// N.B.: We have to issue the call outside the lock so that its callback can
   133  	// fire.  It's safe to do this, the flush will block until complete.
   134  	//
   135  	// We could just not require the caller to hold the lock at all, but the
   136  	// semantics of the Mempool interface require the caller to hold it, and we
   137  	// can't change that without disrupting existing use.
   138  	txmp.mtx.Unlock()
   139  	defer txmp.mtx.Lock()
   140  
   141  	return txmp.proxyAppConn.FlushSync()
   142  }
   143  
   144  // EnableTxsAvailable enables the mempool to trigger events when transactions
   145  // are available on a block by block basis.
   146  func (txmp *TxMempool) EnableTxsAvailable() {
   147  	txmp.mtx.Lock()
   148  	defer txmp.mtx.Unlock()
   149  
   150  	txmp.txsAvailable = make(chan struct{}, 1)
   151  }
   152  
   153  // TxsAvailable returns a channel which fires once for every height, and only
   154  // when transactions are available in the mempool. It is thread-safe.
   155  func (txmp *TxMempool) TxsAvailable() <-chan struct{} { return txmp.txsAvailable }
   156  
   157  // CheckTx adds the given transaction to the mempool if it fits and passes the
   158  // application's ABCI CheckTx method.
   159  //
   160  // CheckTx reports an error without adding tx if:
   161  //
   162  // - The size of tx exceeds the configured maximum transaction size.
   163  // - The pre-check hook is defined and reports an error for tx.
   164  // - The transaction already exists in the cache.
   165  // - The proxy connection to the application fails.
   166  //
   167  // If tx passes all of the above conditions, it is passed (asynchronously) to
   168  // the application's ABCI CheckTx method and this CheckTx method returns nil.
   169  // If cb != nil, it is called when the ABCI request completes to report the
   170  // application response.
   171  //
   172  // If the application accepts the transaction and the mempool is full, the
   173  // mempool evicts one or more of the lowest-priority transaction whose priority
   174  // is (strictly) lower than the priority of tx and whose size together exceeds
   175  // the size of tx, and adds tx instead. If no such transactions exist, tx is
   176  // discarded.
   177  func (txmp *TxMempool) CheckTx(tx types.Tx, cb func(*abci.Response), txInfo mempool.TxInfo) error {
   178  
   179  	// During the initial phase of CheckTx, we do not need to modify any state.
   180  	// A transaction will not actually be added to the mempool until it survives
   181  	// a call to the ABCI CheckTx method and size constraint checks.
   182  	height, err := func() (int64, error) {
   183  		txmp.mtx.RLock()
   184  		defer txmp.mtx.RUnlock()
   185  
   186  		// Reject transactions in excess of the configured maximum transaction size.
   187  		if len(tx) > txmp.config.MaxTxBytes {
   188  			return 0, mempool.ErrTxTooLarge{Max: txmp.config.MaxTxBytes, Actual: len(tx)}
   189  		}
   190  
   191  		// If a precheck hook is defined, call it before invoking the application.
   192  		if txmp.preCheck != nil {
   193  			if err := txmp.preCheck(tx); err != nil {
   194  				return 0, mempool.ErrPreCheck{Reason: err}
   195  			}
   196  		}
   197  
   198  		// Early exit if the proxy connection has an error.
   199  		if err := txmp.proxyAppConn.Error(); err != nil {
   200  			return 0, err
   201  		}
   202  
   203  		txKey := tx.Key()
   204  
   205  		// Check for the transaction in the cache.
   206  		if !txmp.cache.Push(tx) {
   207  			// If the cached transaction is also in the pool, record its sender.
   208  			if elt, ok := txmp.txByKey[txKey]; ok {
   209  				w := elt.Value.(*WrappedTx)
   210  				w.SetPeer(txInfo.SenderID)
   211  			}
   212  			return 0, mempool.ErrTxInCache
   213  		}
   214  		return txmp.height, nil
   215  	}()
   216  	if err != nil {
   217  		return err
   218  	}
   219  
   220  	// Invoke an ABCI CheckTx for this transaction.
   221  	rsp, err := txmp.proxyAppConn.CheckTxSync(abci.RequestCheckTx{Tx: tx})
   222  	if err != nil {
   223  		txmp.cache.Remove(tx)
   224  		return err
   225  	}
   226  	wtx := &WrappedTx{
   227  		tx:        tx,
   228  		hash:      tx.Key(),
   229  		timestamp: time.Now().UTC(),
   230  		height:    height,
   231  	}
   232  	wtx.SetPeer(txInfo.SenderID)
   233  	txmp.addNewTransaction(wtx, rsp)
   234  	if cb != nil {
   235  		cb(&abci.Response{Value: &abci.Response_CheckTx{CheckTx: rsp}})
   236  	}
   237  	return nil
   238  }
   239  
   240  // RemoveTxByKey removes the transaction with the specified key from the
   241  // mempool. It reports an error if no such transaction exists.  This operation
   242  // does not remove the transaction from the cache.
   243  func (txmp *TxMempool) RemoveTxByKey(txKey types.TxKey) error {
   244  	txmp.mtx.Lock()
   245  	defer txmp.mtx.Unlock()
   246  	return txmp.removeTxByKey(txKey)
   247  }
   248  
   249  // removeTxByKey removes the specified transaction key from the mempool.
   250  // The caller must hold txmp.mtx excluxively.
   251  func (txmp *TxMempool) removeTxByKey(key types.TxKey) error {
   252  	if elt, ok := txmp.txByKey[key]; ok {
   253  		w := elt.Value.(*WrappedTx)
   254  		delete(txmp.txByKey, key)
   255  		delete(txmp.txBySender, w.sender)
   256  		txmp.txs.Remove(elt)
   257  		elt.DetachPrev()
   258  		elt.DetachNext()
   259  		atomic.AddInt64(&txmp.txsBytes, -w.Size())
   260  		return nil
   261  	}
   262  	return fmt.Errorf("transaction %x not found", key)
   263  }
   264  
   265  // removeTxByElement removes the specified transaction element from the mempool.
   266  // The caller must hold txmp.mtx exclusively.
   267  func (txmp *TxMempool) removeTxByElement(elt *clist.CElement) {
   268  	w := elt.Value.(*WrappedTx)
   269  	delete(txmp.txByKey, w.tx.Key())
   270  	delete(txmp.txBySender, w.sender)
   271  	txmp.txs.Remove(elt)
   272  	elt.DetachPrev()
   273  	elt.DetachNext()
   274  	atomic.AddInt64(&txmp.txsBytes, -w.Size())
   275  }
   276  
   277  // Flush purges the contents of the mempool and the cache, leaving both empty.
   278  // The current height is not modified by this operation.
   279  func (txmp *TxMempool) Flush() {
   280  	txmp.mtx.Lock()
   281  	defer txmp.mtx.Unlock()
   282  
   283  	// Remove all the transactions in the list explicitly, so that the sizes
   284  	// and indexes get updated properly.
   285  	cur := txmp.txs.Front()
   286  	for cur != nil {
   287  		next := cur.Next()
   288  		txmp.removeTxByElement(cur)
   289  		cur = next
   290  	}
   291  	txmp.cache.Reset()
   292  }
   293  
   294  // allEntriesSorted returns a slice of all the transactions currently in the
   295  // mempool, sorted in nonincreasing order by priority with ties broken by
   296  // increasing order of arrival time.
   297  func (txmp *TxMempool) allEntriesSorted() []*WrappedTx {
   298  	txmp.mtx.RLock()
   299  	defer txmp.mtx.RUnlock()
   300  
   301  	all := make([]*WrappedTx, 0, len(txmp.txByKey))
   302  	for _, tx := range txmp.txByKey {
   303  		all = append(all, tx.Value.(*WrappedTx))
   304  	}
   305  	sort.Slice(all, func(i, j int) bool {
   306  		if all[i].priority == all[j].priority {
   307  			return all[i].timestamp.Before(all[j].timestamp)
   308  		}
   309  		return all[i].priority > all[j].priority // N.B. higher priorities first
   310  	})
   311  	return all
   312  }
   313  
   314  // ReapMaxBytesMaxGas returns a slice of valid transactions that fit within the
   315  // size and gas constraints. The results are ordered by nonincreasing priority,
   316  // with ties broken by increasing order of arrival.  Reaping transactions does
   317  // not remove them from the mempool.
   318  //
   319  // If maxBytes < 0, no limit is set on the total size in bytes.
   320  // If maxGas < 0, no limit is set on the total gas cost.
   321  //
   322  // If the mempool is empty or has no transactions fitting within the given
   323  // constraints, the result will also be empty.
   324  func (txmp *TxMempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs {
   325  	var totalGas, totalBytes int64
   326  
   327  	var keep []types.Tx //nolint:prealloc
   328  	for _, w := range txmp.allEntriesSorted() {
   329  		// N.B. When computing byte size, we need to include the overhead for
   330  		// encoding as protobuf to send to the application.
   331  		totalGas += w.gasWanted
   332  		totalBytes += types.ComputeProtoSizeForTxs([]types.Tx{w.tx})
   333  		if (maxGas >= 0 && totalGas > maxGas) || (maxBytes >= 0 && totalBytes > maxBytes) {
   334  			break
   335  		}
   336  		keep = append(keep, w.tx)
   337  	}
   338  	return keep
   339  }
   340  
   341  // TxsWaitChan returns a channel that is closed when there is at least one
   342  // transaction available to be gossiped.
   343  func (txmp *TxMempool) TxsWaitChan() <-chan struct{} { return txmp.txs.WaitChan() }
   344  
   345  // TxsFront returns the frontmost element of the pending transaction list.
   346  // It will be nil if the mempool is empty.
   347  func (txmp *TxMempool) TxsFront() *clist.CElement { return txmp.txs.Front() }
   348  
   349  // ReapMaxTxs returns up to max transactions from the mempool. The results are
   350  // ordered by nonincreasing priority with ties broken by increasing order of
   351  // arrival. Reaping transactions does not remove them from the mempool.
   352  //
   353  // If max < 0, all transactions in the mempool are reaped.
   354  //
   355  // The result may have fewer than max elements (possibly zero) if the mempool
   356  // does not have that many transactions available.
   357  func (txmp *TxMempool) ReapMaxTxs(max int) types.Txs {
   358  	var keep []types.Tx //nolint:prealloc
   359  
   360  	for _, w := range txmp.allEntriesSorted() {
   361  		if max >= 0 && len(keep) >= max {
   362  			break
   363  		}
   364  		keep = append(keep, w.tx)
   365  	}
   366  	return keep
   367  }
   368  
   369  // Update removes all the given transactions from the mempool and the cache,
   370  // and updates the current block height. The blockTxs and deliverTxResponses
   371  // must have the same length with each response corresponding to the tx at the
   372  // same offset.
   373  //
   374  // If the configuration enables recheck, Update sends each remaining
   375  // transaction after removing blockTxs to the ABCI CheckTx method.  Any
   376  // transactions marked as invalid during recheck are also removed.
   377  //
   378  // The caller must hold an exclusive mempool lock (by calling txmp.Lock) before
   379  // calling Update.
   380  func (txmp *TxMempool) Update(
   381  	blockHeight int64,
   382  	blockTxs types.Txs,
   383  	deliverTxResponses []*abci.ResponseDeliverTx,
   384  	newPreFn mempool.PreCheckFunc,
   385  	newPostFn mempool.PostCheckFunc,
   386  ) error {
   387  	// Safety check: Transactions and responses must match in number.
   388  	if len(blockTxs) != len(deliverTxResponses) {
   389  		panic(fmt.Sprintf("mempool: got %d transactions but %d DeliverTx responses",
   390  			len(blockTxs), len(deliverTxResponses)))
   391  	}
   392  
   393  	txmp.height = blockHeight
   394  	txmp.notifiedTxsAvailable = false
   395  
   396  	if newPreFn != nil {
   397  		txmp.preCheck = newPreFn
   398  	}
   399  	if newPostFn != nil {
   400  		txmp.postCheck = newPostFn
   401  	}
   402  
   403  	for i, tx := range blockTxs {
   404  		// Add successful committed transactions to the cache (if they are not
   405  		// already present).  Transactions that failed to commit are removed from
   406  		// the cache unless the operator has explicitly requested we keep them.
   407  		if deliverTxResponses[i].Code == abci.CodeTypeOK {
   408  			_ = txmp.cache.Push(tx)
   409  		} else if !txmp.config.KeepInvalidTxsInCache {
   410  			txmp.cache.Remove(tx)
   411  		}
   412  
   413  		// Regardless of success, remove the transaction from the mempool.
   414  		_ = txmp.removeTxByKey(tx.Key())
   415  	}
   416  
   417  	txmp.purgeExpiredTxs(blockHeight)
   418  
   419  	// If there any uncommitted transactions left in the mempool, we either
   420  	// initiate re-CheckTx per remaining transaction or notify that remaining
   421  	// transactions are left.
   422  	size := txmp.Size()
   423  	txmp.metrics.Size.Set(float64(size))
   424  	if size > 0 {
   425  		if txmp.config.Recheck {
   426  			txmp.recheckTransactions()
   427  		} else {
   428  			txmp.notifyTxsAvailable()
   429  		}
   430  	}
   431  	return nil
   432  }
   433  
   434  // addNewTransaction handles the ABCI CheckTx response for the first time a
   435  // transaction is added to the mempool.  A recheck after a block is committed
   436  // goes to handleRecheckResult.
   437  //
   438  // If either the application rejected the transaction or a post-check hook is
   439  // defined and rejects the transaction, it is discarded.
   440  //
   441  // Otherwise, if the mempool is full, check for lower-priority transactions
   442  // that can be evicted to make room for the new one. If no such transactions
   443  // exist, this transaction is logged and dropped; otherwise the selected
   444  // transactions are evicted.
   445  //
   446  // Finally, the new transaction is added and size stats updated.
   447  func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx, checkTxRes *abci.ResponseCheckTx) {
   448  	txmp.mtx.Lock()
   449  	defer txmp.mtx.Unlock()
   450  
   451  	var err error
   452  	if txmp.postCheck != nil {
   453  		err = txmp.postCheck(wtx.tx, checkTxRes)
   454  	}
   455  
   456  	if err != nil || checkTxRes.Code != abci.CodeTypeOK {
   457  		txmp.logger.Info(
   458  			"rejected bad transaction",
   459  			"priority", wtx.Priority(),
   460  			"tx", fmt.Sprintf("%X", wtx.tx.Hash()),
   461  			"peer_id", wtx.peers,
   462  			"code", checkTxRes.Code,
   463  			"post_check_err", err,
   464  		)
   465  
   466  		txmp.metrics.FailedTxs.Add(1)
   467  
   468  		// Remove the invalid transaction from the cache, unless the operator has
   469  		// instructed us to keep invalid transactions.
   470  		if !txmp.config.KeepInvalidTxsInCache {
   471  			txmp.cache.Remove(wtx.tx)
   472  		}
   473  
   474  		// If there was a post-check error, record its text in the result for
   475  		// debugging purposes.
   476  		if err != nil {
   477  			checkTxRes.MempoolError = err.Error()
   478  		}
   479  		return
   480  	}
   481  
   482  	priority := checkTxRes.Priority
   483  	sender := checkTxRes.Sender
   484  
   485  	// Disallow multiple concurrent transactions from the same sender assigned
   486  	// by the ABCI application. As a special case, an empty sender is not
   487  	// restricted.
   488  	if sender != "" {
   489  		elt, ok := txmp.txBySender[sender]
   490  		if ok {
   491  			w := elt.Value.(*WrappedTx)
   492  			txmp.logger.Debug(
   493  				"rejected valid incoming transaction; tx already exists for sender",
   494  				"tx", fmt.Sprintf("%X", w.tx.Hash()),
   495  				"sender", sender,
   496  			)
   497  			checkTxRes.MempoolError =
   498  				fmt.Sprintf("rejected valid incoming transaction; tx already exists for sender %q (%X)",
   499  					sender, w.tx.Hash())
   500  			txmp.metrics.RejectedTxs.Add(1)
   501  			return
   502  		}
   503  	}
   504  
   505  	// At this point the application has ruled the transaction valid, but the
   506  	// mempool might be full. If so, find the lowest-priority items with lower
   507  	// priority than the application assigned to this new one, and evict as many
   508  	// of them as necessary to make room for tx. If no such items exist, we
   509  	// discard tx.
   510  
   511  	if err := txmp.canAddTx(wtx); err != nil {
   512  		var victims []*clist.CElement // eligible transactions for eviction
   513  		var victimBytes int64         // total size of victims
   514  		for cur := txmp.txs.Front(); cur != nil; cur = cur.Next() {
   515  			cw := cur.Value.(*WrappedTx)
   516  			if cw.priority < priority {
   517  				victims = append(victims, cur)
   518  				victimBytes += cw.Size()
   519  			}
   520  		}
   521  
   522  		// If there are no suitable eviction candidates, or the total size of
   523  		// those candidates is not enough to make room for the new transaction,
   524  		// drop the new one.
   525  		if len(victims) == 0 || victimBytes < wtx.Size() {
   526  			txmp.cache.Remove(wtx.tx)
   527  			txmp.logger.Error(
   528  				"rejected valid incoming transaction; mempool is full",
   529  				"tx", fmt.Sprintf("%X", wtx.tx.Hash()),
   530  				"err", err.Error(),
   531  			)
   532  			checkTxRes.MempoolError =
   533  				fmt.Sprintf("rejected valid incoming transaction; mempool is full (%X)",
   534  					wtx.tx.Hash())
   535  			txmp.metrics.RejectedTxs.Add(1)
   536  			return
   537  		}
   538  
   539  		txmp.logger.Debug("evicting lower-priority transactions",
   540  			"new_tx", fmt.Sprintf("%X", wtx.tx.Hash()),
   541  			"new_priority", priority,
   542  		)
   543  
   544  		// Sort lowest priority items first so they will be evicted first.  Break
   545  		// ties in favor of newer items (to maintain FIFO semantics in a group).
   546  		sort.Slice(victims, func(i, j int) bool {
   547  			iw := victims[i].Value.(*WrappedTx)
   548  			jw := victims[j].Value.(*WrappedTx)
   549  			if iw.Priority() == jw.Priority() {
   550  				return iw.timestamp.After(jw.timestamp)
   551  			}
   552  			return iw.Priority() < jw.Priority()
   553  		})
   554  
   555  		// Evict as many of the victims as necessary to make room.
   556  		var evictedBytes int64
   557  		for _, vic := range victims {
   558  			w := vic.Value.(*WrappedTx)
   559  
   560  			txmp.logger.Debug(
   561  				"evicted valid existing transaction; mempool full",
   562  				"old_tx", fmt.Sprintf("%X", w.tx.Hash()),
   563  				"old_priority", w.priority,
   564  			)
   565  			txmp.removeTxByElement(vic)
   566  			txmp.cache.Remove(w.tx)
   567  			txmp.metrics.EvictedTxs.Add(1)
   568  
   569  			// We may not need to evict all the eligible transactions.  Bail out
   570  			// early if we have made enough room.
   571  			evictedBytes += w.Size()
   572  			if evictedBytes >= wtx.Size() {
   573  				break
   574  			}
   575  		}
   576  	}
   577  
   578  	wtx.SetGasWanted(checkTxRes.GasWanted)
   579  	wtx.SetPriority(priority)
   580  	wtx.SetSender(sender)
   581  	txmp.insertTx(wtx)
   582  
   583  	txmp.metrics.TxSizeBytes.Observe(float64(wtx.Size()))
   584  	txmp.metrics.Size.Set(float64(txmp.Size()))
   585  	txmp.logger.Debug(
   586  		"inserted new valid transaction",
   587  		"priority", wtx.Priority(),
   588  		"tx", fmt.Sprintf("%X", wtx.tx.Hash()),
   589  		"height", txmp.height,
   590  		"num_txs", txmp.Size(),
   591  	)
   592  	txmp.notifyTxsAvailable()
   593  }
   594  
   595  func (txmp *TxMempool) insertTx(wtx *WrappedTx) {
   596  	elt := txmp.txs.PushBack(wtx)
   597  	txmp.txByKey[wtx.tx.Key()] = elt
   598  	if s := wtx.Sender(); s != "" {
   599  		txmp.txBySender[s] = elt
   600  	}
   601  
   602  	atomic.AddInt64(&txmp.txsBytes, wtx.Size())
   603  }
   604  
   605  // handleRecheckResult handles the responses from ABCI CheckTx calls issued
   606  // during the recheck phase of a block Update.  It removes any transactions
   607  // invalidated by the application.
   608  //
   609  // This method is NOT executed for the initial CheckTx on a new transaction;
   610  // that case is handled by addNewTransaction instead.
   611  func (txmp *TxMempool) handleRecheckResult(tx types.Tx, checkTxRes *abci.ResponseCheckTx) {
   612  	txmp.metrics.RecheckTimes.Add(1)
   613  	txmp.mtx.Lock()
   614  	defer txmp.mtx.Unlock()
   615  
   616  	// Find the transaction reported by the ABCI callback. It is possible the
   617  	// transaction was evicted during the recheck, in which case the transaction
   618  	// will be gone.
   619  	elt, ok := txmp.txByKey[tx.Key()]
   620  	if !ok {
   621  		return
   622  	}
   623  	wtx := elt.Value.(*WrappedTx)
   624  
   625  	// If a postcheck hook is defined, call it before checking the result.
   626  	var err error
   627  	if txmp.postCheck != nil {
   628  		err = txmp.postCheck(tx, checkTxRes)
   629  	}
   630  
   631  	if checkTxRes.Code == abci.CodeTypeOK && err == nil {
   632  		wtx.SetPriority(checkTxRes.Priority)
   633  		return // N.B. Size of mempool did not change
   634  	}
   635  
   636  	txmp.logger.Debug(
   637  		"existing transaction no longer valid; failed re-CheckTx callback",
   638  		"priority", wtx.Priority(),
   639  		"tx", fmt.Sprintf("%X", wtx.tx.Hash()),
   640  		"err", err,
   641  		"code", checkTxRes.Code,
   642  	)
   643  	txmp.removeTxByElement(elt)
   644  	txmp.metrics.FailedTxs.Add(1)
   645  	if !txmp.config.KeepInvalidTxsInCache {
   646  		txmp.cache.Remove(wtx.tx)
   647  	}
   648  	txmp.metrics.Size.Set(float64(txmp.Size()))
   649  }
   650  
   651  // recheckTransactions initiates re-CheckTx ABCI calls for all the transactions
   652  // currently in the mempool. It reports the number of recheck calls that were
   653  // successfully initiated.
   654  //
   655  // Precondition: The mempool is not empty.
   656  // The caller must hold txmp.mtx exclusively.
   657  func (txmp *TxMempool) recheckTransactions() {
   658  	if txmp.Size() == 0 {
   659  		panic("mempool: cannot run recheck on an empty mempool")
   660  	}
   661  	txmp.logger.Debug(
   662  		"executing re-CheckTx for all remaining transactions",
   663  		"num_txs", txmp.Size(),
   664  		"height", txmp.height,
   665  	)
   666  
   667  	// Collect transactions currently in the mempool requiring recheck.
   668  	wtxs := make([]*WrappedTx, 0, txmp.txs.Len())
   669  	for e := txmp.txs.Front(); e != nil; e = e.Next() {
   670  		wtxs = append(wtxs, e.Value.(*WrappedTx))
   671  	}
   672  
   673  	// Issue CheckTx calls for each remaining transaction, and when all the
   674  	// rechecks are complete signal watchers that transactions may be available.
   675  	go func() {
   676  		g, start := taskgroup.New(nil).Limit(2 * runtime.NumCPU())
   677  
   678  		for _, wtx := range wtxs {
   679  			wtx := wtx
   680  			start(func() error {
   681  				// The response for this CheckTx is handled by the default recheckTxCallback.
   682  				rsp, err := txmp.proxyAppConn.CheckTxSync(abci.RequestCheckTx{
   683  					Tx:   wtx.tx,
   684  					Type: abci.CheckTxType_Recheck,
   685  				})
   686  				if err != nil {
   687  					txmp.logger.Error("failed to execute CheckTx during recheck",
   688  						"err", err, "hash", fmt.Sprintf("%x", wtx.tx.Hash()))
   689  				} else {
   690  					txmp.handleRecheckResult(wtx.tx, rsp)
   691  				}
   692  				return nil
   693  			})
   694  		}
   695  		_ = txmp.proxyAppConn.FlushAsync()
   696  
   697  		// When recheck is complete, trigger a notification for more transactions.
   698  		_ = g.Wait()
   699  		txmp.mtx.Lock()
   700  		defer txmp.mtx.Unlock()
   701  		txmp.notifyTxsAvailable()
   702  	}()
   703  }
   704  
   705  // canAddTx returns an error if we cannot insert the provided *WrappedTx into
   706  // the mempool due to mempool configured constraints. Otherwise, nil is
   707  // returned and the transaction can be inserted into the mempool.
   708  func (txmp *TxMempool) canAddTx(wtx *WrappedTx) error {
   709  	numTxs := txmp.Size()
   710  	txBytes := txmp.SizeBytes()
   711  
   712  	if numTxs >= txmp.config.Size || wtx.Size()+txBytes > txmp.config.MaxTxsBytes {
   713  		return mempool.ErrMempoolIsFull{
   714  			NumTxs:      numTxs,
   715  			MaxTxs:      txmp.config.Size,
   716  			TxsBytes:    txBytes,
   717  			MaxTxsBytes: txmp.config.MaxTxsBytes,
   718  		}
   719  	}
   720  
   721  	return nil
   722  }
   723  
   724  // purgeExpiredTxs removes all transactions from the mempool that have exceeded
   725  // their respective height or time-based limits as of the given blockHeight.
   726  // Transactions removed by this operation are not removed from the cache.
   727  //
   728  // The caller must hold txmp.mtx exclusively.
   729  func (txmp *TxMempool) purgeExpiredTxs(blockHeight int64) {
   730  	if txmp.config.TTLNumBlocks == 0 && txmp.config.TTLDuration == 0 {
   731  		return // nothing to do
   732  	}
   733  
   734  	now := time.Now()
   735  	cur := txmp.txs.Front()
   736  	for cur != nil {
   737  		// N.B. Grab the next element first, since if we remove cur its successor
   738  		// will be invalidated.
   739  		next := cur.Next()
   740  
   741  		w := cur.Value.(*WrappedTx)
   742  		if txmp.config.TTLNumBlocks > 0 && (blockHeight-w.height) > txmp.config.TTLNumBlocks {
   743  			txmp.removeTxByElement(cur)
   744  			txmp.cache.Remove(w.tx)
   745  			txmp.metrics.EvictedTxs.Add(1)
   746  		} else if txmp.config.TTLDuration > 0 && now.Sub(w.timestamp) > txmp.config.TTLDuration {
   747  			txmp.removeTxByElement(cur)
   748  			txmp.cache.Remove(w.tx)
   749  			txmp.metrics.EvictedTxs.Add(1)
   750  		}
   751  		cur = next
   752  	}
   753  }
   754  
   755  func (txmp *TxMempool) notifyTxsAvailable() {
   756  	if txmp.Size() == 0 {
   757  		return // nothing to do
   758  	}
   759  
   760  	if txmp.txsAvailable != nil && !txmp.notifiedTxsAvailable {
   761  		// channel cap is 1, so this will send once
   762  		txmp.notifiedTxsAvailable = true
   763  
   764  		select {
   765  		case txmp.txsAvailable <- struct{}{}:
   766  		default:
   767  		}
   768  	}
   769  }