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 }