|
|
- package consensus
-
- import (
- "bytes"
- "fmt"
- "path"
- "testing"
-
- "github.com/tendermint/tendermint/config/tendermint_test"
- // . "github.com/tendermint/go-common"
- "github.com/tendermint/abci/example/dummy"
- cfg "github.com/tendermint/go-config"
- "github.com/tendermint/go-crypto"
- dbm "github.com/tendermint/go-db"
- "github.com/tendermint/tendermint/proxy"
- sm "github.com/tendermint/tendermint/state"
- "github.com/tendermint/tendermint/types"
- )
-
- var (
- privKey = crypto.GenPrivKeyEd25519FromSecret([]byte("handshake_test"))
- chainID = "handshake_chain"
- nBlocks = 5
- mempool = sm.MockMempool{}
- testPartSize = 65536
- )
-
- //---------------------------------------
- // Test handshake/replay
-
- // Sync from scratch
- func _TestHandshakeReplayAll(t *testing.T) {
- testHandshakeReplay(t, 0)
- }
-
- // Sync many, not from scratch
- func _TestHandshakeReplaySome(t *testing.T) {
- testHandshakeReplay(t, 1)
- }
-
- // Sync from lagging by one
- func _TestHandshakeReplayOne(t *testing.T) {
- testHandshakeReplay(t, nBlocks-1)
- }
-
- // Sync from caught up
- func TestHandshakeReplayNone(t *testing.T) {
- testHandshakeReplay(t, nBlocks)
- }
-
- // Make some blocks. Start a fresh app and apply n blocks. Then restart the app and sync it up with the remaining blocks
- func testHandshakeReplay(t *testing.T, n int) {
- config := tendermint_test.ResetConfig("proxy_test_")
- config.Set("chain_id", chainID)
-
- state, store := stateAndStore(config)
- clientCreator := proxy.NewLocalClientCreator(dummy.NewPersistentDummyApplication(path.Join(config.GetString("db_dir"), "1")))
- clientCreator2 := proxy.NewLocalClientCreator(dummy.NewPersistentDummyApplication(path.Join(config.GetString("db_dir"), "2")))
- proxyApp := proxy.NewAppConns(config, clientCreator, sm.NewHandshaker(config, state, store, ReplayLastBlock))
- if _, err := proxyApp.Start(); err != nil {
- t.Fatalf("Error starting proxy app connections: %v", err)
- }
- chain, commits := makeBlockchain(t, proxyApp, state)
- store.chain = chain //
- store.commits = commits
- latestAppHash := state.AppHash
- proxyApp.Stop()
-
- if n > 0 {
- // start a new app without handshake, play n blocks
- proxyApp = proxy.NewAppConns(config, clientCreator2, nil)
- if _, err := proxyApp.Start(); err != nil {
- t.Fatalf("Error starting proxy app connections: %v", err)
- }
- state2, _ := stateAndStore(config)
- for i := 0; i < n; i++ {
- block := chain[i]
- err := state2.ApplyBlock(nil, proxyApp.Consensus(), block, block.MakePartSet(testPartSize).Header(), mempool)
- if err != nil {
- t.Fatal(err)
- }
- }
- proxyApp.Stop()
- }
-
- // now start it with the handshake
- handshaker := sm.NewHandshaker(config, state, store, ReplayLastBlock)
- proxyApp = proxy.NewAppConns(config, clientCreator2, handshaker)
- if _, err := proxyApp.Start(); err != nil {
- t.Fatalf("Error starting proxy app connections: %v", err)
- }
-
- // get the latest app hash from the app
- res, err := proxyApp.Query().InfoSync()
- if err != nil {
- t.Fatal(err)
- }
-
- // the app hash should be synced up
- if !bytes.Equal(latestAppHash, res.LastBlockAppHash) {
- t.Fatalf("Expected app hashes to match after handshake/replay. got %X, expected %X", res.LastBlockAppHash, latestAppHash)
- }
-
- if handshaker.NBlocks() != nBlocks-n {
- t.Fatalf("Expected handshake to sync %d blocks, got %d", nBlocks-n, handshaker.NBlocks())
- }
-
- }
-
- //--------------------------
- // utils for making blocks
-
- // make some bogus txs
- func txsFunc(blockNum int) (txs []types.Tx) {
- for i := 0; i < 10; i++ {
- txs = append(txs, types.Tx([]byte{byte(blockNum), byte(i)}))
- }
- return txs
- }
-
- // sign a commit vote
- func signCommit(height, round int, hash []byte, header types.PartSetHeader) *types.Vote {
- vote := &types.Vote{
- ValidatorIndex: 0,
- ValidatorAddress: privKey.PubKey().Address(),
- Height: height,
- Round: round,
- Type: types.VoteTypePrecommit,
- BlockID: types.BlockID{hash, header},
- }
-
- sig := privKey.Sign(types.SignBytes(chainID, vote))
- vote.Signature = sig
- return vote
- }
-
- // make a blockchain with one validator
- func makeBlockchain(t *testing.T, proxyApp proxy.AppConns, state *sm.State) (blockchain []*types.Block, commits []*types.Commit) {
-
- prevHash := state.LastBlockID.Hash
- lastCommit := new(types.Commit)
- prevParts := types.PartSetHeader{}
- valHash := state.Validators.Hash()
- prevBlockID := types.BlockID{prevHash, prevParts}
-
- for i := 1; i < nBlocks+1; i++ {
- block, parts := types.MakeBlock(i, chainID, txsFunc(i), lastCommit,
- prevBlockID, valHash, state.AppHash, testPartSize)
- fmt.Println(i)
- fmt.Println(block.LastBlockID)
- err := state.ApplyBlock(nil, proxyApp.Consensus(), block, block.MakePartSet(testPartSize).Header(), mempool)
- if err != nil {
- t.Fatal(i, err)
- }
-
- voteSet := types.NewVoteSet(chainID, i, 0, types.VoteTypePrecommit, state.Validators)
- vote := signCommit(i, 0, block.Hash(), parts.Header())
- _, err = voteSet.AddVote(vote)
- if err != nil {
- t.Fatal(err)
- }
-
- prevHash = block.Hash()
- prevParts = parts.Header()
- lastCommit = voteSet.MakeCommit()
- prevBlockID = types.BlockID{prevHash, prevParts}
-
- blockchain = append(blockchain, block)
- commits = append(commits, lastCommit)
- }
- return blockchain, commits
- }
-
- // fresh state and mock store
- func stateAndStore(config cfg.Config) (*sm.State, *mockBlockStore) {
- stateDB := dbm.NewMemDB()
- return sm.MakeGenesisState(stateDB, &types.GenesisDoc{
- ChainID: chainID,
- Validators: []types.GenesisValidator{
- types.GenesisValidator{privKey.PubKey(), 10000, "test"},
- },
- AppHash: nil,
- }), NewMockBlockStore(config)
- }
-
- //----------------------------------
- // mock block store
-
- type mockBlockStore struct {
- config cfg.Config
- chain []*types.Block
- commits []*types.Commit
- }
-
- func NewMockBlockStore(config cfg.Config) *mockBlockStore {
- return &mockBlockStore{config, nil, nil}
- }
-
- func (bs *mockBlockStore) Height() int { return len(bs.chain) }
- func (bs *mockBlockStore) LoadBlock(height int) *types.Block { return bs.chain[height-1] }
- func (bs *mockBlockStore) LoadBlockMeta(height int) *types.BlockMeta {
- block := bs.chain[height-1]
- return &types.BlockMeta{
- BlockID: types.BlockID{block.Hash(), block.MakePartSet(bs.config.GetInt("block_part_size")).Header()},
- Header: block.Header,
- }
- }
- func (bs *mockBlockStore) LoadBlockPart(height int, index int) *types.Part { return nil }
- func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
- }
- func (bs *mockBlockStore) LoadBlockCommit(height int) *types.Commit {
- return bs.commits[height-1]
- }
- func (bs *mockBlockStore) LoadSeenCommit(height int) *types.Commit {
- return bs.commits[height-1]
- }
|