github.com/number571/tendermint@v0.34.11-gost/internal/mempool/v1/mempool.go (about)

     1  package v1
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"sync/atomic"
     8  	"time"
     9  
    10  	abci "github.com/number571/tendermint/abci/types"
    11  	"github.com/number571/tendermint/config"
    12  	"github.com/number571/tendermint/internal/libs/clist"
    13  	tmsync "github.com/number571/tendermint/internal/libs/sync"
    14  	"github.com/number571/tendermint/internal/mempool"
    15  	"github.com/number571/tendermint/libs/log"
    16  	tmmath "github.com/number571/tendermint/libs/math"
    17  	pubmempool "github.com/number571/tendermint/pkg/mempool"
    18  	"github.com/number571/tendermint/proxy"
    19  	"github.com/number571/tendermint/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 defines a prioritized mempool data structure used by the v1 mempool
    28  // reactor. It keeps a thread-safe priority queue of transactions that is used
    29  // when a block proposer constructs a block and a thread-safe linked-list that
    30  // is used to gossip transactions to peers in a FIFO manner.
    31  type TxMempool struct {
    32  	logger       log.Logger
    33  	metrics      *mempool.Metrics
    34  	config       *config.MempoolConfig
    35  	proxyAppConn proxy.AppConnMempool
    36  
    37  	// txsAvailable fires once for each height when the mempool is not empty
    38  	txsAvailable         chan struct{}
    39  	notifiedTxsAvailable bool
    40  
    41  	// height defines the last block height process during Update()
    42  	height int64
    43  
    44  	// sizeBytes defines the total size of the mempool (sum of all tx bytes)
    45  	sizeBytes int64
    46  
    47  	// cache defines a fixed-size cache of already seen transactions as this
    48  	// reduces pressure on the proxyApp.
    49  	cache mempool.TxCache
    50  
    51  	// txStore defines the main storage of valid transactions. Indexes are built
    52  	// on top of this store.
    53  	txStore *TxStore
    54  
    55  	// gossipIndex defines the gossiping index of valid transactions via a
    56  	// thread-safe linked-list. We also use the gossip index as a cursor for
    57  	// rechecking transactions already in the mempool.
    58  	gossipIndex *clist.CList
    59  
    60  	// recheckCursor and recheckEnd are used as cursors based on the gossip index
    61  	// to recheck transactions that are already in the mempool. Iteration is not
    62  	// thread-safe and transaction may be mutated in serial order.
    63  	//
    64  	// XXX/TODO: It might be somewhat of a codesmell to use the gossip index for
    65  	// iterator and cursor management when rechecking transactions. If the gossip
    66  	// index changes or is removed in a future refactor, this will have to be
    67  	// refactored. Instead, we should consider just keeping a slice of a snapshot
    68  	// of the mempool's current transactions during Update and an integer cursor
    69  	// into that slice. This, however, requires additional O(n) space complexity.
    70  	recheckCursor *clist.CElement // next expected response
    71  	recheckEnd    *clist.CElement // re-checking stops here
    72  
    73  	// priorityIndex defines the priority index of valid transactions via a
    74  	// thread-safe priority queue.
    75  	priorityIndex *TxPriorityQueue
    76  
    77  	// heightIndex defines a height-based, in ascending order, transaction index.
    78  	// i.e. older transactions are first.
    79  	heightIndex *WrappedTxList
    80  
    81  	// timestampIndex defines a timestamp-based, in ascending order, transaction
    82  	// index. i.e. older transactions are first.
    83  	timestampIndex *WrappedTxList
    84  
    85  	// A read/write lock is used to safe guard updates, insertions and deletions
    86  	// from the mempool. A read-lock is implicitly acquired when executing CheckTx,
    87  	// however, a caller must explicitly grab a write-lock via Lock when updating
    88  	// the mempool via Update().
    89  	mtx       tmsync.RWMutex
    90  	preCheck  mempool.PreCheckFunc
    91  	postCheck mempool.PostCheckFunc
    92  }
    93  
    94  func NewTxMempool(
    95  	logger log.Logger,
    96  	cfg *config.MempoolConfig,
    97  	proxyAppConn proxy.AppConnMempool,
    98  	height int64,
    99  	options ...TxMempoolOption,
   100  ) *TxMempool {
   101  
   102  	txmp := &TxMempool{
   103  		logger:        logger,
   104  		config:        cfg,
   105  		proxyAppConn:  proxyAppConn,
   106  		height:        height,
   107  		cache:         mempool.NopTxCache{},
   108  		metrics:       mempool.NopMetrics(),
   109  		txStore:       NewTxStore(),
   110  		gossipIndex:   clist.New(),
   111  		priorityIndex: NewTxPriorityQueue(),
   112  		heightIndex: NewWrappedTxList(func(wtx1, wtx2 *WrappedTx) bool {
   113  			return wtx1.height >= wtx2.height
   114  		}),
   115  		timestampIndex: NewWrappedTxList(func(wtx1, wtx2 *WrappedTx) bool {
   116  			return wtx1.timestamp.After(wtx2.timestamp) || wtx1.timestamp.Equal(wtx2.timestamp)
   117  		}),
   118  	}
   119  
   120  	if cfg.CacheSize > 0 {
   121  		txmp.cache = mempool.NewLRUTxCache(cfg.CacheSize)
   122  	}
   123  
   124  	proxyAppConn.SetResponseCallback(txmp.defaultTxCallback)
   125  
   126  	for _, opt := range options {
   127  		opt(txmp)
   128  	}
   129  
   130  	return txmp
   131  }
   132  
   133  // WithPreCheck sets a filter for the mempool to reject a transaction if f(tx)
   134  // returns an error. This is executed before CheckTx. It only applies to the
   135  // first created block. After that, Update() overwrites the existing value.
   136  func WithPreCheck(f mempool.PreCheckFunc) TxMempoolOption {
   137  	return func(txmp *TxMempool) { txmp.preCheck = f }
   138  }
   139  
   140  // WithPostCheck sets a filter for the mempool to reject a transaction if
   141  // f(tx, resp) returns an error. This is executed after CheckTx. It only applies
   142  // to the first created block. After that, Update overwrites the existing value.
   143  func WithPostCheck(f mempool.PostCheckFunc) TxMempoolOption {
   144  	return func(txmp *TxMempool) { txmp.postCheck = f }
   145  }
   146  
   147  // WithMetrics sets the mempool's metrics collector.
   148  func WithMetrics(metrics *mempool.Metrics) TxMempoolOption {
   149  	return func(txmp *TxMempool) { txmp.metrics = metrics }
   150  }
   151  
   152  // Lock obtains a write-lock on the mempool. A caller must be sure to explicitly
   153  // release the lock when finished.
   154  func (txmp *TxMempool) Lock() {
   155  	txmp.mtx.Lock()
   156  }
   157  
   158  // Unlock releases a write-lock on the mempool.
   159  func (txmp *TxMempool) Unlock() {
   160  	txmp.mtx.Unlock()
   161  }
   162  
   163  // Size returns the number of valid transactions in the mempool. It is
   164  // thread-safe.
   165  func (txmp *TxMempool) Size() int {
   166  	return txmp.txStore.Size()
   167  }
   168  
   169  // SizeBytes return the total sum in bytes of all the valid transactions in the
   170  // mempool. It is thread-safe.
   171  func (txmp *TxMempool) SizeBytes() int64 {
   172  	return atomic.LoadInt64(&txmp.sizeBytes)
   173  }
   174  
   175  // FlushAppConn executes FlushSync on the mempool's proxyAppConn.
   176  //
   177  // NOTE: The caller must obtain a write-lock via Lock() prior to execution.
   178  func (txmp *TxMempool) FlushAppConn() error {
   179  	return txmp.proxyAppConn.FlushSync(context.Background())
   180  }
   181  
   182  // WaitForNextTx returns a blocking channel that will be closed when the next
   183  // valid transaction is available to gossip. It is thread-safe.
   184  func (txmp *TxMempool) WaitForNextTx() <-chan struct{} {
   185  	return txmp.gossipIndex.WaitChan()
   186  }
   187  
   188  // NextGossipTx returns the next valid transaction to gossip. A caller must wait
   189  // for WaitForNextTx to signal a transaction is available to gossip first. It is
   190  // thread-safe.
   191  func (txmp *TxMempool) NextGossipTx() *WrappedTx {
   192  	return txmp.gossipIndex.Front().Value.(*WrappedTx)
   193  }
   194  
   195  // EnableTxsAvailable enables the mempool to trigger events when transactions
   196  // are available on a block by block basis.
   197  func (txmp *TxMempool) EnableTxsAvailable() {
   198  	txmp.mtx.Lock()
   199  	defer txmp.mtx.Unlock()
   200  
   201  	txmp.txsAvailable = make(chan struct{}, 1)
   202  }
   203  
   204  // TxsAvailable returns a channel which fires once for every height, and only
   205  // when transactions are available in the mempool. It is thread-safe.
   206  func (txmp *TxMempool) TxsAvailable() <-chan struct{} {
   207  	return txmp.txsAvailable
   208  }
   209  
   210  // CheckTx executes the ABCI CheckTx method for a given transaction. It acquires
   211  // a read-lock attempts to execute the application's CheckTx ABCI method via
   212  // CheckTxAsync. We return an error if any of the following happen:
   213  //
   214  // - The CheckTxAsync execution fails.
   215  // - The transaction already exists in the cache and we've already received the
   216  //   transaction from the peer. Otherwise, if it solely exists in the cache, we
   217  //   return nil.
   218  // - The transaction size exceeds the maximum transaction size as defined by the
   219  //   configuration provided to the mempool.
   220  // - The transaction fails Pre-Check (if it is defined).
   221  // - The proxyAppConn fails, e.g. the buffer is full.
   222  //
   223  // If the mempool is full, we still execute CheckTx and attempt to find a lower
   224  // priority transaction to evict. If such a transaction exists, we remove the
   225  // lower priority transaction and add the new one with higher priority.
   226  //
   227  // NOTE:
   228  // - The applications' CheckTx implementation may panic.
   229  // - The caller is not to explicitly require any locks for executing CheckTx.
   230  func (txmp *TxMempool) CheckTx(
   231  	ctx context.Context,
   232  	tx types.Tx,
   233  	cb func(*abci.Response),
   234  	txInfo mempool.TxInfo,
   235  ) error {
   236  
   237  	txmp.mtx.RLock()
   238  	defer txmp.mtx.RUnlock()
   239  
   240  	txSize := len(tx)
   241  	if txSize > txmp.config.MaxTxBytes {
   242  		return pubmempool.ErrTxTooLarge{
   243  			Max:    txmp.config.MaxTxBytes,
   244  			Actual: txSize,
   245  		}
   246  	}
   247  
   248  	if txmp.preCheck != nil {
   249  		if err := txmp.preCheck(tx); err != nil {
   250  			return pubmempool.ErrPreCheck{
   251  				Reason: err,
   252  			}
   253  		}
   254  	}
   255  
   256  	if err := txmp.proxyAppConn.Error(); err != nil {
   257  		return err
   258  	}
   259  
   260  	txHash := mempool.TxKey(tx)
   261  
   262  	// We add the transaction to the mempool's cache and if the transaction already
   263  	// exists, i.e. false is returned, then we check if we've seen this transaction
   264  	// from the same sender and error if we have. Otherwise, we return nil.
   265  	if !txmp.cache.Push(tx) {
   266  		wtx, ok := txmp.txStore.GetOrSetPeerByTxHash(txHash, txInfo.SenderID)
   267  		if wtx != nil && ok {
   268  			// We already have the transaction stored and the we've already seen this
   269  			// transaction from txInfo.SenderID.
   270  			return pubmempool.ErrTxInCache
   271  		}
   272  
   273  		txmp.logger.Debug("tx exists already in cache", "tx_hash", tx.Hash())
   274  		return nil
   275  	}
   276  
   277  	if ctx == nil {
   278  		ctx = context.Background()
   279  	}
   280  
   281  	reqRes, err := txmp.proxyAppConn.CheckTxAsync(ctx, abci.RequestCheckTx{Tx: tx})
   282  	if err != nil {
   283  		txmp.cache.Remove(tx)
   284  		return err
   285  	}
   286  
   287  	reqRes.SetCallback(func(res *abci.Response) {
   288  		if txmp.recheckCursor != nil {
   289  			panic("recheck cursor is non-nil in CheckTx callback")
   290  		}
   291  
   292  		wtx := &WrappedTx{
   293  			tx:        tx,
   294  			hash:      txHash,
   295  			timestamp: time.Now().UTC(),
   296  			height:    txmp.height,
   297  		}
   298  		txmp.initTxCallback(wtx, res, txInfo)
   299  
   300  		if cb != nil {
   301  			cb(res)
   302  		}
   303  	})
   304  
   305  	return nil
   306  }
   307  
   308  // Flush flushes out the mempool. It acquires a read-lock, fetches all the
   309  // transactions currently in the transaction store and removes each transaction
   310  // from the store and all indexes and finally resets the cache.
   311  //
   312  // NOTE:
   313  // - Flushing the mempool may leave the mempool in an inconsistent state.
   314  func (txmp *TxMempool) Flush() {
   315  	txmp.mtx.RLock()
   316  	defer txmp.mtx.RUnlock()
   317  
   318  	txmp.heightIndex.Reset()
   319  	txmp.timestampIndex.Reset()
   320  
   321  	for _, wtx := range txmp.txStore.GetAllTxs() {
   322  		txmp.removeTx(wtx, false)
   323  	}
   324  
   325  	atomic.SwapInt64(&txmp.sizeBytes, 0)
   326  	txmp.cache.Reset()
   327  }
   328  
   329  // ReapMaxBytesMaxGas returns a list of transactions within the provided size
   330  // and gas constraints. Transaction are retrieved in priority order.
   331  //
   332  // NOTE:
   333  // - A read-lock is acquired.
   334  // - Transactions returned are not actually removed from the mempool transaction
   335  //   store or indexes.
   336  func (txmp *TxMempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs {
   337  	txmp.mtx.RLock()
   338  	defer txmp.mtx.RUnlock()
   339  
   340  	var (
   341  		totalGas  int64
   342  		totalSize int64
   343  	)
   344  
   345  	// wTxs contains a list of *WrappedTx retrieved from the priority queue that
   346  	// need to be re-enqueued prior to returning.
   347  	wTxs := make([]*WrappedTx, 0, txmp.priorityIndex.NumTxs())
   348  	defer func() {
   349  		for _, wtx := range wTxs {
   350  			txmp.priorityIndex.PushTx(wtx)
   351  		}
   352  	}()
   353  
   354  	txs := make([]types.Tx, 0, txmp.priorityIndex.NumTxs())
   355  	for txmp.priorityIndex.NumTxs() > 0 {
   356  		wtx := txmp.priorityIndex.PopTx()
   357  		txs = append(txs, wtx.tx)
   358  		wTxs = append(wTxs, wtx)
   359  		size := types.ComputeProtoSizeForTxs([]types.Tx{wtx.tx})
   360  
   361  		// Ensure we have capacity for the transaction with respect to the
   362  		// transaction size.
   363  		if maxBytes > -1 && totalSize+size > maxBytes {
   364  			return txs[:len(txs)-1]
   365  		}
   366  
   367  		totalSize += size
   368  
   369  		// ensure we have capacity for the transaction with respect to total gas
   370  		gas := totalGas + wtx.gasWanted
   371  		if maxGas > -1 && gas > maxGas {
   372  			return txs[:len(txs)-1]
   373  		}
   374  
   375  		totalGas = gas
   376  	}
   377  
   378  	return txs
   379  }
   380  
   381  // ReapMaxTxs returns a list of transactions within the provided number of
   382  // transactions bound. Transaction are retrieved in priority order.
   383  //
   384  // NOTE:
   385  // - A read-lock is acquired.
   386  // - Transactions returned are not actually removed from the mempool transaction
   387  //   store or indexes.
   388  func (txmp *TxMempool) ReapMaxTxs(max int) types.Txs {
   389  	txmp.mtx.RLock()
   390  	defer txmp.mtx.RUnlock()
   391  
   392  	numTxs := txmp.priorityIndex.NumTxs()
   393  	if max < 0 {
   394  		max = numTxs
   395  	}
   396  
   397  	cap := tmmath.MinInt(numTxs, max)
   398  
   399  	// wTxs contains a list of *WrappedTx retrieved from the priority queue that
   400  	// need to be re-enqueued prior to returning.
   401  	wTxs := make([]*WrappedTx, 0, cap)
   402  	defer func() {
   403  		for _, wtx := range wTxs {
   404  			txmp.priorityIndex.PushTx(wtx)
   405  		}
   406  	}()
   407  
   408  	txs := make([]types.Tx, 0, cap)
   409  	for txmp.priorityIndex.NumTxs() > 0 && len(txs) < max {
   410  		wtx := txmp.priorityIndex.PopTx()
   411  		txs = append(txs, wtx.tx)
   412  		wTxs = append(wTxs, wtx)
   413  	}
   414  
   415  	return txs
   416  }
   417  
   418  // Update iterates over all the transactions provided by the caller, i.e. the
   419  // block producer, and removes them from the cache (if applicable) and removes
   420  // the transactions from the main transaction store and associated indexes.
   421  // Finally, if there are trainsactions remaining in the mempool, we initiate a
   422  // re-CheckTx for them (if applicable), otherwise, we notify the caller more
   423  // transactions are available.
   424  //
   425  // NOTE:
   426  // - The caller must explicitly acquire a write-lock via Lock().
   427  func (txmp *TxMempool) Update(
   428  	blockHeight int64,
   429  	blockTxs types.Txs,
   430  	deliverTxResponses []*abci.ResponseDeliverTx,
   431  	newPreFn mempool.PreCheckFunc,
   432  	newPostFn mempool.PostCheckFunc,
   433  ) error {
   434  
   435  	txmp.height = blockHeight
   436  	txmp.notifiedTxsAvailable = false
   437  
   438  	if newPreFn != nil {
   439  		txmp.preCheck = newPreFn
   440  	}
   441  	if newPostFn != nil {
   442  		txmp.postCheck = newPostFn
   443  	}
   444  
   445  	for i, tx := range blockTxs {
   446  		if deliverTxResponses[i].Code == abci.CodeTypeOK {
   447  			// add the valid committed transaction to the cache (if missing)
   448  			_ = txmp.cache.Push(tx)
   449  		} else if !txmp.config.KeepInvalidTxsInCache {
   450  			// allow invalid transactions to be re-submitted
   451  			txmp.cache.Remove(tx)
   452  		}
   453  
   454  		// remove the committed transaction from the transaction store and indexes
   455  		if wtx := txmp.txStore.GetTxByHash(mempool.TxKey(tx)); wtx != nil {
   456  			txmp.removeTx(wtx, false)
   457  		}
   458  	}
   459  
   460  	txmp.purgeExpiredTxs(blockHeight)
   461  
   462  	// If there any uncommitted transactions left in the mempool, we either
   463  	// initiate re-CheckTx per remaining transaction or notify that remaining
   464  	// transactions are left.
   465  	if txmp.Size() > 0 {
   466  		if txmp.config.Recheck {
   467  			txmp.logger.Debug(
   468  				"executing re-CheckTx for all remaining transactions",
   469  				"num_txs", txmp.Size(),
   470  				"height", blockHeight,
   471  			)
   472  			txmp.updateReCheckTxs()
   473  		} else {
   474  			txmp.notifyTxsAvailable()
   475  		}
   476  	}
   477  
   478  	txmp.metrics.Size.Set(float64(txmp.Size()))
   479  	return nil
   480  }
   481  
   482  // initTxCallback performs the initial, i.e. the first, callback after CheckTx
   483  // has been executed by the ABCI application. In other words, initTxCallback is
   484  // called after executing CheckTx when we see a unique transaction for the first
   485  // time. CheckTx can be called again for the same transaction at a later point
   486  // in time when re-checking, however, this callback will not be called.
   487  //
   488  // After the ABCI application executes CheckTx, initTxCallback is called with
   489  // the ABCI *Response object and TxInfo. If postCheck is defined on the mempool,
   490  // we execute that first. If there is no error from postCheck (if defined) and
   491  // the ABCI CheckTx response code is OK, we attempt to insert the transaction.
   492  //
   493  // When attempting to insert the transaction, we first check if there is
   494  // sufficient capacity. If there is sufficient capacity, the transaction is
   495  // inserted into the txStore and indexed across all indexes. Otherwise, if the
   496  // mempool is full, we attempt to find a lower priority transaction to evict in
   497  // place of the new incoming transaction. If no such transaction exists, the
   498  // new incoming transaction is rejected.
   499  //
   500  // If the new incoming transaction fails CheckTx or postCheck fails, we reject
   501  // the new incoming transaction.
   502  //
   503  // NOTE:
   504  // - An explicit lock is NOT required.
   505  func (txmp *TxMempool) initTxCallback(wtx *WrappedTx, res *abci.Response, txInfo mempool.TxInfo) {
   506  	checkTxRes, ok := res.Value.(*abci.Response_CheckTx)
   507  	if !ok {
   508  		return
   509  	}
   510  
   511  	var err error
   512  	if txmp.postCheck != nil {
   513  		err = txmp.postCheck(wtx.tx, checkTxRes.CheckTx)
   514  	}
   515  
   516  	if err != nil || checkTxRes.CheckTx.Code != abci.CodeTypeOK {
   517  		// ignore bad transactions
   518  		txmp.logger.Info(
   519  			"rejected bad transaction",
   520  			"priority", wtx.priority,
   521  			"tx", fmt.Sprintf("%X", wtx.tx.Hash()),
   522  			"peer_id", txInfo.SenderNodeID,
   523  			"code", checkTxRes.CheckTx.Code,
   524  			"post_check_err", err,
   525  		)
   526  
   527  		txmp.metrics.FailedTxs.Add(1)
   528  
   529  		if !txmp.config.KeepInvalidTxsInCache {
   530  			txmp.cache.Remove(wtx.tx)
   531  		}
   532  		if err != nil {
   533  			checkTxRes.CheckTx.MempoolError = err.Error()
   534  		}
   535  		return
   536  	}
   537  
   538  	sender := checkTxRes.CheckTx.Sender
   539  	priority := checkTxRes.CheckTx.Priority
   540  
   541  	if len(sender) > 0 {
   542  		if wtx := txmp.txStore.GetTxBySender(sender); wtx != nil {
   543  			txmp.logger.Error(
   544  				"rejected incoming good transaction; tx already exists for sender",
   545  				"tx", fmt.Sprintf("%X", wtx.tx.Hash()),
   546  				"sender", sender,
   547  			)
   548  			txmp.metrics.RejectedTxs.Add(1)
   549  			return
   550  		}
   551  	}
   552  
   553  	if err := txmp.canAddTx(wtx); err != nil {
   554  		evictTxs := txmp.priorityIndex.GetEvictableTxs(
   555  			priority,
   556  			int64(wtx.Size()),
   557  			txmp.SizeBytes(),
   558  			txmp.config.MaxTxsBytes,
   559  		)
   560  		if len(evictTxs) == 0 {
   561  			// No room for the new incoming transaction so we just remove it from
   562  			// the cache.
   563  			txmp.cache.Remove(wtx.tx)
   564  			txmp.logger.Error(
   565  				"rejected incoming good transaction; mempool full",
   566  				"tx", fmt.Sprintf("%X", wtx.tx.Hash()),
   567  				"err", err.Error(),
   568  			)
   569  			txmp.metrics.RejectedTxs.Add(1)
   570  			return
   571  		}
   572  
   573  		// evict an existing transaction(s)
   574  		//
   575  		// NOTE:
   576  		// - The transaction, toEvict, can be removed while a concurrent
   577  		//   reCheckTx callback is being executed for the same transaction.
   578  		for _, toEvict := range evictTxs {
   579  			txmp.removeTx(toEvict, true)
   580  			txmp.logger.Debug(
   581  				"evicted existing good transaction; mempool full",
   582  				"old_tx", fmt.Sprintf("%X", toEvict.tx.Hash()),
   583  				"old_priority", toEvict.priority,
   584  				"new_tx", fmt.Sprintf("%X", wtx.tx.Hash()),
   585  				"new_priority", wtx.priority,
   586  			)
   587  			txmp.metrics.EvictedTxs.Add(1)
   588  		}
   589  	}
   590  
   591  	wtx.gasWanted = checkTxRes.CheckTx.GasWanted
   592  	wtx.priority = priority
   593  	wtx.sender = sender
   594  	wtx.peers = map[uint16]struct{}{
   595  		txInfo.SenderID: {},
   596  	}
   597  
   598  	txmp.metrics.TxSizeBytes.Observe(float64(wtx.Size()))
   599  	txmp.metrics.Size.Set(float64(txmp.Size()))
   600  
   601  	txmp.insertTx(wtx)
   602  	txmp.logger.Debug(
   603  		"inserted good transaction",
   604  		"priority", wtx.priority,
   605  		"tx", fmt.Sprintf("%X", wtx.tx.Hash()),
   606  		"height", txmp.height,
   607  		"num_txs", txmp.Size(),
   608  	)
   609  	txmp.notifyTxsAvailable()
   610  
   611  }
   612  
   613  // defaultTxCallback performs the default CheckTx application callback. This is
   614  // NOT executed when a transaction is first seen/received. Instead, this callback
   615  // is executed during re-checking transactions (if enabled). A caller, i.e a
   616  // block proposer, acquires a mempool write-lock via Lock() and when executing
   617  // Update(), if the mempool is non-empty and Recheck is enabled, then all
   618  // remaining transactions will be rechecked via CheckTxAsync. The order in which
   619  // they are rechecked must be the same order in which this callback is called
   620  // per transaction.
   621  func (txmp *TxMempool) defaultTxCallback(req *abci.Request, res *abci.Response) {
   622  	if txmp.recheckCursor == nil {
   623  		return
   624  	}
   625  
   626  	txmp.metrics.RecheckTimes.Add(1)
   627  
   628  	checkTxRes, ok := res.Value.(*abci.Response_CheckTx)
   629  	if ok {
   630  		tx := req.GetCheckTx().Tx
   631  		wtx := txmp.recheckCursor.Value.(*WrappedTx)
   632  		if !bytes.Equal(tx, wtx.tx) {
   633  			panic(fmt.Sprintf("re-CheckTx transaction mismatch; got: %X, expected: %X", wtx.tx.Hash(), mempool.TxKey(tx)))
   634  		}
   635  
   636  		// Only evaluate transactions that have not been removed. This can happen
   637  		// if an existing transaction is evicted during CheckTx and while this
   638  		// callback is being executed for the same evicted transaction.
   639  		if !txmp.txStore.IsTxRemoved(wtx.hash) {
   640  			var err error
   641  			if txmp.postCheck != nil {
   642  				err = txmp.postCheck(tx, checkTxRes.CheckTx)
   643  			}
   644  
   645  			if checkTxRes.CheckTx.Code == abci.CodeTypeOK && err == nil {
   646  				wtx.priority = checkTxRes.CheckTx.Priority
   647  			} else {
   648  				txmp.logger.Debug(
   649  					"existing transaction no longer valid; failed re-CheckTx callback",
   650  					"priority", wtx.priority,
   651  					"tx", fmt.Sprintf("%X", mempool.TxHashFromBytes(wtx.tx)),
   652  					"err", err,
   653  					"code", checkTxRes.CheckTx.Code,
   654  				)
   655  
   656  				if wtx.gossipEl != txmp.recheckCursor {
   657  					panic("corrupted reCheckTx cursor")
   658  				}
   659  
   660  				txmp.removeTx(wtx, !txmp.config.KeepInvalidTxsInCache)
   661  			}
   662  		}
   663  
   664  		// move reCheckTx cursor to next element
   665  		if txmp.recheckCursor == txmp.recheckEnd {
   666  			txmp.recheckCursor = nil
   667  		} else {
   668  			txmp.recheckCursor = txmp.recheckCursor.Next()
   669  		}
   670  
   671  		if txmp.recheckCursor == nil {
   672  			txmp.logger.Debug("finished rechecking transactions")
   673  
   674  			if txmp.Size() > 0 {
   675  				txmp.notifyTxsAvailable()
   676  			}
   677  		}
   678  
   679  		txmp.metrics.Size.Set(float64(txmp.Size()))
   680  	}
   681  }
   682  
   683  // updateReCheckTxs updates the recheck cursors by using the gossipIndex. For
   684  // each transaction, it executes CheckTxAsync. The global callback defined on
   685  // the proxyAppConn will be executed for each transaction after CheckTx is
   686  // executed.
   687  //
   688  // NOTE:
   689  // - The caller must have a write-lock when executing updateReCheckTxs.
   690  func (txmp *TxMempool) updateReCheckTxs() {
   691  	if txmp.Size() == 0 {
   692  		panic("attempted to update re-CheckTx txs when mempool is empty")
   693  	}
   694  
   695  	txmp.recheckCursor = txmp.gossipIndex.Front()
   696  	txmp.recheckEnd = txmp.gossipIndex.Back()
   697  	ctx := context.Background()
   698  
   699  	for e := txmp.gossipIndex.Front(); e != nil; e = e.Next() {
   700  		wtx := e.Value.(*WrappedTx)
   701  
   702  		// Only execute CheckTx if the transaction is not marked as removed which
   703  		// could happen if the transaction was evicted.
   704  		if !txmp.txStore.IsTxRemoved(wtx.hash) {
   705  			_, err := txmp.proxyAppConn.CheckTxAsync(ctx, abci.RequestCheckTx{
   706  				Tx:   wtx.tx,
   707  				Type: abci.CheckTxType_Recheck,
   708  			})
   709  			if err != nil {
   710  				// no need in retrying since the tx will be rechecked after the next block
   711  				txmp.logger.Error("failed to execute CheckTx during rechecking", "err", err)
   712  			}
   713  		}
   714  	}
   715  
   716  	if _, err := txmp.proxyAppConn.FlushAsync(ctx); err != nil {
   717  		txmp.logger.Error("failed to flush transactions during rechecking", "err", err)
   718  	}
   719  }
   720  
   721  // canAddTx returns an error if we cannot insert the provided *WrappedTx into
   722  // the mempool due to mempool configured constraints. Otherwise, nil is returned
   723  // and the transaction can be inserted into the mempool.
   724  func (txmp *TxMempool) canAddTx(wtx *WrappedTx) error {
   725  	var (
   726  		numTxs    = txmp.Size()
   727  		sizeBytes = txmp.SizeBytes()
   728  	)
   729  
   730  	if numTxs >= txmp.config.Size || int64(wtx.Size())+sizeBytes > txmp.config.MaxTxsBytes {
   731  		return pubmempool.ErrMempoolIsFull{
   732  			NumTxs:      numTxs,
   733  			MaxTxs:      txmp.config.Size,
   734  			TxsBytes:    sizeBytes,
   735  			MaxTxsBytes: txmp.config.MaxTxsBytes,
   736  		}
   737  	}
   738  
   739  	return nil
   740  }
   741  
   742  func (txmp *TxMempool) insertTx(wtx *WrappedTx) {
   743  	txmp.txStore.SetTx(wtx)
   744  	txmp.priorityIndex.PushTx(wtx)
   745  	txmp.heightIndex.Insert(wtx)
   746  	txmp.timestampIndex.Insert(wtx)
   747  
   748  	// Insert the transaction into the gossip index and mark the reference to the
   749  	// linked-list element, which will be needed at a later point when the
   750  	// transaction is removed.
   751  	gossipEl := txmp.gossipIndex.PushBack(wtx)
   752  	wtx.gossipEl = gossipEl
   753  
   754  	atomic.AddInt64(&txmp.sizeBytes, int64(wtx.Size()))
   755  }
   756  
   757  func (txmp *TxMempool) removeTx(wtx *WrappedTx, removeFromCache bool) {
   758  	if txmp.txStore.IsTxRemoved(wtx.hash) {
   759  		return
   760  	}
   761  
   762  	txmp.txStore.RemoveTx(wtx)
   763  	txmp.priorityIndex.RemoveTx(wtx)
   764  	txmp.heightIndex.Remove(wtx)
   765  	txmp.timestampIndex.Remove(wtx)
   766  
   767  	// Remove the transaction from the gossip index and cleanup the linked-list
   768  	// element so it can be garbage collected.
   769  	txmp.gossipIndex.Remove(wtx.gossipEl)
   770  	wtx.gossipEl.DetachPrev()
   771  
   772  	atomic.AddInt64(&txmp.sizeBytes, int64(-wtx.Size()))
   773  
   774  	if removeFromCache {
   775  		txmp.cache.Remove(wtx.tx)
   776  	}
   777  }
   778  
   779  // purgeExpiredTxs removes all transactions that have exceeded their respective
   780  // height and/or time based TTLs from their respective indexes. Every expired
   781  // transaction will be removed from the mempool entirely, except for the cache.
   782  //
   783  // NOTE: purgeExpiredTxs must only be called during TxMempool#Update in which
   784  // the caller has a write-lock on the mempool and so we can safely iterate over
   785  // the height and time based indexes.
   786  func (txmp *TxMempool) purgeExpiredTxs(blockHeight int64) {
   787  	now := time.Now()
   788  	expiredTxs := make(map[[mempool.TxKeySize]byte]*WrappedTx)
   789  
   790  	if txmp.config.TTLNumBlocks > 0 {
   791  		purgeIdx := -1
   792  		for i, wtx := range txmp.heightIndex.txs {
   793  			if (blockHeight - wtx.height) > txmp.config.TTLNumBlocks {
   794  				expiredTxs[mempool.TxKey(wtx.tx)] = wtx
   795  				purgeIdx = i
   796  			} else {
   797  				// since the index is sorted, we know no other txs can be be purged
   798  				break
   799  			}
   800  		}
   801  
   802  		if purgeIdx >= 0 {
   803  			txmp.heightIndex.txs = txmp.heightIndex.txs[purgeIdx+1:]
   804  		}
   805  	}
   806  
   807  	if txmp.config.TTLDuration > 0 {
   808  		purgeIdx := -1
   809  		for i, wtx := range txmp.timestampIndex.txs {
   810  			if now.Sub(wtx.timestamp) > txmp.config.TTLDuration {
   811  				expiredTxs[mempool.TxKey(wtx.tx)] = wtx
   812  				purgeIdx = i
   813  			} else {
   814  				// since the index is sorted, we know no other txs can be be purged
   815  				break
   816  			}
   817  		}
   818  
   819  		if purgeIdx >= 0 {
   820  			txmp.timestampIndex.txs = txmp.timestampIndex.txs[purgeIdx+1:]
   821  		}
   822  	}
   823  
   824  	for _, wtx := range expiredTxs {
   825  		txmp.removeTx(wtx, false)
   826  	}
   827  }
   828  
   829  func (txmp *TxMempool) notifyTxsAvailable() {
   830  	if txmp.Size() == 0 {
   831  		panic("attempt to notify txs available but mempool is empty!")
   832  	}
   833  
   834  	if txmp.txsAvailable != nil && !txmp.notifiedTxsAvailable {
   835  		// channel cap is 1, so this will send once
   836  		txmp.notifiedTxsAvailable = true
   837  
   838  		select {
   839  		case txmp.txsAvailable <- struct{}{}:
   840  		default:
   841  		}
   842  	}
   843  }