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