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.

204 lines
5.7 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. package consensus
  2. import (
  3. "encoding/binary"
  4. "testing"
  5. "time"
  6. abci "github.com/tendermint/abci/types"
  7. "github.com/tendermint/tendermint/types"
  8. . "github.com/tendermint/tmlibs/common"
  9. )
  10. func init() {
  11. config = ResetConfig("consensus_mempool_test")
  12. }
  13. func TestNoProgressUntilTxsAvailable(t *testing.T) {
  14. config := ResetConfig("consensus_mempool_txs_available_test")
  15. config.Consensus.CreateEmptyBlocks = false
  16. state, privVals := randGenesisState(1, false, 10)
  17. cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
  18. cs.mempool.EnableTxsAvailable()
  19. height, round := cs.Height, cs.Round
  20. newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
  21. startTestRound(cs, height, round)
  22. ensureNewStep(newBlockCh) // first block gets committed
  23. ensureNoNewStep(newBlockCh)
  24. deliverTxsRange(cs, 0, 2)
  25. ensureNewStep(newBlockCh) // commit txs
  26. ensureNewStep(newBlockCh) // commit updated app hash
  27. ensureNoNewStep(newBlockCh)
  28. }
  29. func TestProgressInHigherRound(t *testing.T) {
  30. config := ResetConfig("consensus_mempool_txs_available_test")
  31. config.Consensus.CreateEmptyBlocks = false
  32. state, privVals := randGenesisState(1, false, 10)
  33. cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
  34. cs.mempool.EnableTxsAvailable()
  35. height, round := cs.Height, cs.Round
  36. newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
  37. newRoundCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewRound(), 1)
  38. timeoutCh := subscribeToEvent(cs.evsw, "tester", types.EventStringTimeoutPropose(), 1)
  39. cs.setProposal = func(proposal *types.Proposal) error {
  40. if cs.Height == 2 && cs.Round == 0 {
  41. // dont set the proposal in round 0 so we timeout and
  42. // go to next round
  43. cs.Logger.Info("Ignoring set proposal at height 2, round 0")
  44. return nil
  45. }
  46. return cs.defaultSetProposal(proposal)
  47. }
  48. startTestRound(cs, height, round)
  49. ensureNewStep(newRoundCh) // first round at first height
  50. ensureNewStep(newBlockCh) // first block gets committed
  51. ensureNewStep(newRoundCh) // first round at next height
  52. deliverTxsRange(cs, 0, 2) // we deliver txs, but dont set a proposal so we get the next round
  53. <-timeoutCh
  54. ensureNewStep(newRoundCh) // wait for the next round
  55. ensureNewStep(newBlockCh) // now we can commit the block
  56. }
  57. func deliverTxsRange(cs *ConsensusState, start, end int) {
  58. // Deliver some txs.
  59. for i := start; i < end; i++ {
  60. txBytes := make([]byte, 8)
  61. binary.BigEndian.PutUint64(txBytes, uint64(i))
  62. err := cs.mempool.CheckTx(txBytes, nil)
  63. if err != nil {
  64. panic(Fmt("Error after CheckTx: %v", err))
  65. }
  66. }
  67. }
  68. func TestTxConcurrentWithCommit(t *testing.T) {
  69. state, privVals := randGenesisState(1, false, 10)
  70. cs := newConsensusState(state, privVals[0], NewCounterApplication())
  71. height, round := cs.Height, cs.Round
  72. newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
  73. NTxs := 10000
  74. go deliverTxsRange(cs, 0, NTxs)
  75. startTestRound(cs, height, round)
  76. ticker := time.NewTicker(time.Second * 20)
  77. for nTxs := 0; nTxs < NTxs; {
  78. select {
  79. case b := <-newBlockCh:
  80. nTxs += b.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block.Header.NumTxs
  81. case <-ticker.C:
  82. panic("Timed out waiting to commit blocks with transactions")
  83. }
  84. }
  85. }
  86. func TestRmBadTx(t *testing.T) {
  87. state, privVals := randGenesisState(1, false, 10)
  88. app := NewCounterApplication()
  89. cs := newConsensusState(state, privVals[0], app)
  90. // increment the counter by 1
  91. txBytes := make([]byte, 8)
  92. binary.BigEndian.PutUint64(txBytes, uint64(0))
  93. app.DeliverTx(txBytes)
  94. app.Commit()
  95. ch := make(chan struct{})
  96. cbCh := make(chan struct{})
  97. go func() {
  98. // Try to send the tx through the mempool.
  99. // CheckTx should not err, but the app should return a bad abci code
  100. // and the tx should get removed from the pool
  101. err := cs.mempool.CheckTx(txBytes, func(r *abci.Response) {
  102. if r.GetCheckTx().Code != abci.CodeType_BadNonce {
  103. t.Fatalf("expected checktx to return bad nonce, got %v", r)
  104. }
  105. cbCh <- struct{}{}
  106. })
  107. if err != nil {
  108. t.Fatal("Error after CheckTx: %v", err)
  109. }
  110. // check for the tx
  111. for {
  112. time.Sleep(time.Second)
  113. txs := cs.mempool.Reap(1)
  114. if len(txs) == 0 {
  115. ch <- struct{}{}
  116. return
  117. }
  118. }
  119. }()
  120. // Wait until the tx returns
  121. ticker := time.After(time.Second * 5)
  122. select {
  123. case <-cbCh:
  124. // success
  125. case <-ticker:
  126. t.Fatalf("Timed out waiting for tx to return")
  127. }
  128. // Wait until the tx is removed
  129. ticker = time.After(time.Second * 5)
  130. select {
  131. case <-ch:
  132. // success
  133. case <-ticker:
  134. t.Fatalf("Timed out waiting for tx to be removed")
  135. }
  136. }
  137. // CounterApplication that maintains a mempool state and resets it upon commit
  138. type CounterApplication struct {
  139. abci.BaseApplication
  140. txCount int
  141. mempoolTxCount int
  142. }
  143. func NewCounterApplication() *CounterApplication {
  144. return &CounterApplication{}
  145. }
  146. func (app *CounterApplication) Info() abci.ResponseInfo {
  147. return abci.ResponseInfo{Data: Fmt("txs:%v", app.txCount)}
  148. }
  149. func (app *CounterApplication) DeliverTx(tx []byte) abci.Result {
  150. return runTx(tx, &app.txCount)
  151. }
  152. func (app *CounterApplication) CheckTx(tx []byte) abci.Result {
  153. return runTx(tx, &app.mempoolTxCount)
  154. }
  155. func runTx(tx []byte, countPtr *int) abci.Result {
  156. count := *countPtr
  157. tx8 := make([]byte, 8)
  158. copy(tx8[len(tx8)-len(tx):], tx)
  159. txValue := binary.BigEndian.Uint64(tx8)
  160. if txValue != uint64(count) {
  161. return abci.ErrBadNonce.AppendLog(Fmt("Invalid nonce. Expected %v, got %v", count, txValue))
  162. }
  163. *countPtr += 1
  164. return abci.OK
  165. }
  166. func (app *CounterApplication) Commit() abci.Result {
  167. app.mempoolTxCount = app.txCount
  168. if app.txCount == 0 {
  169. return abci.OK
  170. } else {
  171. hash := make([]byte, 8)
  172. binary.BigEndian.PutUint64(hash, uint64(app.txCount))
  173. return abci.NewResultOK(hash, "")
  174. }
  175. }