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.

210 lines
6.0 KiB

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