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