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