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.

273 lines
7.0 KiB

9 years ago
9 years ago
9 years ago
9 years ago
  1. package mempool
  2. import (
  3. "fmt"
  4. "sync"
  5. "testing"
  6. "time"
  7. acm "github.com/tendermint/tendermint/account"
  8. _ "github.com/tendermint/tendermint/config/tendermint_test"
  9. sm "github.com/tendermint/tendermint/state"
  10. "github.com/tendermint/tendermint/types"
  11. )
  12. var someAddr = []byte("ABCDEFGHIJABCDEFGHIJ")
  13. // number of txs
  14. var nTxs = 100
  15. // what the ResetInfo should look like after ResetForBlockAndState
  16. var TestResetInfoData = ResetInfo{
  17. Included: []Range{
  18. Range{0, 5},
  19. Range{10, 10},
  20. Range{30, 5},
  21. },
  22. Invalid: []Range{
  23. Range{5, 5},
  24. Range{20, 8}, // let 28 and 29 be valid
  25. Range{35, 64}, // let 99 be valid
  26. },
  27. }
  28. // inverse of the ResetInfo
  29. var notInvalidNotIncluded = map[int]struct{}{
  30. 28: struct{}{},
  31. 29: struct{}{},
  32. 99: struct{}{},
  33. }
  34. func newSendTx(t *testing.T, mempool *Mempool, from *acm.PrivAccount, to []byte, amt int64) types.Tx {
  35. tx := types.NewSendTx()
  36. tx.AddInput(mempool.GetCache(), from.PubKey, amt)
  37. tx.AddOutput(to, amt)
  38. tx.SignInput(config.GetString("chain_id"), 0, from)
  39. if err := mempool.AddTx(tx); err != nil {
  40. t.Fatal(err)
  41. }
  42. return tx
  43. }
  44. func addTxs(t *testing.T, mempool *Mempool, lastAcc *acm.PrivAccount, privAccs []*acm.PrivAccount) []types.Tx {
  45. txs := make([]types.Tx, nTxs)
  46. for i := 0; i < nTxs; i++ {
  47. if _, ok := notInvalidNotIncluded[i]; ok {
  48. txs[i] = newSendTx(t, mempool, lastAcc, someAddr, 10)
  49. } else {
  50. txs[i] = newSendTx(t, mempool, privAccs[i%len(privAccs)], privAccs[(i+1)%len(privAccs)].Address, 5)
  51. }
  52. }
  53. return txs
  54. }
  55. func makeBlock(mempool *Mempool) *types.Block {
  56. txs := mempool.GetProposalTxs()
  57. var includedTxs []types.Tx
  58. for _, rid := range TestResetInfoData.Included {
  59. includedTxs = append(includedTxs, txs[rid.Start:rid.Start+rid.Length]...)
  60. }
  61. mempool.mtx.Lock()
  62. state := mempool.state
  63. state.LastBlockHeight += 1
  64. mempool.mtx.Unlock()
  65. return &types.Block{
  66. Header: &types.Header{
  67. ChainID: state.ChainID,
  68. Height: state.LastBlockHeight,
  69. NumTxs: len(includedTxs),
  70. },
  71. Data: &types.Data{
  72. Txs: includedTxs,
  73. },
  74. }
  75. }
  76. // Add txs. Grab chunks to put in block. All the others become invalid because of nonce errors except those in notInvalidNotIncluded
  77. func TestResetInfo(t *testing.T) {
  78. amtPerAccount := int64(100000)
  79. state, privAccs, _ := sm.RandGenesisState(6, false, amtPerAccount, 1, true, 100)
  80. mempool := NewMempool(state)
  81. lastAcc := privAccs[5] // we save him (his tx wont become invalid)
  82. privAccs = privAccs[:5]
  83. txs := addTxs(t, mempool, lastAcc, privAccs)
  84. // its actually an invalid block since we're skipping nonces
  85. // but all we care about is how the mempool responds after
  86. block := makeBlock(mempool)
  87. ri := mempool.ResetForBlockAndState(block, state)
  88. if len(ri.Included) != len(TestResetInfoData.Included) {
  89. t.Fatalf("invalid number of included ranges. Got %d, expected %d\n", len(ri.Included), len(TestResetInfoData.Included))
  90. }
  91. if len(ri.Invalid) != len(TestResetInfoData.Invalid) {
  92. t.Fatalf("invalid number of invalid ranges. Got %d, expected %d\n", len(ri.Invalid), len(TestResetInfoData.Invalid))
  93. }
  94. for i, rid := range ri.Included {
  95. inc := TestResetInfoData.Included[i]
  96. if rid.Start != inc.Start {
  97. t.Fatalf("Invalid start of range. Got %d, expected %d\n", inc.Start, rid.Start)
  98. }
  99. if rid.Length != inc.Length {
  100. t.Fatalf("Invalid length of range. Got %d, expected %d\n", inc.Length, rid.Length)
  101. }
  102. }
  103. txs = mempool.GetProposalTxs()
  104. if len(txs) != len(notInvalidNotIncluded) {
  105. t.Fatalf("Expected %d txs left in mempool. Got %d", len(notInvalidNotIncluded), len(txs))
  106. }
  107. }
  108. //------------------------------------------------------------------------------------------
  109. type TestPeer struct {
  110. sync.Mutex
  111. running bool
  112. height int
  113. t *testing.T
  114. received int
  115. txs map[string]int
  116. timeoutFail int
  117. done chan int
  118. }
  119. func newPeer(t *testing.T, state *sm.State) *TestPeer {
  120. return &TestPeer{
  121. running: true,
  122. height: state.LastBlockHeight,
  123. t: t,
  124. txs: make(map[string]int),
  125. done: make(chan int),
  126. }
  127. }
  128. func (tp *TestPeer) IsRunning() bool {
  129. tp.Lock()
  130. defer tp.Unlock()
  131. return tp.running
  132. }
  133. func (tp *TestPeer) SetRunning(running bool) {
  134. tp.Lock()
  135. defer tp.Unlock()
  136. tp.running = running
  137. }
  138. func (tp *TestPeer) Send(chID byte, msg interface{}) bool {
  139. if tp.timeoutFail > 0 {
  140. time.Sleep(time.Second * time.Duration(tp.timeoutFail))
  141. return false
  142. }
  143. tx := msg.(*TxMessage).Tx
  144. id := types.TxID(config.GetString("chain_id"), tx)
  145. if _, ok := tp.txs[string(id)]; ok {
  146. tp.t.Fatal("received the same tx twice!")
  147. }
  148. tp.txs[string(id)] = tp.received
  149. tp.received += 1
  150. tp.done <- tp.received
  151. return true
  152. }
  153. func (tp *TestPeer) Get(key string) interface{} {
  154. return tp
  155. }
  156. func (tp *TestPeer) GetHeight() int {
  157. return tp.height
  158. }
  159. func TestBroadcast(t *testing.T) {
  160. state, privAccs, _ := sm.RandGenesisState(6, false, 10000, 1, true, 100)
  161. mempool := NewMempool(state)
  162. reactor := NewMempoolReactor(mempool)
  163. reactor.Start()
  164. lastAcc := privAccs[5] // we save him (his tx wont become invalid)
  165. privAccs = privAccs[:5]
  166. peer := newPeer(t, state)
  167. newBlockChan := make(chan ResetInfo)
  168. tickerChan := make(chan time.Time)
  169. go reactor.broadcastTxRoutine(tickerChan, newBlockChan, peer)
  170. // we don't broadcast any before updating
  171. fmt.Println("dont broadcast any")
  172. addTxs(t, mempool, lastAcc, privAccs)
  173. block := makeBlock(mempool)
  174. ri := mempool.ResetForBlockAndState(block, state)
  175. newBlockChan <- ri
  176. peer.height = ri.Height
  177. tickerChan <- time.Now()
  178. pullTxs(t, peer, len(mempool.txs)) // should have sent whatever txs are left (3)
  179. toBroadcast := []int{1, 3, 7, 9, 11, 12, 18, 20, 21, 28, 29, 30, 31, 34, 35, 36, 50, 90, 99, 100}
  180. for _, N := range toBroadcast {
  181. peer = resetPeer(t, reactor, mempool, state, tickerChan, newBlockChan, peer)
  182. // we broadcast N txs before updating
  183. fmt.Println("broadcast", N)
  184. addTxs(t, mempool, lastAcc, privAccs)
  185. txsToSendPerCheck = N
  186. tickerChan <- time.Now()
  187. pullTxs(t, peer, txsToSendPerCheck) // should have sent N txs
  188. block = makeBlock(mempool)
  189. ri := mempool.ResetForBlockAndState(block, state)
  190. newBlockChan <- ri
  191. peer.height = ri.Height
  192. txsToSendPerCheck = 100
  193. tickerChan <- time.Now()
  194. left := len(mempool.txs)
  195. if N > 99 {
  196. left -= 3
  197. } else if N > 29 {
  198. left -= 2
  199. } else if N > 28 {
  200. left -= 1
  201. }
  202. pullTxs(t, peer, left) // should have sent whatever txs are left that havent been sent
  203. }
  204. }
  205. func pullTxs(t *testing.T, peer *TestPeer, N int) {
  206. timer := time.NewTicker(time.Second * 2)
  207. for i := 0; i < N; i++ {
  208. select {
  209. case <-peer.done:
  210. case <-timer.C:
  211. panic(fmt.Sprintf("invalid number of received messages. Got %d, expected %d\n", i, N))
  212. }
  213. }
  214. if N == 0 {
  215. select {
  216. case <-peer.done:
  217. t.Fatalf("should not have sent any more txs")
  218. case <-timer.C:
  219. }
  220. }
  221. }
  222. func resetPeer(t *testing.T, reactor *MempoolReactor, mempool *Mempool, state *sm.State, tickerChan chan time.Time, newBlockChan chan ResetInfo, peer *TestPeer) *TestPeer {
  223. // reset peer
  224. mempool.txs = []types.Tx{}
  225. mempool.state = state
  226. mempool.cache = sm.NewBlockCache(state)
  227. peer.SetRunning(false)
  228. tickerChan <- time.Now()
  229. peer = newPeer(t, state)
  230. go reactor.broadcastTxRoutine(tickerChan, newBlockChan, peer)
  231. return peer
  232. }