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.

216 lines
6.5 KiB

  1. package consensus
  2. import (
  3. "bytes"
  4. "fmt"
  5. "path"
  6. "testing"
  7. "github.com/tendermint/tendermint/config/tendermint_test"
  8. // . "github.com/tendermint/go-common"
  9. "github.com/tendermint/abci/example/dummy"
  10. cfg "github.com/tendermint/go-config"
  11. "github.com/tendermint/go-crypto"
  12. dbm "github.com/tendermint/go-db"
  13. "github.com/tendermint/tendermint/proxy"
  14. sm "github.com/tendermint/tendermint/state"
  15. "github.com/tendermint/tendermint/types"
  16. )
  17. var (
  18. privKey = crypto.GenPrivKeyEd25519FromSecret([]byte("handshake_test"))
  19. chainID = "handshake_chain"
  20. nBlocks = 5
  21. mempool = sm.MockMempool{}
  22. testPartSize = 65536
  23. )
  24. //---------------------------------------
  25. // Test handshake/replay
  26. // Sync from scratch
  27. func _TestHandshakeReplayAll(t *testing.T) {
  28. testHandshakeReplay(t, 0)
  29. }
  30. // Sync many, not from scratch
  31. func _TestHandshakeReplaySome(t *testing.T) {
  32. testHandshakeReplay(t, 1)
  33. }
  34. // Sync from lagging by one
  35. func _TestHandshakeReplayOne(t *testing.T) {
  36. testHandshakeReplay(t, nBlocks-1)
  37. }
  38. // Sync from caught up
  39. func TestHandshakeReplayNone(t *testing.T) {
  40. testHandshakeReplay(t, nBlocks)
  41. }
  42. // Make some blocks. Start a fresh app and apply n blocks. Then restart the app and sync it up with the remaining blocks
  43. func testHandshakeReplay(t *testing.T, n int) {
  44. config := tendermint_test.ResetConfig("proxy_test_")
  45. config.Set("chain_id", chainID)
  46. state, store := stateAndStore(config)
  47. clientCreator := proxy.NewLocalClientCreator(dummy.NewPersistentDummyApplication(path.Join(config.GetString("db_dir"), "1")))
  48. clientCreator2 := proxy.NewLocalClientCreator(dummy.NewPersistentDummyApplication(path.Join(config.GetString("db_dir"), "2")))
  49. proxyApp := proxy.NewAppConns(config, clientCreator, sm.NewHandshaker(config, state, store, ReplayLastBlock))
  50. if _, err := proxyApp.Start(); err != nil {
  51. t.Fatalf("Error starting proxy app connections: %v", err)
  52. }
  53. chain, commits := makeBlockchain(t, proxyApp, state)
  54. store.chain = chain //
  55. store.commits = commits
  56. latestAppHash := state.AppHash
  57. proxyApp.Stop()
  58. if n > 0 {
  59. // start a new app without handshake, play n blocks
  60. proxyApp = proxy.NewAppConns(config, clientCreator2, nil)
  61. if _, err := proxyApp.Start(); err != nil {
  62. t.Fatalf("Error starting proxy app connections: %v", err)
  63. }
  64. state2, _ := stateAndStore(config)
  65. for i := 0; i < n; i++ {
  66. block := chain[i]
  67. err := state2.ApplyBlock(nil, proxyApp.Consensus(), block, block.MakePartSet(testPartSize).Header(), mempool)
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. }
  72. proxyApp.Stop()
  73. }
  74. // now start it with the handshake
  75. handshaker := sm.NewHandshaker(config, state, store, ReplayLastBlock)
  76. proxyApp = proxy.NewAppConns(config, clientCreator2, handshaker)
  77. if _, err := proxyApp.Start(); err != nil {
  78. t.Fatalf("Error starting proxy app connections: %v", err)
  79. }
  80. // get the latest app hash from the app
  81. res, err := proxyApp.Query().InfoSync()
  82. if err != nil {
  83. t.Fatal(err)
  84. }
  85. // the app hash should be synced up
  86. if !bytes.Equal(latestAppHash, res.LastBlockAppHash) {
  87. t.Fatalf("Expected app hashes to match after handshake/replay. got %X, expected %X", res.LastBlockAppHash, latestAppHash)
  88. }
  89. if handshaker.NBlocks() != nBlocks-n {
  90. t.Fatalf("Expected handshake to sync %d blocks, got %d", nBlocks-n, handshaker.NBlocks())
  91. }
  92. }
  93. //--------------------------
  94. // utils for making blocks
  95. // make some bogus txs
  96. func txsFunc(blockNum int) (txs []types.Tx) {
  97. for i := 0; i < 10; i++ {
  98. txs = append(txs, types.Tx([]byte{byte(blockNum), byte(i)}))
  99. }
  100. return txs
  101. }
  102. // sign a commit vote
  103. func signCommit(height, round int, hash []byte, header types.PartSetHeader) *types.Vote {
  104. vote := &types.Vote{
  105. ValidatorIndex: 0,
  106. ValidatorAddress: privKey.PubKey().Address(),
  107. Height: height,
  108. Round: round,
  109. Type: types.VoteTypePrecommit,
  110. BlockID: types.BlockID{hash, header},
  111. }
  112. sig := privKey.Sign(types.SignBytes(chainID, vote))
  113. vote.Signature = sig
  114. return vote
  115. }
  116. // make a blockchain with one validator
  117. func makeBlockchain(t *testing.T, proxyApp proxy.AppConns, state *sm.State) (blockchain []*types.Block, commits []*types.Commit) {
  118. prevHash := state.LastBlockID.Hash
  119. lastCommit := new(types.Commit)
  120. prevParts := types.PartSetHeader{}
  121. valHash := state.Validators.Hash()
  122. prevBlockID := types.BlockID{prevHash, prevParts}
  123. for i := 1; i < nBlocks+1; i++ {
  124. block, parts := types.MakeBlock(i, chainID, txsFunc(i), lastCommit,
  125. prevBlockID, valHash, state.AppHash, testPartSize)
  126. fmt.Println(i)
  127. fmt.Println(block.LastBlockID)
  128. err := state.ApplyBlock(nil, proxyApp.Consensus(), block, block.MakePartSet(testPartSize).Header(), mempool)
  129. if err != nil {
  130. t.Fatal(i, err)
  131. }
  132. voteSet := types.NewVoteSet(chainID, i, 0, types.VoteTypePrecommit, state.Validators)
  133. vote := signCommit(i, 0, block.Hash(), parts.Header())
  134. _, err = voteSet.AddVote(vote)
  135. if err != nil {
  136. t.Fatal(err)
  137. }
  138. prevHash = block.Hash()
  139. prevParts = parts.Header()
  140. lastCommit = voteSet.MakeCommit()
  141. prevBlockID = types.BlockID{prevHash, prevParts}
  142. blockchain = append(blockchain, block)
  143. commits = append(commits, lastCommit)
  144. }
  145. return blockchain, commits
  146. }
  147. // fresh state and mock store
  148. func stateAndStore(config cfg.Config) (*sm.State, *mockBlockStore) {
  149. stateDB := dbm.NewMemDB()
  150. return sm.MakeGenesisState(stateDB, &types.GenesisDoc{
  151. ChainID: chainID,
  152. Validators: []types.GenesisValidator{
  153. types.GenesisValidator{privKey.PubKey(), 10000, "test"},
  154. },
  155. AppHash: nil,
  156. }), NewMockBlockStore(config)
  157. }
  158. //----------------------------------
  159. // mock block store
  160. type mockBlockStore struct {
  161. config cfg.Config
  162. chain []*types.Block
  163. commits []*types.Commit
  164. }
  165. func NewMockBlockStore(config cfg.Config) *mockBlockStore {
  166. return &mockBlockStore{config, nil, nil}
  167. }
  168. func (bs *mockBlockStore) Height() int { return len(bs.chain) }
  169. func (bs *mockBlockStore) LoadBlock(height int) *types.Block { return bs.chain[height-1] }
  170. func (bs *mockBlockStore) LoadBlockMeta(height int) *types.BlockMeta {
  171. block := bs.chain[height-1]
  172. return &types.BlockMeta{
  173. BlockID: types.BlockID{block.Hash(), block.MakePartSet(bs.config.GetInt("block_part_size")).Header()},
  174. Header: block.Header,
  175. }
  176. }
  177. func (bs *mockBlockStore) LoadBlockPart(height int, index int) *types.Part { return nil }
  178. func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
  179. }
  180. func (bs *mockBlockStore) LoadBlockCommit(height int) *types.Commit {
  181. return bs.commits[height-1]
  182. }
  183. func (bs *mockBlockStore) LoadSeenCommit(height int) *types.Commit {
  184. return bs.commits[height-1]
  185. }