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.

578 lines
15 KiB

  1. package mempool
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "math/rand"
  8. "os"
  9. "sort"
  10. "strconv"
  11. "strings"
  12. "sync"
  13. "testing"
  14. "time"
  15. "github.com/stretchr/testify/require"
  16. abciclient "github.com/tendermint/tendermint/abci/client"
  17. "github.com/tendermint/tendermint/abci/example/code"
  18. "github.com/tendermint/tendermint/abci/example/kvstore"
  19. abci "github.com/tendermint/tendermint/abci/types"
  20. "github.com/tendermint/tendermint/config"
  21. "github.com/tendermint/tendermint/libs/log"
  22. "github.com/tendermint/tendermint/types"
  23. )
  24. // application extends the KV store application by overriding CheckTx to provide
  25. // transaction priority based on the value in the key/value pair.
  26. type application struct {
  27. *kvstore.Application
  28. }
  29. type testTx struct {
  30. tx types.Tx
  31. priority int64
  32. }
  33. func (app *application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
  34. var (
  35. priority int64
  36. sender string
  37. )
  38. // infer the priority from the raw transaction value (sender=key=value)
  39. parts := bytes.Split(req.Tx, []byte("="))
  40. if len(parts) == 3 {
  41. v, err := strconv.ParseInt(string(parts[2]), 10, 64)
  42. if err != nil {
  43. return abci.ResponseCheckTx{
  44. Priority: priority,
  45. Code: 100,
  46. GasWanted: 1,
  47. }
  48. }
  49. priority = v
  50. sender = string(parts[0])
  51. } else {
  52. return abci.ResponseCheckTx{
  53. Priority: priority,
  54. Code: 101,
  55. GasWanted: 1,
  56. }
  57. }
  58. return abci.ResponseCheckTx{
  59. Priority: priority,
  60. Sender: sender,
  61. Code: code.CodeTypeOK,
  62. GasWanted: 1,
  63. }
  64. }
  65. func setup(ctx context.Context, t testing.TB, cacheSize int, options ...TxMempoolOption) *TxMempool {
  66. t.Helper()
  67. var cancel context.CancelFunc
  68. ctx, cancel = context.WithCancel(ctx)
  69. app := &application{kvstore.NewApplication()}
  70. cc := abciclient.NewLocalCreator(app)
  71. logger := log.TestingLogger()
  72. cfg, err := config.ResetTestRoot(strings.ReplaceAll(t.Name(), "/", "|"))
  73. require.NoError(t, err)
  74. cfg.Mempool.CacheSize = cacheSize
  75. appConnMem, err := cc(logger)
  76. require.NoError(t, err)
  77. require.NoError(t, appConnMem.Start(ctx))
  78. t.Cleanup(func() {
  79. os.RemoveAll(cfg.RootDir)
  80. cancel()
  81. appConnMem.Wait()
  82. })
  83. return NewTxMempool(logger.With("test", t.Name()), cfg.Mempool, appConnMem, 0, options...)
  84. }
  85. func checkTxs(t *testing.T, txmp *TxMempool, numTxs int, peerID uint16) []testTx {
  86. txs := make([]testTx, numTxs)
  87. txInfo := TxInfo{SenderID: peerID}
  88. rng := rand.New(rand.NewSource(time.Now().UnixNano()))
  89. for i := 0; i < numTxs; i++ {
  90. prefix := make([]byte, 20)
  91. _, err := rng.Read(prefix)
  92. require.NoError(t, err)
  93. priority := int64(rng.Intn(9999-1000) + 1000)
  94. txs[i] = testTx{
  95. tx: []byte(fmt.Sprintf("sender-%d-%d=%X=%d", i, peerID, prefix, priority)),
  96. priority: priority,
  97. }
  98. require.NoError(t, txmp.CheckTx(context.Background(), txs[i].tx, nil, txInfo))
  99. }
  100. return txs
  101. }
  102. func convertTex(in []testTx) types.Txs {
  103. out := make([]types.Tx, len(in))
  104. for idx := range in {
  105. out[idx] = in[idx].tx
  106. }
  107. return out
  108. }
  109. func TestTxMempool_TxsAvailable(t *testing.T) {
  110. ctx, cancel := context.WithCancel(context.Background())
  111. defer cancel()
  112. txmp := setup(ctx, t, 0)
  113. txmp.EnableTxsAvailable()
  114. ensureNoTxFire := func() {
  115. timer := time.NewTimer(500 * time.Millisecond)
  116. select {
  117. case <-txmp.TxsAvailable():
  118. require.Fail(t, "unexpected transactions event")
  119. case <-timer.C:
  120. }
  121. }
  122. ensureTxFire := func() {
  123. timer := time.NewTimer(500 * time.Millisecond)
  124. select {
  125. case <-txmp.TxsAvailable():
  126. case <-timer.C:
  127. require.Fail(t, "expected transactions event")
  128. }
  129. }
  130. // ensure no event as we have not executed any transactions yet
  131. ensureNoTxFire()
  132. // Execute CheckTx for some transactions and ensure TxsAvailable only fires
  133. // once.
  134. txs := checkTxs(t, txmp, 100, 0)
  135. ensureTxFire()
  136. ensureNoTxFire()
  137. rawTxs := make([]types.Tx, len(txs))
  138. for i, tx := range txs {
  139. rawTxs[i] = tx.tx
  140. }
  141. responses := make([]*abci.ResponseDeliverTx, len(rawTxs[:50]))
  142. for i := 0; i < len(responses); i++ {
  143. responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
  144. }
  145. // commit half the transactions and ensure we fire an event
  146. txmp.Lock()
  147. require.NoError(t, txmp.Update(1, rawTxs[:50], responses, nil, nil))
  148. txmp.Unlock()
  149. ensureTxFire()
  150. ensureNoTxFire()
  151. // Execute CheckTx for more transactions and ensure we do not fire another
  152. // event as we're still on the same height (1).
  153. _ = checkTxs(t, txmp, 100, 0)
  154. ensureNoTxFire()
  155. }
  156. func TestTxMempool_Size(t *testing.T) {
  157. ctx, cancel := context.WithCancel(context.Background())
  158. defer cancel()
  159. txmp := setup(ctx, t, 0)
  160. txs := checkTxs(t, txmp, 100, 0)
  161. require.Equal(t, len(txs), txmp.Size())
  162. require.Equal(t, int64(5690), txmp.SizeBytes())
  163. rawTxs := make([]types.Tx, len(txs))
  164. for i, tx := range txs {
  165. rawTxs[i] = tx.tx
  166. }
  167. responses := make([]*abci.ResponseDeliverTx, len(rawTxs[:50]))
  168. for i := 0; i < len(responses); i++ {
  169. responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
  170. }
  171. txmp.Lock()
  172. require.NoError(t, txmp.Update(1, rawTxs[:50], responses, nil, nil))
  173. txmp.Unlock()
  174. require.Equal(t, len(rawTxs)/2, txmp.Size())
  175. require.Equal(t, int64(2850), txmp.SizeBytes())
  176. }
  177. func TestTxMempool_Flush(t *testing.T) {
  178. ctx, cancel := context.WithCancel(context.Background())
  179. defer cancel()
  180. txmp := setup(ctx, t, 0)
  181. txs := checkTxs(t, txmp, 100, 0)
  182. require.Equal(t, len(txs), txmp.Size())
  183. require.Equal(t, int64(5690), txmp.SizeBytes())
  184. rawTxs := make([]types.Tx, len(txs))
  185. for i, tx := range txs {
  186. rawTxs[i] = tx.tx
  187. }
  188. responses := make([]*abci.ResponseDeliverTx, len(rawTxs[:50]))
  189. for i := 0; i < len(responses); i++ {
  190. responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
  191. }
  192. txmp.Lock()
  193. require.NoError(t, txmp.Update(1, rawTxs[:50], responses, nil, nil))
  194. txmp.Unlock()
  195. txmp.Flush()
  196. require.Zero(t, txmp.Size())
  197. require.Equal(t, int64(0), txmp.SizeBytes())
  198. }
  199. func TestTxMempool_ReapMaxBytesMaxGas(t *testing.T) {
  200. ctx, cancel := context.WithCancel(context.Background())
  201. defer cancel()
  202. txmp := setup(ctx, t, 0)
  203. tTxs := checkTxs(t, txmp, 100, 0) // all txs request 1 gas unit
  204. require.Equal(t, len(tTxs), txmp.Size())
  205. require.Equal(t, int64(5690), txmp.SizeBytes())
  206. txMap := make(map[types.TxKey]testTx)
  207. priorities := make([]int64, len(tTxs))
  208. for i, tTx := range tTxs {
  209. txMap[tTx.tx.Key()] = tTx
  210. priorities[i] = tTx.priority
  211. }
  212. sort.Slice(priorities, func(i, j int) bool {
  213. // sort by priority, i.e. decreasing order
  214. return priorities[i] > priorities[j]
  215. })
  216. ensurePrioritized := func(reapedTxs types.Txs) {
  217. reapedPriorities := make([]int64, len(reapedTxs))
  218. for i, rTx := range reapedTxs {
  219. reapedPriorities[i] = txMap[rTx.Key()].priority
  220. }
  221. require.Equal(t, priorities[:len(reapedPriorities)], reapedPriorities)
  222. }
  223. // reap by gas capacity only
  224. reapedTxs := txmp.ReapMaxBytesMaxGas(-1, 50)
  225. ensurePrioritized(reapedTxs)
  226. require.Equal(t, len(tTxs), txmp.Size())
  227. require.Equal(t, int64(5690), txmp.SizeBytes())
  228. require.Len(t, reapedTxs, 50)
  229. // reap by transaction bytes only
  230. reapedTxs = txmp.ReapMaxBytesMaxGas(1000, -1)
  231. ensurePrioritized(reapedTxs)
  232. require.Equal(t, len(tTxs), txmp.Size())
  233. require.Equal(t, int64(5690), txmp.SizeBytes())
  234. require.GreaterOrEqual(t, len(reapedTxs), 16)
  235. // Reap by both transaction bytes and gas, where the size yields 31 reaped
  236. // transactions and the gas limit reaps 25 transactions.
  237. reapedTxs = txmp.ReapMaxBytesMaxGas(1500, 30)
  238. ensurePrioritized(reapedTxs)
  239. require.Equal(t, len(tTxs), txmp.Size())
  240. require.Equal(t, int64(5690), txmp.SizeBytes())
  241. require.Len(t, reapedTxs, 25)
  242. }
  243. func TestTxMempool_ReapMaxTxs(t *testing.T) {
  244. ctx, cancel := context.WithCancel(context.Background())
  245. defer cancel()
  246. txmp := setup(ctx, t, 0)
  247. tTxs := checkTxs(t, txmp, 100, 0)
  248. require.Equal(t, len(tTxs), txmp.Size())
  249. require.Equal(t, int64(5690), txmp.SizeBytes())
  250. txMap := make(map[types.TxKey]testTx)
  251. priorities := make([]int64, len(tTxs))
  252. for i, tTx := range tTxs {
  253. txMap[tTx.tx.Key()] = tTx
  254. priorities[i] = tTx.priority
  255. }
  256. sort.Slice(priorities, func(i, j int) bool {
  257. // sort by priority, i.e. decreasing order
  258. return priorities[i] > priorities[j]
  259. })
  260. ensurePrioritized := func(reapedTxs types.Txs) {
  261. reapedPriorities := make([]int64, len(reapedTxs))
  262. for i, rTx := range reapedTxs {
  263. reapedPriorities[i] = txMap[rTx.Key()].priority
  264. }
  265. require.Equal(t, priorities[:len(reapedPriorities)], reapedPriorities)
  266. }
  267. // reap all transactions
  268. reapedTxs := txmp.ReapMaxTxs(-1)
  269. ensurePrioritized(reapedTxs)
  270. require.Equal(t, len(tTxs), txmp.Size())
  271. require.Equal(t, int64(5690), txmp.SizeBytes())
  272. require.Len(t, reapedTxs, len(tTxs))
  273. // reap a single transaction
  274. reapedTxs = txmp.ReapMaxTxs(1)
  275. ensurePrioritized(reapedTxs)
  276. require.Equal(t, len(tTxs), txmp.Size())
  277. require.Equal(t, int64(5690), txmp.SizeBytes())
  278. require.Len(t, reapedTxs, 1)
  279. // reap half of the transactions
  280. reapedTxs = txmp.ReapMaxTxs(len(tTxs) / 2)
  281. ensurePrioritized(reapedTxs)
  282. require.Equal(t, len(tTxs), txmp.Size())
  283. require.Equal(t, int64(5690), txmp.SizeBytes())
  284. require.Len(t, reapedTxs, len(tTxs)/2)
  285. }
  286. func TestTxMempool_CheckTxExceedsMaxSize(t *testing.T) {
  287. ctx, cancel := context.WithCancel(context.Background())
  288. defer cancel()
  289. txmp := setup(ctx, t, 0)
  290. rng := rand.New(rand.NewSource(time.Now().UnixNano()))
  291. tx := make([]byte, txmp.config.MaxTxBytes+1)
  292. _, err := rng.Read(tx)
  293. require.NoError(t, err)
  294. require.Error(t, txmp.CheckTx(context.Background(), tx, nil, TxInfo{SenderID: 0}))
  295. tx = make([]byte, txmp.config.MaxTxBytes-1)
  296. _, err = rng.Read(tx)
  297. require.NoError(t, err)
  298. require.NoError(t, txmp.CheckTx(context.Background(), tx, nil, TxInfo{SenderID: 0}))
  299. }
  300. func TestTxMempool_CheckTxSamePeer(t *testing.T) {
  301. ctx, cancel := context.WithCancel(context.Background())
  302. defer cancel()
  303. txmp := setup(ctx, t, 100)
  304. peerID := uint16(1)
  305. rng := rand.New(rand.NewSource(time.Now().UnixNano()))
  306. prefix := make([]byte, 20)
  307. _, err := rng.Read(prefix)
  308. require.NoError(t, err)
  309. tx := []byte(fmt.Sprintf("sender-0=%X=%d", prefix, 50))
  310. require.NoError(t, txmp.CheckTx(context.Background(), tx, nil, TxInfo{SenderID: peerID}))
  311. require.Error(t, txmp.CheckTx(context.Background(), tx, nil, TxInfo{SenderID: peerID}))
  312. }
  313. func TestTxMempool_CheckTxSameSender(t *testing.T) {
  314. ctx, cancel := context.WithCancel(context.Background())
  315. defer cancel()
  316. txmp := setup(ctx, t, 100)
  317. peerID := uint16(1)
  318. rng := rand.New(rand.NewSource(time.Now().UnixNano()))
  319. prefix1 := make([]byte, 20)
  320. _, err := rng.Read(prefix1)
  321. require.NoError(t, err)
  322. prefix2 := make([]byte, 20)
  323. _, err = rng.Read(prefix2)
  324. require.NoError(t, err)
  325. tx1 := []byte(fmt.Sprintf("sender-0=%X=%d", prefix1, 50))
  326. tx2 := []byte(fmt.Sprintf("sender-0=%X=%d", prefix2, 50))
  327. require.NoError(t, txmp.CheckTx(context.Background(), tx1, nil, TxInfo{SenderID: peerID}))
  328. require.Equal(t, 1, txmp.Size())
  329. require.NoError(t, txmp.CheckTx(context.Background(), tx2, nil, TxInfo{SenderID: peerID}))
  330. require.Equal(t, 1, txmp.Size())
  331. }
  332. func TestTxMempool_ConcurrentTxs(t *testing.T) {
  333. ctx, cancel := context.WithCancel(context.Background())
  334. defer cancel()
  335. txmp := setup(ctx, t, 100)
  336. rng := rand.New(rand.NewSource(time.Now().UnixNano()))
  337. checkTxDone := make(chan struct{})
  338. var wg sync.WaitGroup
  339. wg.Add(1)
  340. go func() {
  341. for i := 0; i < 20; i++ {
  342. _ = checkTxs(t, txmp, 100, 0)
  343. dur := rng.Intn(1000-500) + 500
  344. time.Sleep(time.Duration(dur) * time.Millisecond)
  345. }
  346. wg.Done()
  347. close(checkTxDone)
  348. }()
  349. wg.Add(1)
  350. go func() {
  351. ticker := time.NewTicker(time.Second)
  352. defer ticker.Stop()
  353. defer wg.Done()
  354. var height int64 = 1
  355. for range ticker.C {
  356. reapedTxs := txmp.ReapMaxTxs(200)
  357. if len(reapedTxs) > 0 {
  358. responses := make([]*abci.ResponseDeliverTx, len(reapedTxs))
  359. for i := 0; i < len(responses); i++ {
  360. var code uint32
  361. if i%10 == 0 {
  362. code = 100
  363. } else {
  364. code = abci.CodeTypeOK
  365. }
  366. responses[i] = &abci.ResponseDeliverTx{Code: code}
  367. }
  368. txmp.Lock()
  369. require.NoError(t, txmp.Update(height, reapedTxs, responses, nil, nil))
  370. txmp.Unlock()
  371. height++
  372. } else {
  373. // only return once we know we finished the CheckTx loop
  374. select {
  375. case <-checkTxDone:
  376. return
  377. default:
  378. }
  379. }
  380. }
  381. }()
  382. wg.Wait()
  383. require.Zero(t, txmp.Size())
  384. require.Zero(t, txmp.SizeBytes())
  385. }
  386. func TestTxMempool_ExpiredTxs_NumBlocks(t *testing.T) {
  387. ctx, cancel := context.WithCancel(context.Background())
  388. defer cancel()
  389. txmp := setup(ctx, t, 500)
  390. txmp.height = 100
  391. txmp.config.TTLNumBlocks = 10
  392. tTxs := checkTxs(t, txmp, 100, 0)
  393. require.Equal(t, len(tTxs), txmp.Size())
  394. require.Equal(t, 100, txmp.heightIndex.Size())
  395. // reap 5 txs at the next height -- no txs should expire
  396. reapedTxs := txmp.ReapMaxTxs(5)
  397. responses := make([]*abci.ResponseDeliverTx, len(reapedTxs))
  398. for i := 0; i < len(responses); i++ {
  399. responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
  400. }
  401. txmp.Lock()
  402. require.NoError(t, txmp.Update(txmp.height+1, reapedTxs, responses, nil, nil))
  403. txmp.Unlock()
  404. require.Equal(t, 95, txmp.Size())
  405. require.Equal(t, 95, txmp.heightIndex.Size())
  406. // check more txs at height 101
  407. _ = checkTxs(t, txmp, 50, 1)
  408. require.Equal(t, 145, txmp.Size())
  409. require.Equal(t, 145, txmp.heightIndex.Size())
  410. // Reap 5 txs at a height that would expire all the transactions from before
  411. // the previous Update (height 100).
  412. //
  413. // NOTE: When we reap txs below, we do not know if we're picking txs from the
  414. // initial CheckTx calls or from the second round of CheckTx calls. Thus, we
  415. // cannot guarantee that all 95 txs are remaining that should be expired and
  416. // removed. However, we do know that that at most 95 txs can be expired and
  417. // removed.
  418. reapedTxs = txmp.ReapMaxTxs(5)
  419. responses = make([]*abci.ResponseDeliverTx, len(reapedTxs))
  420. for i := 0; i < len(responses); i++ {
  421. responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
  422. }
  423. txmp.Lock()
  424. require.NoError(t, txmp.Update(txmp.height+10, reapedTxs, responses, nil, nil))
  425. txmp.Unlock()
  426. require.GreaterOrEqual(t, txmp.Size(), 45)
  427. require.GreaterOrEqual(t, txmp.heightIndex.Size(), 45)
  428. }
  429. func TestTxMempool_CheckTxPostCheckError(t *testing.T) {
  430. ctx, cancel := context.WithCancel(context.Background())
  431. defer cancel()
  432. cases := []struct {
  433. name string
  434. err error
  435. }{
  436. {
  437. name: "error",
  438. err: errors.New("test error"),
  439. },
  440. {
  441. name: "no error",
  442. err: nil,
  443. },
  444. }
  445. for _, tc := range cases {
  446. testCase := tc
  447. t.Run(testCase.name, func(t *testing.T) {
  448. ctx, cancel := context.WithCancel(ctx)
  449. defer cancel()
  450. postCheckFn := func(_ types.Tx, _ *abci.ResponseCheckTx) error {
  451. return testCase.err
  452. }
  453. txmp := setup(ctx, t, 0, WithPostCheck(postCheckFn))
  454. rng := rand.New(rand.NewSource(time.Now().UnixNano()))
  455. tx := make([]byte, txmp.config.MaxTxBytes-1)
  456. _, err := rng.Read(tx)
  457. require.NoError(t, err)
  458. callback := func(res *abci.Response) {
  459. checkTxRes, ok := res.Value.(*abci.Response_CheckTx)
  460. require.True(t, ok)
  461. expectedErrString := ""
  462. if testCase.err != nil {
  463. expectedErrString = testCase.err.Error()
  464. }
  465. require.Equal(t, expectedErrString, checkTxRes.CheckTx.MempoolError)
  466. }
  467. require.NoError(t, txmp.CheckTx(ctx, tx, callback, TxInfo{SenderID: 0}))
  468. })
  469. }
  470. }