github.com/aakash4dev/cometbft@v0.38.2/mempool/clist_mempool_test.go (about) 1 package mempool 2 3 import ( 4 "context" 5 "encoding/binary" 6 mrand "math/rand" 7 "os" 8 "testing" 9 "time" 10 11 "fmt" 12 13 "github.com/cosmos/gogoproto/proto" 14 gogotypes "github.com/cosmos/gogoproto/types" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/mock" 17 "github.com/stretchr/testify/require" 18 19 abciclient "github.com/aakash4dev/cometbft/abci/client" 20 abciclimocks "github.com/aakash4dev/cometbft/abci/client/mocks" 21 "github.com/aakash4dev/cometbft/abci/example/kvstore" 22 abciserver "github.com/aakash4dev/cometbft/abci/server" 23 abci "github.com/aakash4dev/cometbft/abci/types" 24 "github.com/aakash4dev/cometbft/config" 25 "github.com/aakash4dev/cometbft/internal/test" 26 "github.com/aakash4dev/cometbft/libs/log" 27 cmtrand "github.com/aakash4dev/cometbft/libs/rand" 28 "github.com/aakash4dev/cometbft/libs/service" 29 "github.com/aakash4dev/cometbft/proxy" 30 "github.com/aakash4dev/cometbft/types" 31 ) 32 33 // A cleanupFunc cleans up any config / test files created for a particular 34 // test. 35 type cleanupFunc func() 36 37 func newMempoolWithAppMock(client abciclient.Client) (*CListMempool, cleanupFunc, error) { 38 conf := test.ResetTestRoot("mempool_test") 39 40 mp, cu := newMempoolWithAppAndConfigMock(conf, client) 41 return mp, cu, nil 42 } 43 44 func newMempoolWithAppAndConfigMock( 45 cfg *config.Config, 46 client abciclient.Client, 47 ) (*CListMempool, cleanupFunc) { 48 appConnMem := client 49 appConnMem.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "mempool")) 50 err := appConnMem.Start() 51 if err != nil { 52 panic(err) 53 } 54 55 mp := NewCListMempool(cfg.Mempool, appConnMem, 0) 56 mp.SetLogger(log.TestingLogger()) 57 58 return mp, func() { os.RemoveAll(cfg.RootDir) } 59 } 60 61 func newMempoolWithApp(cc proxy.ClientCreator) (*CListMempool, cleanupFunc) { 62 conf := test.ResetTestRoot("mempool_test") 63 64 mp, cu := newMempoolWithAppAndConfig(cc, conf) 65 return mp, cu 66 } 67 68 func newMempoolWithAppAndConfig(cc proxy.ClientCreator, cfg *config.Config) (*CListMempool, cleanupFunc) { 69 appConnMem, _ := cc.NewABCIClient() 70 appConnMem.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "mempool")) 71 err := appConnMem.Start() 72 if err != nil { 73 panic(err) 74 } 75 76 mp := NewCListMempool(cfg.Mempool, appConnMem, 0) 77 mp.SetLogger(log.TestingLogger()) 78 79 return mp, func() { os.RemoveAll(cfg.RootDir) } 80 } 81 82 func ensureNoFire(t *testing.T, ch <-chan struct{}, timeoutMS int) { 83 timer := time.NewTimer(time.Duration(timeoutMS) * time.Millisecond) 84 select { 85 case <-ch: 86 t.Fatal("Expected not to fire") 87 case <-timer.C: 88 } 89 } 90 91 func ensureFire(t *testing.T, ch <-chan struct{}, timeoutMS int) { 92 timer := time.NewTimer(time.Duration(timeoutMS) * time.Millisecond) 93 select { 94 case <-ch: 95 case <-timer.C: 96 t.Fatal("Expected to fire") 97 } 98 } 99 100 // Call CheckTx on a given mempool on each transaction in the list. 101 func callCheckTx(t *testing.T, mp Mempool, txs types.Txs) { 102 for i, tx := range txs { 103 if _, err := mp.CheckTx(tx); err != nil { 104 // Skip invalid txs. 105 // TestMempoolFilters will fail otherwise. It asserts a number of txs 106 // returned. 107 if IsPreCheckError(err) { 108 continue 109 } 110 t.Fatalf("CheckTx failed: %v while checking #%d tx", err, i) 111 } 112 } 113 } 114 115 // Generate a list of random transactions 116 func NewRandomTxs(numTxs int, txLen int) types.Txs { 117 txs := make(types.Txs, numTxs) 118 for i := 0; i < numTxs; i++ { 119 txBytes := kvstore.NewRandomTx(txLen) 120 txs[i] = txBytes 121 } 122 return txs 123 } 124 125 // Generate a list of random transactions of a given size and call CheckTx on 126 // each of them. 127 func checkTxs(t *testing.T, mp Mempool, count int) types.Txs { 128 txs := NewRandomTxs(count, 20) 129 callCheckTx(t, mp, txs) 130 return txs 131 } 132 133 func TestReapMaxBytesMaxGas(t *testing.T) { 134 app := kvstore.NewInMemoryApplication() 135 cc := proxy.NewLocalClientCreator(app) 136 mp, cleanup := newMempoolWithApp(cc) 137 defer cleanup() 138 139 // Ensure gas calculation behaves as expected 140 checkTxs(t, mp, 1) 141 tx0 := mp.TxsFront().Value.(*mempoolTx) 142 require.Equal(t, tx0.gasWanted, int64(1), "transactions gas was set incorrectly") 143 // ensure each tx is 20 bytes long 144 require.Equal(t, len(tx0.tx), 20, "Tx is longer than 20 bytes") 145 mp.Flush() 146 147 // each table driven test creates numTxsToCreate txs with checkTx, and at the end clears all remaining txs. 148 // each tx has 20 bytes 149 tests := []struct { 150 numTxsToCreate int 151 maxBytes int64 152 maxGas int64 153 expectedNumTxs int 154 }{ 155 {20, -1, -1, 20}, 156 {20, -1, 0, 0}, 157 {20, -1, 10, 10}, 158 {20, -1, 30, 20}, 159 {20, 0, -1, 0}, 160 {20, 0, 10, 0}, 161 {20, 10, 10, 0}, 162 {20, 24, 10, 1}, 163 {20, 240, 5, 5}, 164 {20, 240, -1, 10}, 165 {20, 240, 10, 10}, 166 {20, 240, 15, 10}, 167 {20, 20000, -1, 20}, 168 {20, 20000, 5, 5}, 169 {20, 20000, 30, 20}, 170 } 171 for tcIndex, tt := range tests { 172 checkTxs(t, mp, tt.numTxsToCreate) 173 got := mp.ReapMaxBytesMaxGas(tt.maxBytes, tt.maxGas) 174 assert.Equal(t, tt.expectedNumTxs, len(got), "Got %d txs, expected %d, tc #%d", 175 len(got), tt.expectedNumTxs, tcIndex) 176 mp.Flush() 177 } 178 } 179 180 func TestMempoolFilters(t *testing.T) { 181 app := kvstore.NewInMemoryApplication() 182 cc := proxy.NewLocalClientCreator(app) 183 mp, cleanup := newMempoolWithApp(cc) 184 defer cleanup() 185 emptyTxArr := []types.Tx{[]byte{}} 186 187 nopPreFilter := func(tx types.Tx) error { return nil } 188 nopPostFilter := func(tx types.Tx, res *abci.ResponseCheckTx) error { return nil } 189 190 // each table driven test creates numTxsToCreate txs with checkTx, and at the end clears all remaining txs. 191 // each tx has 20 bytes 192 tests := []struct { 193 numTxsToCreate int 194 preFilter PreCheckFunc 195 postFilter PostCheckFunc 196 expectedNumTxs int 197 }{ 198 {10, nopPreFilter, nopPostFilter, 10}, 199 {10, PreCheckMaxBytes(10), nopPostFilter, 0}, 200 {10, PreCheckMaxBytes(22), nopPostFilter, 10}, 201 {10, nopPreFilter, PostCheckMaxGas(-1), 10}, 202 {10, nopPreFilter, PostCheckMaxGas(0), 0}, 203 {10, nopPreFilter, PostCheckMaxGas(1), 10}, 204 {10, nopPreFilter, PostCheckMaxGas(3000), 10}, 205 {10, PreCheckMaxBytes(10), PostCheckMaxGas(20), 0}, 206 {10, PreCheckMaxBytes(30), PostCheckMaxGas(20), 10}, 207 {10, PreCheckMaxBytes(22), PostCheckMaxGas(1), 10}, 208 {10, PreCheckMaxBytes(22), PostCheckMaxGas(0), 0}, 209 } 210 for tcIndex, tt := range tests { 211 err := mp.Update(1, emptyTxArr, abciResponses(len(emptyTxArr), abci.CodeTypeOK), tt.preFilter, tt.postFilter) 212 require.NoError(t, err) 213 checkTxs(t, mp, tt.numTxsToCreate) 214 require.Equal(t, tt.expectedNumTxs, mp.Size(), "mempool had the incorrect size, on test case %d", tcIndex) 215 mp.Flush() 216 } 217 } 218 219 func TestMempoolUpdate(t *testing.T) { 220 app := kvstore.NewInMemoryApplication() 221 cc := proxy.NewLocalClientCreator(app) 222 mp, cleanup := newMempoolWithApp(cc) 223 defer cleanup() 224 225 // 1. Adds valid txs to the cache 226 { 227 tx1 := kvstore.NewTxFromID(1) 228 err := mp.Update(1, []types.Tx{tx1}, abciResponses(1, abci.CodeTypeOK), nil, nil) 229 require.NoError(t, err) 230 _, err = mp.CheckTx(tx1) 231 if assert.Error(t, err) { 232 assert.Equal(t, ErrTxInCache, err) 233 } 234 } 235 236 // 2. Removes valid txs from the mempool 237 { 238 tx2 := kvstore.NewTxFromID(2) 239 _, err := mp.CheckTx(tx2) 240 require.NoError(t, err) 241 err = mp.Update(1, []types.Tx{tx2}, abciResponses(1, abci.CodeTypeOK), nil, nil) 242 require.NoError(t, err) 243 assert.Zero(t, mp.Size()) 244 } 245 246 // 3. Removes invalid transactions from the cache and the mempool (if present) 247 { 248 tx3 := kvstore.NewTxFromID(3) 249 _, err := mp.CheckTx(tx3) 250 require.NoError(t, err) 251 err = mp.Update(1, []types.Tx{tx3}, abciResponses(1, 1), nil, nil) 252 require.NoError(t, err) 253 assert.Zero(t, mp.Size()) 254 255 _, err = mp.CheckTx(tx3) 256 require.NoError(t, err) 257 } 258 } 259 260 func TestMempoolUpdateDoesNotPanicWhenApplicationMissedTx(t *testing.T) { 261 var callback abciclient.Callback 262 mockClient := new(abciclimocks.Client) 263 mockClient.On("Start").Return(nil) 264 mockClient.On("SetLogger", mock.Anything) 265 266 mockClient.On("Error").Return(nil).Times(4) 267 mockClient.On("SetResponseCallback", mock.MatchedBy(func(cb abciclient.Callback) bool { callback = cb; return true })) 268 269 mp, cleanup, err := newMempoolWithAppMock(mockClient) 270 require.NoError(t, err) 271 defer cleanup() 272 273 // Add 4 transactions to the mempool by calling the mempool's `CheckTx` on each of them. 274 txs := []types.Tx{[]byte{0x01}, []byte{0x02}, []byte{0x03}, []byte{0x04}} 275 for _, tx := range txs { 276 reqRes := abciclient.NewReqRes(abci.ToRequestCheckTx(&abci.RequestCheckTx{Tx: tx})) 277 reqRes.Response = abci.ToResponseCheckTx(&abci.ResponseCheckTx{Code: abci.CodeTypeOK}) 278 279 mockClient.On("CheckTxAsync", mock.Anything, mock.Anything).Return(reqRes, nil) 280 _, err := mp.CheckTx(tx) 281 require.NoError(t, err) 282 283 // ensure that the callback that the mempool sets on the ReqRes is run. 284 reqRes.InvokeCallback() 285 } 286 287 // Calling update to remove the first transaction from the mempool. 288 // This call also triggers the mempool to recheck its remaining transactions. 289 err = mp.Update(0, []types.Tx{txs[0]}, abciResponses(1, abci.CodeTypeOK), nil, nil) 290 require.Nil(t, err) 291 292 // The mempool has now sent its requests off to the client to be rechecked 293 // and is waiting for the corresponding callbacks to be called. 294 // We now call the mempool-supplied callback on the first and third transaction. 295 // This simulates the client dropping the second request. 296 // Previous versions of this code panicked when the ABCI application missed 297 // a recheck-tx request. 298 resp := &abci.ResponseCheckTx{Code: abci.CodeTypeOK} 299 req := &abci.RequestCheckTx{Tx: txs[1]} 300 callback(abci.ToRequestCheckTx(req), abci.ToResponseCheckTx(resp)) 301 302 req = &abci.RequestCheckTx{Tx: txs[3]} 303 callback(abci.ToRequestCheckTx(req), abci.ToResponseCheckTx(resp)) 304 mockClient.AssertExpectations(t) 305 } 306 307 func TestMempool_KeepInvalidTxsInCache(t *testing.T) { 308 app := kvstore.NewInMemoryApplication() 309 cc := proxy.NewLocalClientCreator(app) 310 wcfg := config.DefaultConfig() 311 wcfg.Mempool.KeepInvalidTxsInCache = true 312 mp, cleanup := newMempoolWithAppAndConfig(cc, wcfg) 313 defer cleanup() 314 315 // 1. An invalid transaction must remain in the cache after Update 316 { 317 a := make([]byte, 8) 318 binary.BigEndian.PutUint64(a, 0) 319 320 b := make([]byte, 8) 321 binary.BigEndian.PutUint64(b, 1) 322 323 _, err := mp.CheckTx(b) 324 require.NoError(t, err) 325 326 // simulate new block 327 _, err = app.FinalizeBlock(context.Background(), &abci.RequestFinalizeBlock{ 328 Txs: [][]byte{a, b}, 329 }) 330 require.NoError(t, err) 331 err = mp.Update(1, []types.Tx{a, b}, 332 []*abci.ExecTxResult{{Code: abci.CodeTypeOK}, {Code: 2}}, nil, nil) 333 require.NoError(t, err) 334 335 // a must be added to the cache 336 _, err = mp.CheckTx(a) 337 if assert.Error(t, err) { 338 assert.Equal(t, ErrTxInCache, err) 339 } 340 341 // b must remain in the cache 342 _, err = mp.CheckTx(b) 343 if assert.Error(t, err) { 344 assert.Equal(t, ErrTxInCache, err) 345 } 346 } 347 348 // 2. An invalid transaction must remain in the cache 349 { 350 a := make([]byte, 8) 351 binary.BigEndian.PutUint64(a, 0) 352 353 // remove a from the cache to test (2) 354 mp.cache.Remove(a) 355 356 _, err := mp.CheckTx(a) 357 require.NoError(t, err) 358 } 359 } 360 361 func TestTxsAvailable(t *testing.T) { 362 app := kvstore.NewInMemoryApplication() 363 cc := proxy.NewLocalClientCreator(app) 364 mp, cleanup := newMempoolWithApp(cc) 365 defer cleanup() 366 mp.EnableTxsAvailable() 367 368 timeoutMS := 500 369 370 // with no txs, it shouldnt fire 371 ensureNoFire(t, mp.TxsAvailable(), timeoutMS) 372 373 // send a bunch of txs, it should only fire once 374 txs := checkTxs(t, mp, 100) 375 ensureFire(t, mp.TxsAvailable(), timeoutMS) 376 ensureNoFire(t, mp.TxsAvailable(), timeoutMS) 377 378 // call update with half the txs. 379 // it should fire once now for the new height 380 // since there are still txs left 381 committedTxs, remainingTxs := txs[:50], txs[50:] 382 if err := mp.Update(1, committedTxs, abciResponses(len(committedTxs), abci.CodeTypeOK), nil, nil); err != nil { 383 t.Error(err) 384 } 385 ensureFire(t, mp.TxsAvailable(), timeoutMS) 386 ensureNoFire(t, mp.TxsAvailable(), timeoutMS) 387 388 // send a bunch more txs. we already fired for this height so it shouldnt fire again 389 moreTxs := checkTxs(t, mp, 50) 390 ensureNoFire(t, mp.TxsAvailable(), timeoutMS) 391 392 // now call update with all the txs. it should not fire as there are no txs left 393 committedTxs = append(remainingTxs, moreTxs...) 394 if err := mp.Update(2, committedTxs, abciResponses(len(committedTxs), abci.CodeTypeOK), nil, nil); err != nil { 395 t.Error(err) 396 } 397 ensureNoFire(t, mp.TxsAvailable(), timeoutMS) 398 399 // send a bunch more txs, it should only fire once 400 checkTxs(t, mp, 100) 401 ensureFire(t, mp.TxsAvailable(), timeoutMS) 402 ensureNoFire(t, mp.TxsAvailable(), timeoutMS) 403 } 404 405 func TestSerialReap(t *testing.T) { 406 app := kvstore.NewInMemoryApplication() 407 cc := proxy.NewLocalClientCreator(app) 408 409 mp, cleanup := newMempoolWithApp(cc) 410 defer cleanup() 411 412 appConnCon, _ := cc.NewABCIClient() 413 appConnCon.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "consensus")) 414 err := appConnCon.Start() 415 require.Nil(t, err) 416 417 cacheMap := make(map[string]struct{}) 418 deliverTxsRange := func(start, end int) { 419 // Deliver some txs. 420 for i := start; i < end; i++ { 421 txBytes := kvstore.NewTx(fmt.Sprintf("%d", i), "true") 422 _, err := mp.CheckTx(txBytes) 423 _, cached := cacheMap[string(txBytes)] 424 if cached { 425 require.NotNil(t, err, "expected error for cached tx") 426 } else { 427 require.Nil(t, err, "expected no err for uncached tx") 428 } 429 cacheMap[string(txBytes)] = struct{}{} 430 431 // Duplicates are cached and should return error 432 _, err = mp.CheckTx(txBytes) 433 require.NotNil(t, err, "Expected error after CheckTx on duplicated tx") 434 } 435 } 436 437 reapCheck := func(exp int) { 438 txs := mp.ReapMaxBytesMaxGas(-1, -1) 439 require.Equal(t, len(txs), exp, fmt.Sprintf("Expected to reap %v txs but got %v", exp, len(txs))) 440 } 441 442 updateRange := func(start, end int) { 443 txs := make(types.Txs, end-start) 444 for i := start; i < end; i++ { 445 txs[i-start] = kvstore.NewTx(fmt.Sprintf("%d", i), "true") 446 } 447 if err := mp.Update(0, txs, abciResponses(len(txs), abci.CodeTypeOK), nil, nil); err != nil { 448 t.Error(err) 449 } 450 } 451 452 commitRange := func(start, end int) { 453 // Deliver some txs in a block 454 txs := make([][]byte, end-start) 455 for i := start; i < end; i++ { 456 txs[i-start] = kvstore.NewTx(fmt.Sprintf("%d", i), "true") 457 } 458 459 res, err := appConnCon.FinalizeBlock(context.Background(), &abci.RequestFinalizeBlock{Txs: txs}) 460 if err != nil { 461 t.Errorf("client error committing tx: %v", err) 462 } 463 for _, txResult := range res.TxResults { 464 if txResult.IsErr() { 465 t.Errorf("error committing tx. Code:%v result:%X log:%v", 466 txResult.Code, txResult.Data, txResult.Log) 467 } 468 } 469 if len(res.AppHash) != 8 { 470 t.Errorf("error committing. Hash:%X", res.AppHash) 471 } 472 473 _, err = appConnCon.Commit(context.Background(), &abci.RequestCommit{}) 474 if err != nil { 475 t.Errorf("client error committing: %v", err) 476 } 477 } 478 479 //---------------------------------------- 480 481 // Deliver some txs. 482 deliverTxsRange(0, 100) 483 484 // Reap the txs. 485 reapCheck(100) 486 487 // Reap again. We should get the same amount 488 reapCheck(100) 489 490 // Deliver 0 to 999, we should reap 900 new txs 491 // because 100 were already counted. 492 deliverTxsRange(0, 1000) 493 494 // Reap the txs. 495 reapCheck(1000) 496 497 // Reap again. We should get the same amount 498 reapCheck(1000) 499 500 // Commit from the conensus AppConn 501 commitRange(0, 500) 502 updateRange(0, 500) 503 504 // We should have 500 left. 505 reapCheck(500) 506 507 // Deliver 100 invalid txs and 100 valid txs 508 deliverTxsRange(900, 1100) 509 510 // We should have 600 now. 511 reapCheck(600) 512 } 513 514 func TestMempool_CheckTxChecksTxSize(t *testing.T) { 515 app := kvstore.NewInMemoryApplication() 516 cc := proxy.NewLocalClientCreator(app) 517 518 mempl, cleanup := newMempoolWithApp(cc) 519 defer cleanup() 520 521 maxTxSize := mempl.config.MaxTxBytes 522 523 testCases := []struct { 524 len int 525 err bool 526 }{ 527 // check small txs. no error 528 0: {10, false}, 529 1: {1000, false}, 530 2: {1000000, false}, 531 532 // check around maxTxSize 533 3: {maxTxSize - 1, false}, 534 4: {maxTxSize, false}, 535 5: {maxTxSize + 1, true}, 536 } 537 538 for i, testCase := range testCases { 539 caseString := fmt.Sprintf("case %d, len %d", i, testCase.len) 540 541 tx := cmtrand.Bytes(testCase.len) 542 543 _, err := mempl.CheckTx(tx) 544 bv := gogotypes.BytesValue{Value: tx} 545 bz, err2 := bv.Marshal() 546 require.NoError(t, err2) 547 require.Equal(t, len(bz), proto.Size(&bv), caseString) 548 549 if !testCase.err { 550 require.NoError(t, err, caseString) 551 } else { 552 require.Equal(t, err, ErrTxTooLarge{ 553 Max: maxTxSize, 554 Actual: testCase.len, 555 }, caseString) 556 } 557 } 558 } 559 560 func TestMempoolTxsBytes(t *testing.T) { 561 app := kvstore.NewInMemoryApplication() 562 cc := proxy.NewLocalClientCreator(app) 563 564 cfg := test.ResetTestRoot("mempool_test") 565 566 cfg.Mempool.MaxTxsBytes = 100 567 mp, cleanup := newMempoolWithAppAndConfig(cc, cfg) 568 defer cleanup() 569 570 // 1. zero by default 571 assert.EqualValues(t, 0, mp.SizeBytes()) 572 573 // 2. len(tx) after CheckTx 574 tx1 := kvstore.NewRandomTx(10) 575 _, err := mp.CheckTx(tx1) 576 require.NoError(t, err) 577 assert.EqualValues(t, 10, mp.SizeBytes()) 578 579 // 3. zero again after tx is removed by Update 580 err = mp.Update(1, []types.Tx{tx1}, abciResponses(1, abci.CodeTypeOK), nil, nil) 581 require.NoError(t, err) 582 assert.EqualValues(t, 0, mp.SizeBytes()) 583 584 // 4. zero after Flush 585 tx2 := kvstore.NewRandomTx(20) 586 _, err = mp.CheckTx(tx2) 587 require.NoError(t, err) 588 assert.EqualValues(t, 20, mp.SizeBytes()) 589 590 mp.Flush() 591 assert.EqualValues(t, 0, mp.SizeBytes()) 592 593 // 5. ErrMempoolIsFull is returned when/if MaxTxsBytes limit is reached. 594 tx3 := kvstore.NewRandomTx(100) 595 _, err = mp.CheckTx(tx3) 596 require.NoError(t, err) 597 598 tx4 := kvstore.NewRandomTx(10) 599 _, err = mp.CheckTx(tx4) 600 if assert.Error(t, err) { 601 assert.IsType(t, ErrMempoolIsFull{}, err) 602 } 603 604 // 6. zero after tx is rechecked and removed due to not being valid anymore 605 app2 := kvstore.NewInMemoryApplication() 606 cc = proxy.NewLocalClientCreator(app2) 607 608 mp, cleanup = newMempoolWithApp(cc) 609 defer cleanup() 610 611 txBytes := kvstore.NewRandomTx(10) 612 613 _, err = mp.CheckTx(txBytes) 614 require.NoError(t, err) 615 assert.EqualValues(t, 10, mp.SizeBytes()) 616 617 appConnCon, _ := cc.NewABCIClient() 618 appConnCon.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "consensus")) 619 err = appConnCon.Start() 620 require.Nil(t, err) 621 t.Cleanup(func() { 622 if err := appConnCon.Stop(); err != nil { 623 t.Error(err) 624 } 625 }) 626 627 res, err := appConnCon.FinalizeBlock(context.Background(), &abci.RequestFinalizeBlock{Txs: [][]byte{txBytes}}) 628 require.NoError(t, err) 629 require.EqualValues(t, 0, res.TxResults[0].Code) 630 require.NotEmpty(t, res.AppHash) 631 632 _, err = appConnCon.Commit(context.Background(), &abci.RequestCommit{}) 633 require.NoError(t, err) 634 635 // Pretend like we committed nothing so txBytes gets rechecked and removed. 636 err = mp.Update(1, []types.Tx{}, abciResponses(0, abci.CodeTypeOK), nil, nil) 637 require.NoError(t, err) 638 assert.EqualValues(t, 10, mp.SizeBytes()) 639 640 // 7. Test RemoveTxByKey function 641 _, err = mp.CheckTx(tx1) 642 require.NoError(t, err) 643 assert.EqualValues(t, 20, mp.SizeBytes()) 644 assert.Error(t, mp.RemoveTxByKey(types.Tx([]byte{0x07}).Key())) 645 assert.EqualValues(t, 20, mp.SizeBytes()) 646 assert.NoError(t, mp.RemoveTxByKey(types.Tx(tx1).Key())) 647 assert.EqualValues(t, 10, mp.SizeBytes()) 648 } 649 650 func TestMempoolNoCacheOverflow(t *testing.T) { 651 sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", cmtrand.Str(6)) 652 app := kvstore.NewInMemoryApplication() 653 _, server := newRemoteApp(t, sockPath, app) 654 t.Cleanup(func() { 655 if err := server.Stop(); err != nil { 656 t.Error(err) 657 } 658 }) 659 cfg := test.ResetTestRoot("mempool_test") 660 mp, cleanup := newMempoolWithAppAndConfig(proxy.NewRemoteClientCreator(sockPath, "socket", true), cfg) 661 defer cleanup() 662 663 // add tx0 664 var tx0 = kvstore.NewTxFromID(0) 665 _, err := mp.CheckTx(tx0) 666 require.NoError(t, err) 667 err = mp.FlushAppConn() 668 require.NoError(t, err) 669 670 // saturate the cache to remove tx0 671 for i := 1; i <= mp.config.CacheSize; i++ { 672 _, err = mp.CheckTx(kvstore.NewTxFromID(i)) 673 require.NoError(t, err) 674 } 675 err = mp.FlushAppConn() 676 require.NoError(t, err) 677 assert.False(t, mp.cache.Has(kvstore.NewTxFromID(0))) 678 679 // add again tx0 680 _, err = mp.CheckTx(tx0) 681 require.NoError(t, err) 682 err = mp.FlushAppConn() 683 require.NoError(t, err) 684 685 // tx0 should appear only once in mp.txs 686 found := 0 687 for e := mp.txs.Front(); e != nil; e = e.Next() { 688 if types.Tx.Key(e.Value.(*mempoolTx).tx) == types.Tx.Key(tx0) { 689 found++ 690 } 691 } 692 assert.True(t, found == 1) 693 } 694 695 // This will non-deterministically catch some concurrency failures like 696 // https://github.com/tendermint/tendermint/issues/3509 697 // TODO: all of the tests should probably also run using the remote proxy app 698 // since otherwise we're not actually testing the concurrency of the mempool here! 699 func TestMempoolRemoteAppConcurrency(t *testing.T) { 700 sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", cmtrand.Str(6)) 701 app := kvstore.NewInMemoryApplication() 702 _, server := newRemoteApp(t, sockPath, app) 703 t.Cleanup(func() { 704 if err := server.Stop(); err != nil { 705 t.Error(err) 706 } 707 }) 708 709 cfg := test.ResetTestRoot("mempool_test") 710 711 mp, cleanup := newMempoolWithAppAndConfig(proxy.NewRemoteClientCreator(sockPath, "socket", true), cfg) 712 defer cleanup() 713 714 // generate small number of txs 715 nTxs := 10 716 txLen := 200 717 txs := NewRandomTxs(nTxs, txLen) 718 719 // simulate a group of peers sending them over and over 720 N := cfg.Mempool.Size 721 for i := 0; i < N; i++ { 722 txNum := mrand.Intn(nTxs) 723 tx := txs[txNum] 724 725 // this will err with ErrTxInCache many times ... 726 mp.CheckTx(tx) //nolint: errcheck // will error 727 } 728 729 require.NoError(t, mp.FlushAppConn()) 730 } 731 732 // caller must close server 733 func newRemoteApp(t *testing.T, addr string, app abci.Application) (abciclient.Client, service.Service) { 734 clientCreator, err := abciclient.NewClient(addr, "socket", true) 735 require.NoError(t, err) 736 737 // Start server 738 server := abciserver.NewSocketServer(addr, app) 739 server.SetLogger(log.TestingLogger().With("module", "abci-server")) 740 if err := server.Start(); err != nil { 741 t.Fatalf("Error starting socket server: %v", err.Error()) 742 } 743 744 return clientCreator, server 745 } 746 747 func abciResponses(n int, code uint32) []*abci.ExecTxResult { 748 responses := make([]*abci.ExecTxResult, 0, n) 749 for i := 0; i < n; i++ { 750 responses = append(responses, &abci.ExecTxResult{Code: code}) 751 } 752 return responses 753 } 754 755 func doCommit(t require.TestingT, mp Mempool, app abci.Application, txs types.Txs, height int64) { 756 rfb := &abci.RequestFinalizeBlock{Txs: make([][]byte, len(txs))} 757 for i, tx := range txs { 758 rfb.Txs[i] = tx 759 } 760 _, e := app.FinalizeBlock(context.Background(), rfb) 761 require.True(t, e == nil) 762 mp.Lock() 763 e = mp.FlushAppConn() 764 require.True(t, e == nil) 765 _, e = app.Commit(context.Background(), &abci.RequestCommit{}) 766 require.True(t, e == nil) 767 e = mp.Update(height, txs, abciResponses(txs.Len(), abci.CodeTypeOK), nil, nil) 768 require.True(t, e == nil) 769 mp.Unlock() 770 }