You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

543 lines
16 KiB

8 years ago
8 years ago
  1. package mempool
  2. import (
  3. "crypto/rand"
  4. "crypto/sha256"
  5. "encoding/binary"
  6. "fmt"
  7. "io/ioutil"
  8. "os"
  9. "path/filepath"
  10. "testing"
  11. "time"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/require"
  14. amino "github.com/tendermint/go-amino"
  15. "github.com/tendermint/tendermint/abci/example/counter"
  16. "github.com/tendermint/tendermint/abci/example/kvstore"
  17. abci "github.com/tendermint/tendermint/abci/types"
  18. cfg "github.com/tendermint/tendermint/config"
  19. cmn "github.com/tendermint/tendermint/libs/common"
  20. "github.com/tendermint/tendermint/libs/log"
  21. "github.com/tendermint/tendermint/proxy"
  22. "github.com/tendermint/tendermint/types"
  23. )
  24. // A cleanupFunc cleans up any config / test files created for a particular
  25. // test.
  26. type cleanupFunc func()
  27. func newMempoolWithApp(cc proxy.ClientCreator) (*Mempool, cleanupFunc) {
  28. return newMempoolWithAppAndConfig(cc, cfg.ResetTestRoot("mempool_test"))
  29. }
  30. func newMempoolWithAppAndConfig(cc proxy.ClientCreator, config *cfg.Config) (*Mempool, cleanupFunc) {
  31. appConnMem, _ := cc.NewABCIClient()
  32. appConnMem.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "mempool"))
  33. err := appConnMem.Start()
  34. if err != nil {
  35. panic(err)
  36. }
  37. mempool := NewMempool(config.Mempool, appConnMem, 0)
  38. mempool.SetLogger(log.TestingLogger())
  39. return mempool, func() { os.RemoveAll(config.RootDir) }
  40. }
  41. func ensureNoFire(t *testing.T, ch <-chan struct{}, timeoutMS int) {
  42. timer := time.NewTimer(time.Duration(timeoutMS) * time.Millisecond)
  43. select {
  44. case <-ch:
  45. t.Fatal("Expected not to fire")
  46. case <-timer.C:
  47. }
  48. }
  49. func ensureFire(t *testing.T, ch <-chan struct{}, timeoutMS int) {
  50. timer := time.NewTimer(time.Duration(timeoutMS) * time.Millisecond)
  51. select {
  52. case <-ch:
  53. case <-timer.C:
  54. t.Fatal("Expected to fire")
  55. }
  56. }
  57. func checkTxs(t *testing.T, mempool *Mempool, count int) types.Txs {
  58. txs := make(types.Txs, count)
  59. for i := 0; i < count; i++ {
  60. txBytes := make([]byte, 20)
  61. txs[i] = txBytes
  62. _, err := rand.Read(txBytes)
  63. if err != nil {
  64. t.Error(err)
  65. }
  66. if err := mempool.CheckTx(txBytes, nil); err != nil {
  67. // Skip invalid txs.
  68. // TestMempoolFilters will fail otherwise. It asserts a number of txs
  69. // returned.
  70. if IsPreCheckError(err) {
  71. continue
  72. }
  73. t.Fatalf("CheckTx failed: %v while checking #%d tx", err, i)
  74. }
  75. }
  76. return txs
  77. }
  78. func TestReapMaxBytesMaxGas(t *testing.T) {
  79. app := kvstore.NewKVStoreApplication()
  80. cc := proxy.NewLocalClientCreator(app)
  81. mempool, cleanup := newMempoolWithApp(cc)
  82. defer cleanup()
  83. // Ensure gas calculation behaves as expected
  84. checkTxs(t, mempool, 1)
  85. tx0 := mempool.TxsFront().Value.(*mempoolTx)
  86. // assert that kv store has gas wanted = 1.
  87. require.Equal(t, app.CheckTx(tx0.tx).GasWanted, int64(1), "KVStore had a gas value neq to 1")
  88. require.Equal(t, tx0.gasWanted, int64(1), "transactions gas was set incorrectly")
  89. // ensure each tx is 20 bytes long
  90. require.Equal(t, len(tx0.tx), 20, "Tx is longer than 20 bytes")
  91. mempool.Flush()
  92. // each table driven test creates numTxsToCreate txs with checkTx, and at the end clears all remaining txs.
  93. // each tx has 20 bytes + amino overhead = 21 bytes, 1 gas
  94. tests := []struct {
  95. numTxsToCreate int
  96. maxBytes int64
  97. maxGas int64
  98. expectedNumTxs int
  99. }{
  100. {20, -1, -1, 20},
  101. {20, -1, 0, 0},
  102. {20, -1, 10, 10},
  103. {20, -1, 30, 20},
  104. {20, 0, -1, 0},
  105. {20, 0, 10, 0},
  106. {20, 10, 10, 0},
  107. {20, 22, 10, 1},
  108. {20, 220, -1, 10},
  109. {20, 220, 5, 5},
  110. {20, 220, 10, 10},
  111. {20, 220, 15, 10},
  112. {20, 20000, -1, 20},
  113. {20, 20000, 5, 5},
  114. {20, 20000, 30, 20},
  115. }
  116. for tcIndex, tt := range tests {
  117. checkTxs(t, mempool, tt.numTxsToCreate)
  118. got := mempool.ReapMaxBytesMaxGas(tt.maxBytes, tt.maxGas)
  119. assert.Equal(t, tt.expectedNumTxs, len(got), "Got %d txs, expected %d, tc #%d",
  120. len(got), tt.expectedNumTxs, tcIndex)
  121. mempool.Flush()
  122. }
  123. }
  124. func TestMempoolFilters(t *testing.T) {
  125. app := kvstore.NewKVStoreApplication()
  126. cc := proxy.NewLocalClientCreator(app)
  127. mempool, cleanup := newMempoolWithApp(cc)
  128. defer cleanup()
  129. emptyTxArr := []types.Tx{[]byte{}}
  130. nopPreFilter := func(tx types.Tx) error { return nil }
  131. nopPostFilter := func(tx types.Tx, res *abci.ResponseCheckTx) error { return nil }
  132. // each table driven test creates numTxsToCreate txs with checkTx, and at the end clears all remaining txs.
  133. // each tx has 20 bytes + amino overhead = 21 bytes, 1 gas
  134. tests := []struct {
  135. numTxsToCreate int
  136. preFilter PreCheckFunc
  137. postFilter PostCheckFunc
  138. expectedNumTxs int
  139. }{
  140. {10, nopPreFilter, nopPostFilter, 10},
  141. {10, PreCheckAminoMaxBytes(10), nopPostFilter, 0},
  142. {10, PreCheckAminoMaxBytes(20), nopPostFilter, 0},
  143. {10, PreCheckAminoMaxBytes(22), nopPostFilter, 10},
  144. {10, nopPreFilter, PostCheckMaxGas(-1), 10},
  145. {10, nopPreFilter, PostCheckMaxGas(0), 0},
  146. {10, nopPreFilter, PostCheckMaxGas(1), 10},
  147. {10, nopPreFilter, PostCheckMaxGas(3000), 10},
  148. {10, PreCheckAminoMaxBytes(10), PostCheckMaxGas(20), 0},
  149. {10, PreCheckAminoMaxBytes(30), PostCheckMaxGas(20), 10},
  150. {10, PreCheckAminoMaxBytes(22), PostCheckMaxGas(1), 10},
  151. {10, PreCheckAminoMaxBytes(22), PostCheckMaxGas(0), 0},
  152. }
  153. for tcIndex, tt := range tests {
  154. mempool.Update(1, emptyTxArr, tt.preFilter, tt.postFilter)
  155. checkTxs(t, mempool, tt.numTxsToCreate)
  156. require.Equal(t, tt.expectedNumTxs, mempool.Size(), "mempool had the incorrect size, on test case %d", tcIndex)
  157. mempool.Flush()
  158. }
  159. }
  160. func TestMempoolUpdateAddsTxsToCache(t *testing.T) {
  161. app := kvstore.NewKVStoreApplication()
  162. cc := proxy.NewLocalClientCreator(app)
  163. mempool, cleanup := newMempoolWithApp(cc)
  164. defer cleanup()
  165. mempool.Update(1, []types.Tx{[]byte{0x01}}, nil, nil)
  166. err := mempool.CheckTx([]byte{0x01}, nil)
  167. if assert.Error(t, err) {
  168. assert.Equal(t, ErrTxInCache, err)
  169. }
  170. }
  171. func TestTxsAvailable(t *testing.T) {
  172. app := kvstore.NewKVStoreApplication()
  173. cc := proxy.NewLocalClientCreator(app)
  174. mempool, cleanup := newMempoolWithApp(cc)
  175. defer cleanup()
  176. mempool.EnableTxsAvailable()
  177. timeoutMS := 500
  178. // with no txs, it shouldnt fire
  179. ensureNoFire(t, mempool.TxsAvailable(), timeoutMS)
  180. // send a bunch of txs, it should only fire once
  181. txs := checkTxs(t, mempool, 100)
  182. ensureFire(t, mempool.TxsAvailable(), timeoutMS)
  183. ensureNoFire(t, mempool.TxsAvailable(), timeoutMS)
  184. // call update with half the txs.
  185. // it should fire once now for the new height
  186. // since there are still txs left
  187. committedTxs, txs := txs[:50], txs[50:]
  188. if err := mempool.Update(1, committedTxs, nil, nil); err != nil {
  189. t.Error(err)
  190. }
  191. ensureFire(t, mempool.TxsAvailable(), timeoutMS)
  192. ensureNoFire(t, mempool.TxsAvailable(), timeoutMS)
  193. // send a bunch more txs. we already fired for this height so it shouldnt fire again
  194. moreTxs := checkTxs(t, mempool, 50)
  195. ensureNoFire(t, mempool.TxsAvailable(), timeoutMS)
  196. // now call update with all the txs. it should not fire as there are no txs left
  197. committedTxs = append(txs, moreTxs...)
  198. if err := mempool.Update(2, committedTxs, nil, nil); err != nil {
  199. t.Error(err)
  200. }
  201. ensureNoFire(t, mempool.TxsAvailable(), timeoutMS)
  202. // send a bunch more txs, it should only fire once
  203. checkTxs(t, mempool, 100)
  204. ensureFire(t, mempool.TxsAvailable(), timeoutMS)
  205. ensureNoFire(t, mempool.TxsAvailable(), timeoutMS)
  206. }
  207. func TestSerialReap(t *testing.T) {
  208. app := counter.NewCounterApplication(true)
  209. app.SetOption(abci.RequestSetOption{Key: "serial", Value: "on"})
  210. cc := proxy.NewLocalClientCreator(app)
  211. mempool, cleanup := newMempoolWithApp(cc)
  212. defer cleanup()
  213. appConnCon, _ := cc.NewABCIClient()
  214. appConnCon.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "consensus"))
  215. err := appConnCon.Start()
  216. require.Nil(t, err)
  217. cacheMap := make(map[string]struct{})
  218. deliverTxsRange := func(start, end int) {
  219. // Deliver some txs.
  220. for i := start; i < end; i++ {
  221. // This will succeed
  222. txBytes := make([]byte, 8)
  223. binary.BigEndian.PutUint64(txBytes, uint64(i))
  224. err := mempool.CheckTx(txBytes, nil)
  225. _, cached := cacheMap[string(txBytes)]
  226. if cached {
  227. require.NotNil(t, err, "expected error for cached tx")
  228. } else {
  229. require.Nil(t, err, "expected no err for uncached tx")
  230. }
  231. cacheMap[string(txBytes)] = struct{}{}
  232. // Duplicates are cached and should return error
  233. err = mempool.CheckTx(txBytes, nil)
  234. require.NotNil(t, err, "Expected error after CheckTx on duplicated tx")
  235. }
  236. }
  237. reapCheck := func(exp int) {
  238. txs := mempool.ReapMaxBytesMaxGas(-1, -1)
  239. require.Equal(t, len(txs), exp, fmt.Sprintf("Expected to reap %v txs but got %v", exp, len(txs)))
  240. }
  241. updateRange := func(start, end int) {
  242. txs := make([]types.Tx, 0)
  243. for i := start; i < end; i++ {
  244. txBytes := make([]byte, 8)
  245. binary.BigEndian.PutUint64(txBytes, uint64(i))
  246. txs = append(txs, txBytes)
  247. }
  248. if err := mempool.Update(0, txs, nil, nil); err != nil {
  249. t.Error(err)
  250. }
  251. }
  252. commitRange := func(start, end int) {
  253. // Deliver some txs.
  254. for i := start; i < end; i++ {
  255. txBytes := make([]byte, 8)
  256. binary.BigEndian.PutUint64(txBytes, uint64(i))
  257. res, err := appConnCon.DeliverTxSync(txBytes)
  258. if err != nil {
  259. t.Errorf("Client error committing tx: %v", err)
  260. }
  261. if res.IsErr() {
  262. t.Errorf("Error committing tx. Code:%v result:%X log:%v",
  263. res.Code, res.Data, res.Log)
  264. }
  265. }
  266. res, err := appConnCon.CommitSync()
  267. if err != nil {
  268. t.Errorf("Client error committing: %v", err)
  269. }
  270. if len(res.Data) != 8 {
  271. t.Errorf("Error committing. Hash:%X", res.Data)
  272. }
  273. }
  274. //----------------------------------------
  275. // Deliver some txs.
  276. deliverTxsRange(0, 100)
  277. // Reap the txs.
  278. reapCheck(100)
  279. // Reap again. We should get the same amount
  280. reapCheck(100)
  281. // Deliver 0 to 999, we should reap 900 new txs
  282. // because 100 were already counted.
  283. deliverTxsRange(0, 1000)
  284. // Reap the txs.
  285. reapCheck(1000)
  286. // Reap again. We should get the same amount
  287. reapCheck(1000)
  288. // Commit from the conensus AppConn
  289. commitRange(0, 500)
  290. updateRange(0, 500)
  291. // We should have 500 left.
  292. reapCheck(500)
  293. // Deliver 100 invalid txs and 100 valid txs
  294. deliverTxsRange(900, 1100)
  295. // We should have 600 now.
  296. reapCheck(600)
  297. }
  298. func TestCacheRemove(t *testing.T) {
  299. cache := newMapTxCache(100)
  300. numTxs := 10
  301. txs := make([][]byte, numTxs)
  302. for i := 0; i < numTxs; i++ {
  303. // probability of collision is 2**-256
  304. txBytes := make([]byte, 32)
  305. rand.Read(txBytes)
  306. txs[i] = txBytes
  307. cache.Push(txBytes)
  308. // make sure its added to both the linked list and the map
  309. require.Equal(t, i+1, len(cache.map_))
  310. require.Equal(t, i+1, cache.list.Len())
  311. }
  312. for i := 0; i < numTxs; i++ {
  313. cache.Remove(txs[i])
  314. // make sure its removed from both the map and the linked list
  315. require.Equal(t, numTxs-(i+1), len(cache.map_))
  316. require.Equal(t, numTxs-(i+1), cache.list.Len())
  317. }
  318. }
  319. func TestMempoolCloseWAL(t *testing.T) {
  320. // 1. Create the temporary directory for mempool and WAL testing.
  321. rootDir, err := ioutil.TempDir("", "mempool-test")
  322. require.Nil(t, err, "expecting successful tmpdir creation")
  323. defer os.RemoveAll(rootDir)
  324. // 2. Ensure that it doesn't contain any elements -- Sanity check
  325. m1, err := filepath.Glob(filepath.Join(rootDir, "*"))
  326. require.Nil(t, err, "successful globbing expected")
  327. require.Equal(t, 0, len(m1), "no matches yet")
  328. // 3. Create the mempool
  329. wcfg := cfg.DefaultMempoolConfig()
  330. wcfg.RootDir = rootDir
  331. defer os.RemoveAll(wcfg.RootDir)
  332. app := kvstore.NewKVStoreApplication()
  333. cc := proxy.NewLocalClientCreator(app)
  334. appConnMem, _ := cc.NewABCIClient()
  335. mempool := NewMempool(wcfg, appConnMem, 10)
  336. mempool.InitWAL()
  337. // 4. Ensure that the directory contains the WAL file
  338. m2, err := filepath.Glob(filepath.Join(rootDir, "*"))
  339. require.Nil(t, err, "successful globbing expected")
  340. require.Equal(t, 1, len(m2), "expecting the wal match in")
  341. // 5. Write some contents to the WAL
  342. mempool.CheckTx(types.Tx([]byte("foo")), nil)
  343. walFilepath := mempool.wal.Path
  344. sum1 := checksumFile(walFilepath, t)
  345. // 6. Sanity check to ensure that the written TX matches the expectation.
  346. require.Equal(t, sum1, checksumIt([]byte("foo\n")), "foo with a newline should be written")
  347. // 7. Invoke CloseWAL() and ensure it discards the
  348. // WAL thus any other write won't go through.
  349. mempool.CloseWAL()
  350. mempool.CheckTx(types.Tx([]byte("bar")), nil)
  351. sum2 := checksumFile(walFilepath, t)
  352. require.Equal(t, sum1, sum2, "expected no change to the WAL after invoking CloseWAL() since it was discarded")
  353. // 8. Sanity check to ensure that the WAL file still exists
  354. m3, err := filepath.Glob(filepath.Join(rootDir, "*"))
  355. require.Nil(t, err, "successful globbing expected")
  356. require.Equal(t, 1, len(m3), "expecting the wal match in")
  357. }
  358. // Size of the amino encoded TxMessage is the length of the
  359. // encoded byte array, plus 1 for the struct field, plus 4
  360. // for the amino prefix.
  361. func txMessageSize(tx types.Tx) int {
  362. return amino.ByteSliceSize(tx) + 1 + 4
  363. }
  364. func TestMempoolMaxMsgSize(t *testing.T) {
  365. app := kvstore.NewKVStoreApplication()
  366. cc := proxy.NewLocalClientCreator(app)
  367. mempl, cleanup := newMempoolWithApp(cc)
  368. defer cleanup()
  369. testCases := []struct {
  370. len int
  371. err bool
  372. }{
  373. // check small txs. no error
  374. {10, false},
  375. {1000, false},
  376. {1000000, false},
  377. // check around maxTxSize
  378. // changes from no error to error
  379. {maxTxSize - 2, false},
  380. {maxTxSize - 1, false},
  381. {maxTxSize, false},
  382. {maxTxSize + 1, true},
  383. {maxTxSize + 2, true},
  384. // check around maxMsgSize. all error
  385. {maxMsgSize - 1, true},
  386. {maxMsgSize, true},
  387. {maxMsgSize + 1, true},
  388. }
  389. for i, testCase := range testCases {
  390. caseString := fmt.Sprintf("case %d, len %d", i, testCase.len)
  391. tx := cmn.RandBytes(testCase.len)
  392. err := mempl.CheckTx(tx, nil)
  393. msg := &TxMessage{tx}
  394. encoded := cdc.MustMarshalBinaryBare(msg)
  395. require.Equal(t, len(encoded), txMessageSize(tx), caseString)
  396. if !testCase.err {
  397. require.True(t, len(encoded) <= maxMsgSize, caseString)
  398. require.NoError(t, err, caseString)
  399. } else {
  400. require.True(t, len(encoded) > maxMsgSize, caseString)
  401. require.Equal(t, err, ErrTxTooLarge, caseString)
  402. }
  403. }
  404. }
  405. func TestMempoolTxsBytes(t *testing.T) {
  406. app := kvstore.NewKVStoreApplication()
  407. cc := proxy.NewLocalClientCreator(app)
  408. config := cfg.ResetTestRoot("mempool_test")
  409. config.Mempool.MaxTxsBytes = 10
  410. mempool, cleanup := newMempoolWithAppAndConfig(cc, config)
  411. defer cleanup()
  412. // 1. zero by default
  413. assert.EqualValues(t, 0, mempool.TxsBytes())
  414. // 2. len(tx) after CheckTx
  415. err := mempool.CheckTx([]byte{0x01}, nil)
  416. require.NoError(t, err)
  417. assert.EqualValues(t, 1, mempool.TxsBytes())
  418. // 3. zero again after tx is removed by Update
  419. mempool.Update(1, []types.Tx{[]byte{0x01}}, nil, nil)
  420. assert.EqualValues(t, 0, mempool.TxsBytes())
  421. // 4. zero after Flush
  422. err = mempool.CheckTx([]byte{0x02, 0x03}, nil)
  423. require.NoError(t, err)
  424. assert.EqualValues(t, 2, mempool.TxsBytes())
  425. mempool.Flush()
  426. assert.EqualValues(t, 0, mempool.TxsBytes())
  427. // 5. ErrMempoolIsFull is returned when/if MaxTxsBytes limit is reached.
  428. err = mempool.CheckTx([]byte{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}, nil)
  429. require.NoError(t, err)
  430. err = mempool.CheckTx([]byte{0x05}, nil)
  431. if assert.Error(t, err) {
  432. assert.IsType(t, ErrMempoolIsFull{}, err)
  433. }
  434. // 6. zero after tx is rechecked and removed due to not being valid anymore
  435. app2 := counter.NewCounterApplication(true)
  436. cc = proxy.NewLocalClientCreator(app2)
  437. mempool, cleanup = newMempoolWithApp(cc)
  438. defer cleanup()
  439. txBytes := make([]byte, 8)
  440. binary.BigEndian.PutUint64(txBytes, uint64(0))
  441. err = mempool.CheckTx(txBytes, nil)
  442. require.NoError(t, err)
  443. assert.EqualValues(t, 8, mempool.TxsBytes())
  444. appConnCon, _ := cc.NewABCIClient()
  445. appConnCon.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "consensus"))
  446. err = appConnCon.Start()
  447. require.Nil(t, err)
  448. defer appConnCon.Stop()
  449. res, err := appConnCon.DeliverTxSync(txBytes)
  450. require.NoError(t, err)
  451. require.EqualValues(t, 0, res.Code)
  452. res2, err := appConnCon.CommitSync()
  453. require.NoError(t, err)
  454. require.NotEmpty(t, res2.Data)
  455. // Pretend like we committed nothing so txBytes gets rechecked and removed.
  456. mempool.Update(1, []types.Tx{}, nil, nil)
  457. assert.EqualValues(t, 0, mempool.TxsBytes())
  458. }
  459. func checksumIt(data []byte) string {
  460. h := sha256.New()
  461. h.Write(data)
  462. return fmt.Sprintf("%x", h.Sum(nil))
  463. }
  464. func checksumFile(p string, t *testing.T) string {
  465. data, err := ioutil.ReadFile(p)
  466. require.Nil(t, err, "expecting successful read of %q", p)
  467. return checksumIt(data)
  468. }