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.

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