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