github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/mempool/v1/mempool_test.go (about)

     1  package v1
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"math/rand"
     8  	"os"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/badrootd/celestia-core/abci/example/code"
    20  	"github.com/badrootd/celestia-core/abci/example/kvstore"
    21  	abci "github.com/badrootd/celestia-core/abci/types"
    22  	"github.com/badrootd/celestia-core/config"
    23  	"github.com/badrootd/celestia-core/libs/log"
    24  	"github.com/badrootd/celestia-core/mempool"
    25  	"github.com/badrootd/celestia-core/pkg/consts"
    26  	tmproto "github.com/badrootd/celestia-core/proto/tendermint/types"
    27  	"github.com/badrootd/celestia-core/proxy"
    28  	"github.com/badrootd/celestia-core/types"
    29  )
    30  
    31  // application extends the KV store application by overriding CheckTx to provide
    32  // transaction priority based on the value in the key/value pair.
    33  type application struct {
    34  	*kvstore.Application
    35  }
    36  
    37  type testTx struct {
    38  	tx       types.Tx
    39  	priority int64
    40  }
    41  
    42  func (app *application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
    43  	var (
    44  		priority int64
    45  		sender   string
    46  	)
    47  
    48  	// infer the priority from the raw transaction value (sender=key=value)
    49  	parts := bytes.Split(req.Tx, []byte("="))
    50  	if len(parts) == 3 {
    51  		v, err := strconv.ParseInt(string(parts[2]), 10, 64)
    52  		if err != nil {
    53  			return abci.ResponseCheckTx{
    54  				Priority:  priority,
    55  				Code:      100,
    56  				GasWanted: 1,
    57  			}
    58  		}
    59  
    60  		priority = v
    61  		sender = string(parts[0])
    62  	} else {
    63  		return abci.ResponseCheckTx{
    64  			Priority:  priority,
    65  			Code:      101,
    66  			GasWanted: 1,
    67  		}
    68  	}
    69  
    70  	return abci.ResponseCheckTx{
    71  		Priority:  priority,
    72  		Sender:    sender,
    73  		Code:      code.CodeTypeOK,
    74  		GasWanted: 1,
    75  	}
    76  }
    77  
    78  func setup(t testing.TB, cacheSize int, options ...TxMempoolOption) *TxMempool {
    79  	t.Helper()
    80  
    81  	app := &application{kvstore.NewApplication()}
    82  	cc := proxy.NewLocalClientCreator(app)
    83  
    84  	cfg := config.ResetTestRoot(strings.ReplaceAll(t.Name(), "/", "|"))
    85  	cfg.Mempool.CacheSize = cacheSize
    86  
    87  	appConnMem, err := cc.NewABCIClient()
    88  	require.NoError(t, err)
    89  	require.NoError(t, appConnMem.Start())
    90  
    91  	t.Cleanup(func() {
    92  		os.RemoveAll(cfg.RootDir)
    93  		require.NoError(t, appConnMem.Stop())
    94  	})
    95  
    96  	return NewTxMempool(log.TestingLogger().With("test", t.Name()), cfg.Mempool, appConnMem, 0, options...)
    97  }
    98  
    99  // mustCheckTx invokes txmp.CheckTx for the given transaction and waits until
   100  // its callback has finished executing. It fails t if CheckTx fails.
   101  func mustCheckTx(t *testing.T, txmp *TxMempool, spec string) {
   102  	done := make(chan struct{})
   103  	if err := txmp.CheckTx([]byte(spec), func(*abci.Response) {
   104  		close(done)
   105  	}, mempool.TxInfo{}); err != nil {
   106  		t.Fatalf("CheckTx for %q failed: %v", spec, err)
   107  	}
   108  	<-done
   109  }
   110  
   111  func checkTxs(t *testing.T, txmp *TxMempool, numTxs int, peerID uint16) []testTx {
   112  	txs := make([]testTx, numTxs)
   113  	txInfo := mempool.TxInfo{SenderID: peerID}
   114  
   115  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
   116  
   117  	for i := 0; i < numTxs; i++ {
   118  		prefix := make([]byte, 20)
   119  		_, err := rng.Read(prefix)
   120  		require.NoError(t, err)
   121  
   122  		priority := int64(rng.Intn(9999-1000) + 1000)
   123  
   124  		txs[i] = testTx{
   125  			tx:       []byte(fmt.Sprintf("sender-%03d-%d=%X=%d", i, peerID, prefix, priority)),
   126  			priority: priority,
   127  		}
   128  		require.NoError(t, txmp.CheckTx(txs[i].tx, nil, txInfo))
   129  	}
   130  
   131  	return txs
   132  }
   133  
   134  func TestTxMempool_TxsAvailable(t *testing.T) {
   135  	txmp := setup(t, 0)
   136  	txmp.EnableTxsAvailable()
   137  
   138  	ensureNoTxFire := func() {
   139  		timer := time.NewTimer(500 * time.Millisecond)
   140  		select {
   141  		case <-txmp.TxsAvailable():
   142  			require.Fail(t, "unexpected transactions event")
   143  		case <-timer.C:
   144  		}
   145  	}
   146  
   147  	ensureTxFire := func() {
   148  		timer := time.NewTimer(500 * time.Millisecond)
   149  		select {
   150  		case <-txmp.TxsAvailable():
   151  		case <-timer.C:
   152  			require.Fail(t, "expected transactions event")
   153  		}
   154  	}
   155  
   156  	// ensure no event as we have not executed any transactions yet
   157  	ensureNoTxFire()
   158  
   159  	// Execute CheckTx for some transactions and ensure TxsAvailable only fires
   160  	// once.
   161  	txs := checkTxs(t, txmp, 100, 0)
   162  	ensureTxFire()
   163  	ensureNoTxFire()
   164  
   165  	rawTxs := make([]types.Tx, len(txs))
   166  	for i, tx := range txs {
   167  		rawTxs[i] = tx.tx
   168  	}
   169  
   170  	responses := make([]*abci.ResponseDeliverTx, len(rawTxs[:50]))
   171  	for i := 0; i < len(responses); i++ {
   172  		responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
   173  	}
   174  
   175  	// commit half the transactions and ensure we fire an event
   176  	txmp.Lock()
   177  	require.NoError(t, txmp.Update(1, rawTxs[:50], responses, nil, nil))
   178  	txmp.Unlock()
   179  	ensureTxFire()
   180  	ensureNoTxFire()
   181  
   182  	// Execute CheckTx for more transactions and ensure we do not fire another
   183  	// event as we're still on the same height (1).
   184  	_ = checkTxs(t, txmp, 100, 0)
   185  	ensureNoTxFire()
   186  }
   187  
   188  func TestTxMempool_Size(t *testing.T) {
   189  	txmp := setup(t, 0)
   190  	txs := checkTxs(t, txmp, 100, 0)
   191  	require.Equal(t, len(txs), txmp.Size())
   192  	require.Equal(t, int64(5800), txmp.SizeBytes())
   193  
   194  	rawTxs := make([]types.Tx, len(txs))
   195  	for i, tx := range txs {
   196  		rawTxs[i] = tx.tx
   197  	}
   198  
   199  	responses := make([]*abci.ResponseDeliverTx, len(rawTxs[:50]))
   200  	for i := 0; i < len(responses); i++ {
   201  		responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
   202  	}
   203  
   204  	txmp.Lock()
   205  	require.NoError(t, txmp.Update(1, rawTxs[:50], responses, nil, nil))
   206  	txmp.Unlock()
   207  
   208  	require.Equal(t, len(rawTxs)/2, txmp.Size())
   209  	require.Equal(t, int64(2900), txmp.SizeBytes())
   210  }
   211  
   212  func TestTxMempool_Eviction(t *testing.T) {
   213  	txmp := setup(t, 1000)
   214  	txmp.config.Size = 5
   215  	txmp.config.MaxTxsBytes = 60
   216  	txExists := func(spec string) bool {
   217  		txmp.Lock()
   218  		defer txmp.Unlock()
   219  		key := types.Tx(spec).Key()
   220  		_, ok := txmp.txByKey[key]
   221  		return ok
   222  	}
   223  
   224  	// A transaction bigger than the mempool should be rejected even when there
   225  	// are slots available.
   226  	mustCheckTx(t, txmp, "big=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef=1")
   227  	require.Equal(t, 0, txmp.Size())
   228  
   229  	// Nearly-fill the mempool with a low-priority transaction, to show that it
   230  	// is evicted even when slots are available for a higher-priority tx.
   231  	const bigTx = "big=0123456789abcdef0123456789abcdef0123456789abcdef01234=2"
   232  	mustCheckTx(t, txmp, bigTx)
   233  	require.Equal(t, 1, txmp.Size()) // bigTx is the only element
   234  	require.True(t, txExists(bigTx))
   235  	require.Equal(t, int64(len(bigTx)), txmp.SizeBytes())
   236  
   237  	// The next transaction should evict bigTx, because it is higher priority
   238  	// but does not fit on size.
   239  	mustCheckTx(t, txmp, "key1=0000=25")
   240  	require.True(t, txExists("key1=0000=25"))
   241  	require.False(t, txExists(bigTx))
   242  	require.False(t, txmp.cache.Has([]byte(bigTx)))
   243  	require.Equal(t, int64(len("key1=0000=25")), txmp.SizeBytes())
   244  
   245  	// Now fill up the rest of the slots with other transactions.
   246  	mustCheckTx(t, txmp, "key2=0001=5")
   247  	mustCheckTx(t, txmp, "key3=0002=10")
   248  	mustCheckTx(t, txmp, "key4=0003=3")
   249  	mustCheckTx(t, txmp, "key5=0004=3")
   250  
   251  	// A new transaction with low priority should be discarded.
   252  	mustCheckTx(t, txmp, "key6=0005=1")
   253  	require.False(t, txExists("key6=0005=1"))
   254  
   255  	// A new transaction with higher priority should evict key5, which is the
   256  	// newest of the two transactions with lowest priority.
   257  	mustCheckTx(t, txmp, "key7=0006=7")
   258  	require.True(t, txExists("key7=0006=7"))  // new transaction added
   259  	require.False(t, txExists("key5=0004=3")) // newest low-priority tx evicted
   260  	require.True(t, txExists("key4=0003=3"))  // older low-priority tx retained
   261  
   262  	// Another new transaction evicts the other low-priority element.
   263  	mustCheckTx(t, txmp, "key8=0007=20")
   264  	require.True(t, txExists("key8=0007=20"))
   265  	require.False(t, txExists("key4=0003=3"))
   266  
   267  	// Now the lowest-priority tx is 5, so that should be the next to go.
   268  	mustCheckTx(t, txmp, "key9=0008=9")
   269  	require.True(t, txExists("key9=0008=9"))
   270  	require.False(t, txExists("key2=0001=5"))
   271  
   272  	// Add a transaction that requires eviction of multiple lower-priority
   273  	// entries, in order to fit the size of the element.
   274  	mustCheckTx(t, txmp, "key10=0123456789abcdef=11") // evict 10, 9, 7; keep 25, 20, 11
   275  	require.True(t, txExists("key1=0000=25"))
   276  	require.True(t, txExists("key8=0007=20"))
   277  	require.True(t, txExists("key10=0123456789abcdef=11"))
   278  	require.False(t, txExists("key3=0002=10"))
   279  	require.False(t, txExists("key9=0008=9"))
   280  	require.False(t, txExists("key7=0006=7"))
   281  }
   282  
   283  func TestTxMempool_Flush(t *testing.T) {
   284  	txmp := setup(t, 0)
   285  	txs := checkTxs(t, txmp, 100, 0)
   286  	require.Equal(t, len(txs), txmp.Size())
   287  	require.Equal(t, int64(5800), txmp.SizeBytes())
   288  
   289  	rawTxs := make([]types.Tx, len(txs))
   290  	for i, tx := range txs {
   291  		rawTxs[i] = tx.tx
   292  	}
   293  
   294  	responses := make([]*abci.ResponseDeliverTx, len(rawTxs[:50]))
   295  	for i := 0; i < len(responses); i++ {
   296  		responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
   297  	}
   298  
   299  	txmp.Lock()
   300  	require.NoError(t, txmp.Update(1, rawTxs[:50], responses, nil, nil))
   301  	txmp.Unlock()
   302  
   303  	txmp.Flush()
   304  	require.Zero(t, txmp.Size())
   305  	require.Equal(t, int64(0), txmp.SizeBytes())
   306  }
   307  
   308  func TestTxMempool_ReapMaxBytesMaxGas(t *testing.T) {
   309  	// totalSizeBytes is the expected size of the mempool after adding 100 txs
   310  	// this value is highly dependant upon the size of the txs and the overhead
   311  	// introduced in the mempool. This number will need to be adjusted if
   312  	// changes are made to any of those things.
   313  	totalSizeBytes := int64(5800)
   314  	txmp := setup(t, 0)
   315  	tTxs := checkTxs(t, txmp, 100, 0) // all txs request 1 gas unit
   316  	require.Equal(t, len(tTxs), txmp.Size())
   317  	require.Equal(t, totalSizeBytes, txmp.SizeBytes())
   318  	txMap := make(map[types.TxKey]testTx)
   319  	priorities := make([]int64, len(tTxs))
   320  	for i, tTx := range tTxs {
   321  		txMap[tTx.tx.Key()] = tTx
   322  		priorities[i] = tTx.priority
   323  	}
   324  
   325  	sort.Slice(priorities, func(i, j int) bool {
   326  		// sort by priority, i.e. decreasing order
   327  		return priorities[i] > priorities[j]
   328  	})
   329  
   330  	ensurePrioritized := func(reapedTxs types.Txs) {
   331  		reapedPriorities := make([]int64, len(reapedTxs))
   332  		for i, rTx := range reapedTxs {
   333  			reapedPriorities[i] = txMap[rTx.Key()].priority
   334  		}
   335  
   336  		require.Equal(t, priorities[:len(reapedPriorities)], reapedPriorities)
   337  	}
   338  
   339  	// reap by gas capacity only
   340  	reapedTxs := txmp.ReapMaxBytesMaxGas(-1, 50)
   341  	ensurePrioritized(reapedTxs)
   342  	require.Equal(t, len(tTxs), txmp.Size())
   343  	require.Equal(t, totalSizeBytes, txmp.SizeBytes())
   344  	require.Len(t, reapedTxs, 50)
   345  
   346  	// reap by transaction bytes only
   347  	reapedTxs = txmp.ReapMaxBytesMaxGas(1000, -1)
   348  	ensurePrioritized(reapedTxs)
   349  	require.Equal(t, len(tTxs), txmp.Size())
   350  	require.Equal(t, totalSizeBytes, txmp.SizeBytes())
   351  	require.GreaterOrEqual(t, len(reapedTxs), 16)
   352  
   353  	// Reap by both transaction bytes and gas, where the size yields 31 reaped
   354  	// transactions and the gas limit reaps 25 transactions.
   355  	reapedTxs = txmp.ReapMaxBytesMaxGas(1500, 30)
   356  	ensurePrioritized(reapedTxs)
   357  	require.Equal(t, len(tTxs), txmp.Size())
   358  	require.Equal(t, totalSizeBytes, txmp.SizeBytes())
   359  	require.Len(t, reapedTxs, 25)
   360  }
   361  
   362  func TestTxMempoolTxLargerThanMaxBytes(t *testing.T) {
   363  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
   364  	txmp := setup(t, 0)
   365  	bigPrefix := make([]byte, 100)
   366  	_, err := rng.Read(bigPrefix)
   367  	require.NoError(t, err)
   368  	// large high priority tx
   369  	bigTx := []byte(fmt.Sprintf("sender-1-1=%X=2", bigPrefix))
   370  	smallPrefix := make([]byte, 20)
   371  	_, err = rng.Read(smallPrefix)
   372  	require.NoError(t, err)
   373  	// smaller low priority tx with different sender
   374  	smallTx := []byte(fmt.Sprintf("sender-2-1=%X=1", smallPrefix))
   375  	require.NoError(t, txmp.CheckTx(bigTx, nil, mempool.TxInfo{SenderID: 1}))
   376  	require.NoError(t, txmp.CheckTx(smallTx, nil, mempool.TxInfo{SenderID: 1}))
   377  
   378  	// reap by max bytes less than the large tx
   379  	reapedTxs := txmp.ReapMaxBytesMaxGas(100, -1)
   380  	require.Len(t, reapedTxs, 1)
   381  	require.Equal(t, types.Tx(smallTx), reapedTxs[0])
   382  }
   383  
   384  func TestTxMempool_ReapMaxTxs(t *testing.T) {
   385  	txmp := setup(t, 0)
   386  	tTxs := checkTxs(t, txmp, 100, 0)
   387  	require.Equal(t, len(tTxs), txmp.Size())
   388  	require.Equal(t, int64(5800), txmp.SizeBytes())
   389  
   390  	txMap := make(map[types.TxKey]testTx)
   391  	priorities := make([]int64, len(tTxs))
   392  	for i, tTx := range tTxs {
   393  		txMap[tTx.tx.Key()] = tTx
   394  		priorities[i] = tTx.priority
   395  	}
   396  
   397  	sort.Slice(priorities, func(i, j int) bool {
   398  		// sort by priority, i.e. decreasing order
   399  		return priorities[i] > priorities[j]
   400  	})
   401  
   402  	ensurePrioritized := func(reapedTxs types.Txs) {
   403  		reapedPriorities := make([]int64, len(reapedTxs))
   404  		for i, rTx := range reapedTxs {
   405  			reapedPriorities[i] = txMap[rTx.Key()].priority
   406  		}
   407  
   408  		require.Equal(t, priorities[:len(reapedPriorities)], reapedPriorities)
   409  	}
   410  
   411  	// reap all transactions
   412  	reapedTxs := txmp.ReapMaxTxs(-1)
   413  	ensurePrioritized(reapedTxs)
   414  	require.Equal(t, len(tTxs), txmp.Size())
   415  	require.Equal(t, int64(5800), txmp.SizeBytes())
   416  	require.Len(t, reapedTxs, len(tTxs))
   417  
   418  	// reap a single transaction
   419  	reapedTxs = txmp.ReapMaxTxs(1)
   420  	ensurePrioritized(reapedTxs)
   421  	require.Equal(t, len(tTxs), txmp.Size())
   422  	require.Equal(t, int64(5800), txmp.SizeBytes())
   423  	require.Len(t, reapedTxs, 1)
   424  
   425  	// reap half of the transactions
   426  	reapedTxs = txmp.ReapMaxTxs(len(tTxs) / 2)
   427  	ensurePrioritized(reapedTxs)
   428  	require.Equal(t, len(tTxs), txmp.Size())
   429  	require.Equal(t, int64(5800), txmp.SizeBytes())
   430  	require.Len(t, reapedTxs, len(tTxs)/2)
   431  }
   432  
   433  func TestTxMempool_CheckTxExceedsMaxSize(t *testing.T) {
   434  	txmp := setup(t, 0)
   435  
   436  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
   437  	tx := make([]byte, txmp.config.MaxTxBytes+1)
   438  	_, err := rng.Read(tx)
   439  	require.NoError(t, err)
   440  
   441  	require.Error(t, txmp.CheckTx(tx, nil, mempool.TxInfo{SenderID: 0}))
   442  
   443  	tx = make([]byte, txmp.config.MaxTxBytes-1)
   444  	_, err = rng.Read(tx)
   445  	require.NoError(t, err)
   446  
   447  	require.NoError(t, txmp.CheckTx(tx, nil, mempool.TxInfo{SenderID: 0}))
   448  }
   449  
   450  func TestTxMempool_CheckTxSamePeer(t *testing.T) {
   451  	txmp := setup(t, 100)
   452  	peerID := uint16(1)
   453  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
   454  
   455  	prefix := make([]byte, 20)
   456  	_, err := rng.Read(prefix)
   457  	require.NoError(t, err)
   458  
   459  	tx := []byte(fmt.Sprintf("sender-0=%X=%d", prefix, 50))
   460  
   461  	require.NoError(t, txmp.CheckTx(tx, nil, mempool.TxInfo{SenderID: peerID}))
   462  	require.Error(t, txmp.CheckTx(tx, nil, mempool.TxInfo{SenderID: peerID}))
   463  }
   464  
   465  func TestTxMempool_CheckTxSameSender(t *testing.T) {
   466  	txmp := setup(t, 100)
   467  	peerID := uint16(1)
   468  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
   469  
   470  	prefix1 := make([]byte, 20)
   471  	_, err := rng.Read(prefix1)
   472  	require.NoError(t, err)
   473  
   474  	prefix2 := make([]byte, 20)
   475  	_, err = rng.Read(prefix2)
   476  	require.NoError(t, err)
   477  
   478  	tx1 := []byte(fmt.Sprintf("sender-0=%X=%d", prefix1, 50))
   479  	tx2 := []byte(fmt.Sprintf("sender-0=%X=%d", prefix2, 50))
   480  
   481  	require.NoError(t, txmp.CheckTx(tx1, nil, mempool.TxInfo{SenderID: peerID}))
   482  	require.Equal(t, 1, txmp.Size())
   483  	require.NoError(t, txmp.CheckTx(tx2, nil, mempool.TxInfo{SenderID: peerID}))
   484  	require.Equal(t, 1, txmp.Size())
   485  }
   486  
   487  func TestTxMempool_ConcurrentTxs(t *testing.T) {
   488  	txmp := setup(t, 100)
   489  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
   490  	checkTxDone := make(chan struct{})
   491  
   492  	var wg sync.WaitGroup
   493  
   494  	wg.Add(1)
   495  	go func() {
   496  		for i := 0; i < 20; i++ {
   497  			_ = checkTxs(t, txmp, 100, 0)
   498  			dur := rng.Intn(1000-500) + 500
   499  			time.Sleep(time.Duration(dur) * time.Millisecond)
   500  		}
   501  
   502  		wg.Done()
   503  		close(checkTxDone)
   504  	}()
   505  
   506  	wg.Add(1)
   507  	go func() {
   508  		ticker := time.NewTicker(time.Second)
   509  		defer ticker.Stop()
   510  		defer wg.Done()
   511  
   512  		var height int64 = 1
   513  
   514  		for range ticker.C {
   515  			reapedTxs := txmp.ReapMaxTxs(200)
   516  			if len(reapedTxs) > 0 {
   517  				responses := make([]*abci.ResponseDeliverTx, len(reapedTxs))
   518  				for i := 0; i < len(responses); i++ {
   519  					var code uint32
   520  
   521  					if i%10 == 0 {
   522  						code = 100
   523  					} else {
   524  						code = abci.CodeTypeOK
   525  					}
   526  
   527  					responses[i] = &abci.ResponseDeliverTx{Code: code}
   528  				}
   529  
   530  				txmp.Lock()
   531  				require.NoError(t, txmp.Update(height, reapedTxs, responses, nil, nil))
   532  				txmp.Unlock()
   533  
   534  				height++
   535  			} else {
   536  				// only return once we know we finished the CheckTx loop
   537  				select {
   538  				case <-checkTxDone:
   539  					return
   540  				default:
   541  				}
   542  			}
   543  		}
   544  	}()
   545  
   546  	wg.Wait()
   547  	require.Zero(t, txmp.Size())
   548  	require.Zero(t, txmp.SizeBytes())
   549  }
   550  
   551  func TestTxMempool_ExpiredTxs_Timestamp(t *testing.T) {
   552  	txmp := setup(t, 5000)
   553  	txmp.config.TTLDuration = 5 * time.Millisecond
   554  
   555  	added1 := checkTxs(t, txmp, 10, 0)
   556  	require.Equal(t, len(added1), txmp.Size())
   557  
   558  	// Wait a while, then add some more transactions that should not be expired
   559  	// when the first batch TTLs out.
   560  	//
   561  	// ms: 0   1   2   3   4   5   6
   562  	//     ^           ^       ^   ^
   563  	//     |           |       |   +-- Update (triggers pruning)
   564  	//     |           |       +------ first batch expires
   565  	//     |           +-------------- second batch added
   566  	//     +-------------------------- first batch added
   567  	//
   568  	// The exact intervals are not important except that the delta should be
   569  	// large relative to the cost of CheckTx (ms vs. ns is fine here).
   570  	time.Sleep(3 * time.Millisecond)
   571  	added2 := checkTxs(t, txmp, 10, 1)
   572  
   573  	// Wait a while longer, so that the first batch will expire.
   574  	time.Sleep(3 * time.Millisecond)
   575  
   576  	// Trigger an update so that pruning will occur.
   577  	txmp.Lock()
   578  	defer txmp.Unlock()
   579  	require.NoError(t, txmp.Update(txmp.height+1, nil, nil, nil, nil))
   580  
   581  	// All the transactions in the original set should have been purged.
   582  	for _, tx := range added1 {
   583  		if _, ok := txmp.txByKey[tx.tx.Key()]; ok {
   584  			t.Errorf("Transaction %X should have been purged for TTL", tx.tx.Key())
   585  		}
   586  		if txmp.cache.Has(tx.tx) {
   587  			t.Errorf("Transaction %X should have been removed from the cache", tx.tx.Key())
   588  		}
   589  	}
   590  
   591  	// All the transactions added later should still be around.
   592  	for _, tx := range added2 {
   593  		if _, ok := txmp.txByKey[tx.tx.Key()]; !ok {
   594  			t.Errorf("Transaction %X should still be in the mempool, but is not", tx.tx.Key())
   595  		}
   596  	}
   597  }
   598  
   599  func TestTxMempool_ExpiredTxs_NumBlocks(t *testing.T) {
   600  	txmp := setup(t, 500)
   601  	txmp.height = 100
   602  	txmp.config.TTLNumBlocks = 10
   603  
   604  	tTxs := checkTxs(t, txmp, 100, 0)
   605  	require.Equal(t, len(tTxs), txmp.Size())
   606  
   607  	// reap 5 txs at the next height -- no txs should expire
   608  	reapedTxs := txmp.ReapMaxTxs(5)
   609  	responses := make([]*abci.ResponseDeliverTx, len(reapedTxs))
   610  	for i := 0; i < len(responses); i++ {
   611  		responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
   612  	}
   613  
   614  	txmp.Lock()
   615  	require.NoError(t, txmp.Update(txmp.height+1, reapedTxs, responses, nil, nil))
   616  	txmp.Unlock()
   617  
   618  	require.Equal(t, 95, txmp.Size())
   619  
   620  	// check more txs at height 101
   621  	_ = checkTxs(t, txmp, 50, 1)
   622  	require.Equal(t, 145, txmp.Size())
   623  
   624  	// Reap 5 txs at a height that would expire all the transactions from before
   625  	// the previous Update (height 100).
   626  	//
   627  	// NOTE: When we reap txs below, we do not know if we're picking txs from the
   628  	// initial CheckTx calls or from the second round of CheckTx calls. Thus, we
   629  	// cannot guarantee that all 95 txs are remaining that should be expired and
   630  	// removed. However, we do know that that at most 95 txs can be expired and
   631  	// removed.
   632  	reapedTxs = txmp.ReapMaxTxs(5)
   633  	responses = make([]*abci.ResponseDeliverTx, len(reapedTxs))
   634  	for i := 0; i < len(responses); i++ {
   635  		responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
   636  	}
   637  
   638  	txmp.Lock()
   639  	require.NoError(t, txmp.Update(txmp.height+10, reapedTxs, responses, nil, nil))
   640  	txmp.Unlock()
   641  
   642  	require.GreaterOrEqual(t, txmp.Size(), 45)
   643  }
   644  
   645  func TestTxMempool_CheckTxPostCheckError(t *testing.T) {
   646  	cases := []struct {
   647  		name string
   648  		err  error
   649  	}{
   650  		{
   651  			name: "error",
   652  			err:  errors.New("test error"),
   653  		},
   654  		{
   655  			name: "no error",
   656  			err:  nil,
   657  		},
   658  	}
   659  	for _, tc := range cases {
   660  		testCase := tc
   661  		t.Run(testCase.name, func(t *testing.T) {
   662  			postCheckFn := func(_ types.Tx, _ *abci.ResponseCheckTx) error {
   663  				return testCase.err
   664  			}
   665  			txmp := setup(t, 0, WithPostCheck(postCheckFn))
   666  			rng := rand.New(rand.NewSource(time.Now().UnixNano()))
   667  			tx := make([]byte, txmp.config.MaxTxBytes-1)
   668  			_, err := rng.Read(tx)
   669  			require.NoError(t, err)
   670  
   671  			callback := func(res *abci.Response) {
   672  				checkTxRes, ok := res.Value.(*abci.Response_CheckTx)
   673  				require.True(t, ok)
   674  				expectedErrString := ""
   675  				if testCase.err != nil {
   676  					expectedErrString = testCase.err.Error()
   677  				}
   678  				require.Equal(t, expectedErrString, checkTxRes.CheckTx.MempoolError)
   679  			}
   680  			require.NoError(t, txmp.CheckTx(tx, callback, mempool.TxInfo{SenderID: 0}))
   681  		})
   682  	}
   683  }
   684  
   685  func TestRemoveBlobTx(t *testing.T) {
   686  	txmp := setup(t, 500)
   687  	namespaceOne := bytes.Repeat([]byte{1}, consts.NamespaceIDSize)
   688  
   689  	originalTx := []byte{1, 2, 3, 4}
   690  	indexWrapper, err := types.MarshalIndexWrapper(originalTx, 100)
   691  	require.NoError(t, err)
   692  
   693  	// create the blobTx
   694  	b := tmproto.Blob{
   695  		NamespaceId:      namespaceOne,
   696  		Data:             []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 9},
   697  		ShareVersion:     0,
   698  		NamespaceVersion: 0,
   699  	}
   700  	bTx, err := types.MarshalBlobTx(originalTx, &b)
   701  	require.NoError(t, err)
   702  
   703  	err = txmp.CheckTx(bTx, nil, mempool.TxInfo{})
   704  	require.NoError(t, err)
   705  
   706  	err = txmp.Update(1, []types.Tx{indexWrapper}, abciResponses(1, abci.CodeTypeOK), nil, nil)
   707  	require.NoError(t, err)
   708  	assert.EqualValues(t, 0, txmp.Size())
   709  	assert.EqualValues(t, 0, txmp.SizeBytes())
   710  }
   711  
   712  func abciResponses(n int, code uint32) []*abci.ResponseDeliverTx {
   713  	responses := make([]*abci.ResponseDeliverTx, 0, n)
   714  	for i := 0; i < n; i++ {
   715  		responses = append(responses, &abci.ResponseDeliverTx{Code: code})
   716  	}
   717  	return responses
   718  }