github.com/vipernet-xyz/tm@v0.34.24/consensus/replay_test.go (about)

     1  package consensus
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  	"sort"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/gogo/protobuf/proto"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  	dbm "github.com/tendermint/tm-db"
    19  
    20  	"github.com/vipernet-xyz/tm/abci/example/kvstore"
    21  	abci "github.com/vipernet-xyz/tm/abci/types"
    22  	cfg "github.com/vipernet-xyz/tm/config"
    23  	"github.com/vipernet-xyz/tm/crypto"
    24  	cryptoenc "github.com/vipernet-xyz/tm/crypto/encoding"
    25  	"github.com/vipernet-xyz/tm/libs/log"
    26  	tmrand "github.com/vipernet-xyz/tm/libs/rand"
    27  	mempl "github.com/vipernet-xyz/tm/mempool"
    28  	"github.com/vipernet-xyz/tm/privval"
    29  	tmstate "github.com/vipernet-xyz/tm/proto/tendermint/state"
    30  	tmproto "github.com/vipernet-xyz/tm/proto/tendermint/types"
    31  	"github.com/vipernet-xyz/tm/proxy"
    32  	sm "github.com/vipernet-xyz/tm/state"
    33  	"github.com/vipernet-xyz/tm/types"
    34  )
    35  
    36  func TestMain(m *testing.M) {
    37  	config = ResetConfig("consensus_reactor_test")
    38  	consensusReplayConfig = ResetConfig("consensus_replay_test")
    39  	configStateTest := ResetConfig("consensus_state_test")
    40  	configMempoolTest := ResetConfig("consensus_mempool_test")
    41  	configByzantineTest := ResetConfig("consensus_byzantine_test")
    42  	code := m.Run()
    43  	os.RemoveAll(config.RootDir)
    44  	os.RemoveAll(consensusReplayConfig.RootDir)
    45  	os.RemoveAll(configStateTest.RootDir)
    46  	os.RemoveAll(configMempoolTest.RootDir)
    47  	os.RemoveAll(configByzantineTest.RootDir)
    48  	os.Exit(code)
    49  }
    50  
    51  // These tests ensure we can always recover from failure at any part of the consensus process.
    52  // There are two general failure scenarios: failure during consensus, and failure while applying the block.
    53  // Only the latter interacts with the app and store,
    54  // but the former has to deal with restrictions on re-use of priv_validator keys.
    55  // The `WAL Tests` are for failures during the consensus;
    56  // the `Handshake Tests` are for failures in applying the block.
    57  // With the help of the WAL, we can recover from it all!
    58  
    59  //------------------------------------------------------------------------------------------
    60  // WAL Tests
    61  
    62  // TODO: It would be better to verify explicitly which states we can recover from without the wal
    63  // and which ones we need the wal for - then we'd also be able to only flush the
    64  // wal writer when we need to, instead of with every message.
    65  
    66  func startNewStateAndWaitForBlock(t *testing.T, consensusReplayConfig *cfg.Config,
    67  	lastBlockHeight int64, blockDB dbm.DB, stateStore sm.Store,
    68  ) {
    69  	logger := log.TestingLogger()
    70  	state, _ := stateStore.LoadFromDBOrGenesisFile(consensusReplayConfig.GenesisFile())
    71  	privValidator := loadPrivValidator(consensusReplayConfig)
    72  	cs := newStateWithConfigAndBlockStore(
    73  		consensusReplayConfig,
    74  		state,
    75  		privValidator,
    76  		kvstore.NewApplication(),
    77  		blockDB,
    78  	)
    79  	cs.SetLogger(logger)
    80  
    81  	bytes, _ := os.ReadFile(cs.config.WalFile())
    82  	t.Logf("====== WAL: \n\r%X\n", bytes)
    83  
    84  	err := cs.Start()
    85  	require.NoError(t, err)
    86  	defer func() {
    87  		if err := cs.Stop(); err != nil {
    88  			t.Error(err)
    89  		}
    90  	}()
    91  
    92  	// This is just a signal that we haven't halted; its not something contained
    93  	// in the WAL itself. Assuming the consensus state is running, replay of any
    94  	// WAL, including the empty one, should eventually be followed by a new
    95  	// block, or else something is wrong.
    96  	newBlockSub, err := cs.eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock)
    97  	require.NoError(t, err)
    98  	select {
    99  	case <-newBlockSub.Out():
   100  	case <-newBlockSub.Cancelled():
   101  		t.Fatal("newBlockSub was cancelled")
   102  	case <-time.After(120 * time.Second):
   103  		t.Fatal("Timed out waiting for new block (see trace above)")
   104  	}
   105  }
   106  
   107  func sendTxs(ctx context.Context, cs *State) {
   108  	for i := 0; i < 256; i++ {
   109  		select {
   110  		case <-ctx.Done():
   111  			return
   112  		default:
   113  			tx := []byte{byte(i)}
   114  			if err := assertMempool(cs.txNotifier).CheckTx(tx, nil, mempl.TxInfo{}); err != nil {
   115  				panic(err)
   116  			}
   117  			i++
   118  		}
   119  	}
   120  }
   121  
   122  // TestWALCrash uses crashing WAL to test we can recover from any WAL failure.
   123  func TestWALCrash(t *testing.T) {
   124  	testCases := []struct {
   125  		name         string
   126  		initFn       func(dbm.DB, *State, context.Context)
   127  		heightToStop int64
   128  	}{
   129  		{
   130  			"empty block",
   131  			func(stateDB dbm.DB, cs *State, ctx context.Context) {},
   132  			1,
   133  		},
   134  		{
   135  			"many non-empty blocks",
   136  			func(stateDB dbm.DB, cs *State, ctx context.Context) {
   137  				go sendTxs(ctx, cs)
   138  			},
   139  			3,
   140  		},
   141  	}
   142  
   143  	for i, tc := range testCases {
   144  		tc := tc
   145  		consensusReplayConfig := ResetConfig(fmt.Sprintf("%s_%d", t.Name(), i))
   146  		t.Run(tc.name, func(t *testing.T) {
   147  			crashWALandCheckLiveness(t, consensusReplayConfig, tc.initFn, tc.heightToStop)
   148  		})
   149  	}
   150  }
   151  
   152  func crashWALandCheckLiveness(t *testing.T, consensusReplayConfig *cfg.Config,
   153  	initFn func(dbm.DB, *State, context.Context), heightToStop int64,
   154  ) {
   155  	walPanicked := make(chan error)
   156  	crashingWal := &crashingWAL{panicCh: walPanicked, heightToStop: heightToStop}
   157  
   158  	i := 1
   159  LOOP:
   160  	for {
   161  		t.Logf("====== LOOP %d\n", i)
   162  
   163  		// create consensus state from a clean slate
   164  		logger := log.NewNopLogger()
   165  		blockDB := dbm.NewMemDB()
   166  		stateDB := blockDB
   167  		stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   168  			DiscardABCIResponses: false,
   169  		})
   170  		state, err := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile())
   171  		require.NoError(t, err)
   172  		privValidator := loadPrivValidator(consensusReplayConfig)
   173  		cs := newStateWithConfigAndBlockStore(
   174  			consensusReplayConfig,
   175  			state,
   176  			privValidator,
   177  			kvstore.NewApplication(),
   178  			blockDB,
   179  		)
   180  		cs.SetLogger(logger)
   181  
   182  		// start sending transactions
   183  		ctx, cancel := context.WithCancel(context.Background())
   184  		initFn(stateDB, cs, ctx)
   185  
   186  		// clean up WAL file from the previous iteration
   187  		walFile := cs.config.WalFile()
   188  		os.Remove(walFile)
   189  
   190  		// set crashing WAL
   191  		csWal, err := cs.OpenWAL(walFile)
   192  		require.NoError(t, err)
   193  		crashingWal.next = csWal
   194  
   195  		// reset the message counter
   196  		crashingWal.msgIndex = 1
   197  		cs.wal = crashingWal
   198  
   199  		// start consensus state
   200  		err = cs.Start()
   201  		require.NoError(t, err)
   202  
   203  		i++
   204  
   205  		select {
   206  		case err := <-walPanicked:
   207  			t.Logf("WAL panicked: %v", err)
   208  
   209  			// make sure we can make blocks after a crash
   210  			startNewStateAndWaitForBlock(t, consensusReplayConfig, cs.Height, blockDB, stateStore)
   211  
   212  			// stop consensus state and transactions sender (initFn)
   213  			cs.Stop() //nolint:errcheck // Logging this error causes failure
   214  			cancel()
   215  
   216  			// if we reached the required height, exit
   217  			if _, ok := err.(ReachedHeightToStopError); ok {
   218  				break LOOP
   219  			}
   220  		case <-time.After(10 * time.Second):
   221  			t.Fatal("WAL did not panic for 10 seconds (check the log)")
   222  		}
   223  	}
   224  }
   225  
   226  // crashingWAL is a WAL which crashes or rather simulates a crash during Save
   227  // (before and after). It remembers a message for which we last panicked
   228  // (lastPanickedForMsgIndex), so we don't panic for it in subsequent iterations.
   229  type crashingWAL struct {
   230  	next         WAL
   231  	panicCh      chan error
   232  	heightToStop int64
   233  
   234  	msgIndex                int // current message index
   235  	lastPanickedForMsgIndex int // last message for which we panicked
   236  }
   237  
   238  var _ WAL = &crashingWAL{}
   239  
   240  // WALWriteError indicates a WAL crash.
   241  type WALWriteError struct {
   242  	msg string
   243  }
   244  
   245  func (e WALWriteError) Error() string {
   246  	return e.msg
   247  }
   248  
   249  // ReachedHeightToStopError indicates we've reached the required consensus
   250  // height and may exit.
   251  type ReachedHeightToStopError struct {
   252  	height int64
   253  }
   254  
   255  func (e ReachedHeightToStopError) Error() string {
   256  	return fmt.Sprintf("reached height to stop %d", e.height)
   257  }
   258  
   259  // Write simulate WAL's crashing by sending an error to the panicCh and then
   260  // exiting the cs.receiveRoutine.
   261  func (w *crashingWAL) Write(m WALMessage) error {
   262  	if endMsg, ok := m.(EndHeightMessage); ok {
   263  		if endMsg.Height == w.heightToStop {
   264  			w.panicCh <- ReachedHeightToStopError{endMsg.Height}
   265  			runtime.Goexit()
   266  			return nil
   267  		}
   268  
   269  		return w.next.Write(m)
   270  	}
   271  
   272  	if w.msgIndex > w.lastPanickedForMsgIndex {
   273  		w.lastPanickedForMsgIndex = w.msgIndex
   274  		_, file, line, _ := runtime.Caller(1)
   275  		w.panicCh <- WALWriteError{fmt.Sprintf("failed to write %T to WAL (fileline: %s:%d)", m, file, line)}
   276  		runtime.Goexit()
   277  		return nil
   278  	}
   279  
   280  	w.msgIndex++
   281  	return w.next.Write(m)
   282  }
   283  
   284  func (w *crashingWAL) WriteSync(m WALMessage) error {
   285  	return w.Write(m)
   286  }
   287  
   288  func (w *crashingWAL) FlushAndSync() error { return w.next.FlushAndSync() }
   289  
   290  func (w *crashingWAL) SearchForEndHeight(
   291  	height int64,
   292  	options *WALSearchOptions,
   293  ) (rd io.ReadCloser, found bool, err error) {
   294  	return w.next.SearchForEndHeight(height, options)
   295  }
   296  
   297  func (w *crashingWAL) Start() error { return w.next.Start() }
   298  func (w *crashingWAL) Stop() error  { return w.next.Stop() }
   299  func (w *crashingWAL) Wait()        { w.next.Wait() }
   300  
   301  // ------------------------------------------------------------------------------------------
   302  type testSim struct {
   303  	GenesisState sm.State
   304  	Config       *cfg.Config
   305  	Chain        []*types.Block
   306  	Commits      []*types.Commit
   307  	CleanupFunc  cleanupFunc
   308  }
   309  
   310  const (
   311  	numBlocks = 6
   312  )
   313  
   314  var (
   315  	mempool = emptyMempool{}
   316  	evpool  = sm.EmptyEvidencePool{}
   317  
   318  	sim testSim
   319  )
   320  
   321  //---------------------------------------
   322  // Test handshake/replay
   323  
   324  // 0 - all synced up
   325  // 1 - saved block but app and state are behind
   326  // 2 - save block and committed but state is behind
   327  // 3 - save block and committed with truncated block store and state behind
   328  var modes = []uint{0, 1, 2, 3}
   329  
   330  // This is actually not a test, it's for storing validator change tx data for testHandshakeReplay
   331  func TestSimulateValidatorsChange(t *testing.T) {
   332  	nPeers := 7
   333  	nVals := 4
   334  	css, genDoc, config, cleanup := randConsensusNetWithPeers(
   335  		nVals,
   336  		nPeers,
   337  		"replay_test",
   338  		newMockTickerFunc(true),
   339  		newPersistentKVStoreWithPath)
   340  	sim.Config = config
   341  	sim.GenesisState, _ = sm.MakeGenesisState(genDoc)
   342  	sim.CleanupFunc = cleanup
   343  
   344  	partSize := types.BlockPartSizeBytes
   345  
   346  	newRoundCh := subscribe(css[0].eventBus, types.EventQueryNewRound)
   347  	proposalCh := subscribe(css[0].eventBus, types.EventQueryCompleteProposal)
   348  
   349  	vss := make([]*validatorStub, nPeers)
   350  	for i := 0; i < nPeers; i++ {
   351  		vss[i] = newValidatorStub(css[i].privValidator, int32(i))
   352  	}
   353  	height, round := css[0].Height, css[0].Round
   354  
   355  	// start the machine
   356  	startTestRound(css[0], height, round)
   357  	incrementHeight(vss...)
   358  	ensureNewRound(newRoundCh, height, 0)
   359  	ensureNewProposal(proposalCh, height, round)
   360  	rs := css[0].GetRoundState()
   361  	signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:nVals]...)
   362  	ensureNewRound(newRoundCh, height+1, 0)
   363  
   364  	// HEIGHT 2
   365  	height++
   366  	incrementHeight(vss...)
   367  	newValidatorPubKey1, err := css[nVals].privValidator.GetPubKey()
   368  	require.NoError(t, err)
   369  	valPubKey1ABCI, err := cryptoenc.PubKeyToProto(newValidatorPubKey1)
   370  	require.NoError(t, err)
   371  	newValidatorTx1 := kvstore.MakeValSetChangeTx(valPubKey1ABCI, testMinPower)
   372  	err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx1, nil, mempl.TxInfo{})
   373  	assert.Nil(t, err)
   374  	propBlock, _ := css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
   375  	propBlockParts := propBlock.MakePartSet(partSize)
   376  	blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
   377  
   378  	proposal := types.NewProposal(vss[1].Height, round, -1, blockID)
   379  	p := proposal.ToProto()
   380  	if err := vss[1].SignProposal(config.ChainID(), p); err != nil {
   381  		t.Fatal("failed to sign bad proposal", err)
   382  	}
   383  	proposal.Signature = p.Signature
   384  
   385  	// set the proposal block
   386  	if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil {
   387  		t.Fatal(err)
   388  	}
   389  	ensureNewProposal(proposalCh, height, round)
   390  	rs = css[0].GetRoundState()
   391  	signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:nVals]...)
   392  	ensureNewRound(newRoundCh, height+1, 0)
   393  
   394  	// HEIGHT 3
   395  	height++
   396  	incrementHeight(vss...)
   397  	updateValidatorPubKey1, err := css[nVals].privValidator.GetPubKey()
   398  	require.NoError(t, err)
   399  	updatePubKey1ABCI, err := cryptoenc.PubKeyToProto(updateValidatorPubKey1)
   400  	require.NoError(t, err)
   401  	updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25)
   402  	err = assertMempool(css[0].txNotifier).CheckTx(updateValidatorTx1, nil, mempl.TxInfo{})
   403  	assert.Nil(t, err)
   404  	propBlock, _ = css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
   405  	propBlockParts = propBlock.MakePartSet(partSize)
   406  	blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
   407  
   408  	proposal = types.NewProposal(vss[2].Height, round, -1, blockID)
   409  	p = proposal.ToProto()
   410  	if err := vss[2].SignProposal(config.ChainID(), p); err != nil {
   411  		t.Fatal("failed to sign bad proposal", err)
   412  	}
   413  	proposal.Signature = p.Signature
   414  
   415  	// set the proposal block
   416  	if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil {
   417  		t.Fatal(err)
   418  	}
   419  	ensureNewProposal(proposalCh, height, round)
   420  	rs = css[0].GetRoundState()
   421  	signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:nVals]...)
   422  	ensureNewRound(newRoundCh, height+1, 0)
   423  
   424  	// HEIGHT 4
   425  	height++
   426  	incrementHeight(vss...)
   427  	newValidatorPubKey2, err := css[nVals+1].privValidator.GetPubKey()
   428  	require.NoError(t, err)
   429  	newVal2ABCI, err := cryptoenc.PubKeyToProto(newValidatorPubKey2)
   430  	require.NoError(t, err)
   431  	newValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, testMinPower)
   432  	err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx2, nil, mempl.TxInfo{})
   433  	assert.Nil(t, err)
   434  	newValidatorPubKey3, err := css[nVals+2].privValidator.GetPubKey()
   435  	require.NoError(t, err)
   436  	newVal3ABCI, err := cryptoenc.PubKeyToProto(newValidatorPubKey3)
   437  	require.NoError(t, err)
   438  	newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower)
   439  	err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx3, nil, mempl.TxInfo{})
   440  	assert.Nil(t, err)
   441  	propBlock, _ = css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
   442  	propBlockParts = propBlock.MakePartSet(partSize)
   443  	blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
   444  	newVss := make([]*validatorStub, nVals+1)
   445  	copy(newVss, vss[:nVals+1])
   446  	sort.Sort(ValidatorStubsByPower(newVss))
   447  
   448  	valIndexFn := func(cssIdx int) int {
   449  		for i, vs := range newVss {
   450  			vsPubKey, err := vs.GetPubKey()
   451  			require.NoError(t, err)
   452  
   453  			cssPubKey, err := css[cssIdx].privValidator.GetPubKey()
   454  			require.NoError(t, err)
   455  
   456  			if vsPubKey.Equals(cssPubKey) {
   457  				return i
   458  			}
   459  		}
   460  		panic(fmt.Sprintf("validator css[%d] not found in newVss", cssIdx))
   461  	}
   462  
   463  	selfIndex := valIndexFn(0)
   464  
   465  	proposal = types.NewProposal(vss[3].Height, round, -1, blockID)
   466  	p = proposal.ToProto()
   467  	if err := vss[3].SignProposal(config.ChainID(), p); err != nil {
   468  		t.Fatal("failed to sign bad proposal", err)
   469  	}
   470  	proposal.Signature = p.Signature
   471  
   472  	// set the proposal block
   473  	if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil {
   474  		t.Fatal(err)
   475  	}
   476  	ensureNewProposal(proposalCh, height, round)
   477  
   478  	removeValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, 0)
   479  	err = assertMempool(css[0].txNotifier).CheckTx(removeValidatorTx2, nil, mempl.TxInfo{})
   480  	assert.Nil(t, err)
   481  
   482  	rs = css[0].GetRoundState()
   483  	for i := 0; i < nVals+1; i++ {
   484  		if i == selfIndex {
   485  			continue
   486  		}
   487  		signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), newVss[i])
   488  	}
   489  
   490  	ensureNewRound(newRoundCh, height+1, 0)
   491  
   492  	// HEIGHT 5
   493  	height++
   494  	incrementHeight(vss...)
   495  	// Reflect the changes to vss[nVals] at height 3 and resort newVss.
   496  	newVssIdx := valIndexFn(nVals)
   497  	newVss[newVssIdx].VotingPower = 25
   498  	sort.Sort(ValidatorStubsByPower(newVss))
   499  	selfIndex = valIndexFn(0)
   500  	ensureNewProposal(proposalCh, height, round)
   501  	rs = css[0].GetRoundState()
   502  	for i := 0; i < nVals+1; i++ {
   503  		if i == selfIndex {
   504  			continue
   505  		}
   506  		signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), newVss[i])
   507  	}
   508  	ensureNewRound(newRoundCh, height+1, 0)
   509  
   510  	// HEIGHT 6
   511  	height++
   512  	incrementHeight(vss...)
   513  	removeValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, 0)
   514  	err = assertMempool(css[0].txNotifier).CheckTx(removeValidatorTx3, nil, mempl.TxInfo{})
   515  	assert.Nil(t, err)
   516  	propBlock, _ = css[0].createProposalBlock() // changeProposer(t, cs1, vs2)
   517  	propBlockParts = propBlock.MakePartSet(partSize)
   518  	blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
   519  	newVss = make([]*validatorStub, nVals+3)
   520  	copy(newVss, vss[:nVals+3])
   521  	sort.Sort(ValidatorStubsByPower(newVss))
   522  
   523  	selfIndex = valIndexFn(0)
   524  	proposal = types.NewProposal(vss[1].Height, round, -1, blockID)
   525  	p = proposal.ToProto()
   526  	if err := vss[1].SignProposal(config.ChainID(), p); err != nil {
   527  		t.Fatal("failed to sign bad proposal", err)
   528  	}
   529  	proposal.Signature = p.Signature
   530  
   531  	// set the proposal block
   532  	if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil {
   533  		t.Fatal(err)
   534  	}
   535  	ensureNewProposal(proposalCh, height, round)
   536  	rs = css[0].GetRoundState()
   537  	for i := 0; i < nVals+3; i++ {
   538  		if i == selfIndex {
   539  			continue
   540  		}
   541  		signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), newVss[i])
   542  	}
   543  	ensureNewRound(newRoundCh, height+1, 0)
   544  
   545  	sim.Chain = make([]*types.Block, 0)
   546  	sim.Commits = make([]*types.Commit, 0)
   547  	for i := 1; i <= numBlocks; i++ {
   548  		sim.Chain = append(sim.Chain, css[0].blockStore.LoadBlock(int64(i)))
   549  		sim.Commits = append(sim.Commits, css[0].blockStore.LoadBlockCommit(int64(i)))
   550  	}
   551  }
   552  
   553  // Sync from scratch
   554  func TestHandshakeReplayAll(t *testing.T) {
   555  	for _, m := range modes {
   556  		testHandshakeReplay(t, config, 0, m, false)
   557  	}
   558  	for _, m := range modes {
   559  		testHandshakeReplay(t, config, 0, m, true)
   560  	}
   561  }
   562  
   563  // Sync many, not from scratch
   564  func TestHandshakeReplaySome(t *testing.T) {
   565  	for _, m := range modes {
   566  		testHandshakeReplay(t, config, 2, m, false)
   567  	}
   568  	for _, m := range modes {
   569  		testHandshakeReplay(t, config, 2, m, true)
   570  	}
   571  }
   572  
   573  // Sync from lagging by one
   574  func TestHandshakeReplayOne(t *testing.T) {
   575  	for _, m := range modes {
   576  		testHandshakeReplay(t, config, numBlocks-1, m, false)
   577  	}
   578  	for _, m := range modes {
   579  		testHandshakeReplay(t, config, numBlocks-1, m, true)
   580  	}
   581  }
   582  
   583  // Sync from caught up
   584  func TestHandshakeReplayNone(t *testing.T) {
   585  	for _, m := range modes {
   586  		testHandshakeReplay(t, config, numBlocks, m, false)
   587  	}
   588  	for _, m := range modes {
   589  		testHandshakeReplay(t, config, numBlocks, m, true)
   590  	}
   591  }
   592  
   593  // Test mockProxyApp should not panic when app return ABCIResponses with some empty ResponseDeliverTx
   594  func TestMockProxyApp(t *testing.T) {
   595  	sim.CleanupFunc() // clean the test env created in TestSimulateValidatorsChange
   596  	logger := log.TestingLogger()
   597  	validTxs, invalidTxs := 0, 0
   598  	txIndex := 0
   599  
   600  	assert.NotPanics(t, func() {
   601  		abciResWithEmptyDeliverTx := new(tmstate.ABCIResponses)
   602  		abciResWithEmptyDeliverTx.DeliverTxs = make([]*abci.ResponseDeliverTx, 0)
   603  		abciResWithEmptyDeliverTx.DeliverTxs = append(abciResWithEmptyDeliverTx.DeliverTxs, &abci.ResponseDeliverTx{})
   604  
   605  		// called when saveABCIResponses:
   606  		bytes, err := proto.Marshal(abciResWithEmptyDeliverTx)
   607  		require.NoError(t, err)
   608  		loadedAbciRes := new(tmstate.ABCIResponses)
   609  
   610  		// this also happens sm.LoadABCIResponses
   611  		err = proto.Unmarshal(bytes, loadedAbciRes)
   612  		require.NoError(t, err)
   613  
   614  		mock := newMockProxyApp([]byte("mock_hash"), loadedAbciRes)
   615  
   616  		abciRes := new(tmstate.ABCIResponses)
   617  		abciRes.DeliverTxs = make([]*abci.ResponseDeliverTx, len(loadedAbciRes.DeliverTxs))
   618  		// Execute transactions and get hash.
   619  		proxyCb := func(req *abci.Request, res *abci.Response) {
   620  			if r, ok := res.Value.(*abci.Response_DeliverTx); ok {
   621  				// TODO: make use of res.Log
   622  				// TODO: make use of this info
   623  				// Blocks may include invalid txs.
   624  				txRes := r.DeliverTx
   625  				if txRes.Code == abci.CodeTypeOK {
   626  					validTxs++
   627  				} else {
   628  					logger.Debug("Invalid tx", "code", txRes.Code, "log", txRes.Log)
   629  					invalidTxs++
   630  				}
   631  				abciRes.DeliverTxs[txIndex] = txRes
   632  				txIndex++
   633  			}
   634  		}
   635  		mock.SetResponseCallback(proxyCb)
   636  
   637  		someTx := []byte("tx")
   638  		mock.DeliverTxAsync(abci.RequestDeliverTx{Tx: someTx})
   639  	})
   640  	assert.True(t, validTxs == 1)
   641  	assert.True(t, invalidTxs == 0)
   642  }
   643  
   644  func tempWALWithData(data []byte) string {
   645  	walFile, err := os.CreateTemp("", "wal")
   646  	if err != nil {
   647  		panic(fmt.Sprintf("failed to create temp WAL file: %v", err))
   648  	}
   649  	_, err = walFile.Write(data)
   650  	if err != nil {
   651  		panic(fmt.Sprintf("failed to write to temp WAL file: %v", err))
   652  	}
   653  	if err := walFile.Close(); err != nil {
   654  		panic(fmt.Sprintf("failed to close temp WAL file: %v", err))
   655  	}
   656  	return walFile.Name()
   657  }
   658  
   659  // Make some blocks. Start a fresh app and apply nBlocks blocks.
   660  // Then restart the app and sync it up with the remaining blocks
   661  func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uint, testValidatorsChange bool) {
   662  	var chain []*types.Block
   663  	var commits []*types.Commit
   664  	var store *mockBlockStore
   665  	var stateDB dbm.DB
   666  	var genesisState sm.State
   667  	if testValidatorsChange {
   668  		testConfig := ResetConfig(fmt.Sprintf("%s_%v_m", t.Name(), mode))
   669  		defer os.RemoveAll(testConfig.RootDir)
   670  		stateDB = dbm.NewMemDB()
   671  
   672  		genesisState = sim.GenesisState
   673  		config = sim.Config
   674  		chain = append([]*types.Block{}, sim.Chain...) // copy chain
   675  		commits = sim.Commits
   676  		store = newMockBlockStore(config, genesisState.ConsensusParams)
   677  	} else { // test single node
   678  		testConfig := ResetConfig(fmt.Sprintf("%s_%v_s", t.Name(), mode))
   679  		defer os.RemoveAll(testConfig.RootDir)
   680  		walBody, err := WALWithNBlocks(t, numBlocks)
   681  		require.NoError(t, err)
   682  		walFile := tempWALWithData(walBody)
   683  		config.Consensus.SetWalFile(walFile)
   684  
   685  		privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
   686  
   687  		wal, err := NewWAL(walFile)
   688  		require.NoError(t, err)
   689  		wal.SetLogger(log.TestingLogger())
   690  		err = wal.Start()
   691  		require.NoError(t, err)
   692  		t.Cleanup(func() {
   693  			if err := wal.Stop(); err != nil {
   694  				t.Error(err)
   695  			}
   696  		})
   697  		chain, commits, err = makeBlockchainFromWAL(wal)
   698  		require.NoError(t, err)
   699  		pubKey, err := privVal.GetPubKey()
   700  		require.NoError(t, err)
   701  		stateDB, genesisState, store = stateAndStore(config, pubKey, kvstore.ProtocolVersion)
   702  
   703  	}
   704  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   705  		DiscardABCIResponses: false,
   706  	})
   707  	store.chain = chain
   708  	store.commits = commits
   709  
   710  	state := genesisState.Copy()
   711  	// run the chain through state.ApplyBlock to build up the tendermint state
   712  	state = buildTMStateFromChain(config, stateStore, state, chain, nBlocks, mode)
   713  	latestAppHash := state.AppHash
   714  
   715  	// make a new client creator
   716  	kvstoreApp := kvstore.NewPersistentKVStoreApplication(
   717  		filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_a", nBlocks, mode)))
   718  
   719  	clientCreator2 := proxy.NewLocalClientCreator(kvstoreApp)
   720  	if nBlocks > 0 {
   721  		// run nBlocks against a new client to build up the app state.
   722  		// use a throwaway tendermint state
   723  		proxyApp := proxy.NewAppConns(clientCreator2)
   724  		stateDB1 := dbm.NewMemDB()
   725  		stateStore := sm.NewStore(stateDB1, sm.StoreOptions{
   726  			DiscardABCIResponses: false,
   727  		})
   728  		err := stateStore.Save(genesisState)
   729  		require.NoError(t, err)
   730  		buildAppStateFromChain(proxyApp, stateStore, genesisState, chain, nBlocks, mode)
   731  	}
   732  
   733  	// Prune block store if requested
   734  	expectError := false
   735  	if mode == 3 {
   736  		pruned, err := store.PruneBlocks(2)
   737  		require.NoError(t, err)
   738  		require.EqualValues(t, 1, pruned)
   739  		expectError = int64(nBlocks) < 2
   740  	}
   741  
   742  	// now start the app using the handshake - it should sync
   743  	genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
   744  	handshaker := NewHandshaker(stateStore, state, store, genDoc)
   745  	proxyApp := proxy.NewAppConns(clientCreator2)
   746  	if err := proxyApp.Start(); err != nil {
   747  		t.Fatalf("Error starting proxy app connections: %v", err)
   748  	}
   749  
   750  	t.Cleanup(func() {
   751  		if err := proxyApp.Stop(); err != nil {
   752  			t.Error(err)
   753  		}
   754  	})
   755  
   756  	err := handshaker.Handshake(proxyApp)
   757  	if expectError {
   758  		require.Error(t, err)
   759  		return
   760  	} else if err != nil {
   761  		t.Fatalf("Error on abci handshake: %v", err)
   762  	}
   763  
   764  	// get the latest app hash from the app
   765  	res, err := proxyApp.Query().InfoSync(abci.RequestInfo{Version: ""})
   766  	if err != nil {
   767  		t.Fatal(err)
   768  	}
   769  
   770  	// the app hash should be synced up
   771  	if !bytes.Equal(latestAppHash, res.LastBlockAppHash) {
   772  		t.Fatalf(
   773  			"Expected app hashes to match after handshake/replay. got %X, expected %X",
   774  			res.LastBlockAppHash,
   775  			latestAppHash)
   776  	}
   777  
   778  	expectedBlocksToSync := numBlocks - nBlocks
   779  	if nBlocks == numBlocks && mode > 0 {
   780  		expectedBlocksToSync++
   781  	} else if nBlocks > 0 && mode == 1 {
   782  		expectedBlocksToSync++
   783  	}
   784  
   785  	if handshaker.NBlocks() != expectedBlocksToSync {
   786  		t.Fatalf("Expected handshake to sync %d blocks, got %d", expectedBlocksToSync, handshaker.NBlocks())
   787  	}
   788  }
   789  
   790  func applyBlock(stateStore sm.Store, st sm.State, blk *types.Block, proxyApp proxy.AppConns) sm.State {
   791  	testPartSize := types.BlockPartSizeBytes
   792  	blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
   793  
   794  	blkID := types.BlockID{Hash: blk.Hash(), PartSetHeader: blk.MakePartSet(testPartSize).Header()}
   795  	newState, _, err := blockExec.ApplyBlock(st, blkID, blk)
   796  	if err != nil {
   797  		panic(err)
   798  	}
   799  	return newState
   800  }
   801  
   802  func buildAppStateFromChain(proxyApp proxy.AppConns, stateStore sm.Store,
   803  	state sm.State, chain []*types.Block, nBlocks int, mode uint,
   804  ) {
   805  	// start a new app without handshake, play nBlocks blocks
   806  	if err := proxyApp.Start(); err != nil {
   807  		panic(err)
   808  	}
   809  	defer proxyApp.Stop() //nolint:errcheck // ignore
   810  
   811  	state.Version.Consensus.App = kvstore.ProtocolVersion // simulate handshake, receive app version
   812  	validators := types.TM2PB.ValidatorUpdates(state.Validators)
   813  	if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{
   814  		Validators: validators,
   815  	}); err != nil {
   816  		panic(err)
   817  	}
   818  	if err := stateStore.Save(state); err != nil { // save height 1's validatorsInfo
   819  		panic(err)
   820  	}
   821  	switch mode {
   822  	case 0:
   823  		for i := 0; i < nBlocks; i++ {
   824  			block := chain[i]
   825  			state = applyBlock(stateStore, state, block, proxyApp)
   826  		}
   827  	case 1, 2, 3:
   828  		for i := 0; i < nBlocks-1; i++ {
   829  			block := chain[i]
   830  			state = applyBlock(stateStore, state, block, proxyApp)
   831  		}
   832  
   833  		if mode == 2 || mode == 3 {
   834  			// update the kvstore height and apphash
   835  			// as if we ran commit but not
   836  			state = applyBlock(stateStore, state, chain[nBlocks-1], proxyApp)
   837  		}
   838  	default:
   839  		panic(fmt.Sprintf("unknown mode %v", mode))
   840  	}
   841  }
   842  
   843  func buildTMStateFromChain(
   844  	config *cfg.Config,
   845  	stateStore sm.Store,
   846  	state sm.State,
   847  	chain []*types.Block,
   848  	nBlocks int,
   849  	mode uint,
   850  ) sm.State {
   851  	// run the whole chain against this client to build up the tendermint state
   852  	clientCreator := proxy.NewLocalClientCreator(
   853  		kvstore.NewPersistentKVStoreApplication(
   854  			filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_t", nBlocks, mode))))
   855  	proxyApp := proxy.NewAppConns(clientCreator)
   856  	if err := proxyApp.Start(); err != nil {
   857  		panic(err)
   858  	}
   859  	defer proxyApp.Stop() //nolint:errcheck
   860  
   861  	state.Version.Consensus.App = kvstore.ProtocolVersion // simulate handshake, receive app version
   862  	validators := types.TM2PB.ValidatorUpdates(state.Validators)
   863  	if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{
   864  		Validators: validators,
   865  	}); err != nil {
   866  		panic(err)
   867  	}
   868  	if err := stateStore.Save(state); err != nil { // save height 1's validatorsInfo
   869  		panic(err)
   870  	}
   871  	switch mode {
   872  	case 0:
   873  		// sync right up
   874  		for _, block := range chain {
   875  			state = applyBlock(stateStore, state, block, proxyApp)
   876  		}
   877  
   878  	case 1, 2, 3:
   879  		// sync up to the penultimate as if we stored the block.
   880  		// whether we commit or not depends on the appHash
   881  		for _, block := range chain[:len(chain)-1] {
   882  			state = applyBlock(stateStore, state, block, proxyApp)
   883  		}
   884  
   885  		// apply the final block to a state copy so we can
   886  		// get the right next appHash but keep the state back
   887  		applyBlock(stateStore, state, chain[len(chain)-1], proxyApp)
   888  	default:
   889  		panic(fmt.Sprintf("unknown mode %v", mode))
   890  	}
   891  
   892  	return state
   893  }
   894  
   895  func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) {
   896  	// 1. Initialize tendermint and commit 3 blocks with the following app hashes:
   897  	//		- 0x01
   898  	//		- 0x02
   899  	//		- 0x03
   900  	config := ResetConfig("handshake_test_")
   901  	defer os.RemoveAll(config.RootDir)
   902  	privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
   903  	const appVersion = 0x0
   904  	pubKey, err := privVal.GetPubKey()
   905  	require.NoError(t, err)
   906  	stateDB, state, store := stateAndStore(config, pubKey, appVersion)
   907  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   908  		DiscardABCIResponses: false,
   909  	})
   910  	genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
   911  	state.LastValidators = state.Validators.Copy()
   912  	// mode = 0 for committing all the blocks
   913  	blocks := makeBlocks(3, &state, privVal)
   914  	store.chain = blocks
   915  
   916  	// 2. Tendermint must panic if app returns wrong hash for the first block
   917  	//		- RANDOM HASH
   918  	//		- 0x02
   919  	//		- 0x03
   920  	{
   921  		app := &badApp{numBlocks: 3, allHashesAreWrong: true}
   922  		clientCreator := proxy.NewLocalClientCreator(app)
   923  		proxyApp := proxy.NewAppConns(clientCreator)
   924  		err := proxyApp.Start()
   925  		require.NoError(t, err)
   926  		t.Cleanup(func() {
   927  			if err := proxyApp.Stop(); err != nil {
   928  				t.Error(err)
   929  			}
   930  		})
   931  
   932  		assert.Panics(t, func() {
   933  			h := NewHandshaker(stateStore, state, store, genDoc)
   934  			if err = h.Handshake(proxyApp); err != nil {
   935  				t.Log(err)
   936  			}
   937  		})
   938  	}
   939  
   940  	// 3. Tendermint must panic if app returns wrong hash for the last block
   941  	//		- 0x01
   942  	//		- 0x02
   943  	//		- RANDOM HASH
   944  	{
   945  		app := &badApp{numBlocks: 3, onlyLastHashIsWrong: true}
   946  		clientCreator := proxy.NewLocalClientCreator(app)
   947  		proxyApp := proxy.NewAppConns(clientCreator)
   948  		err := proxyApp.Start()
   949  		require.NoError(t, err)
   950  		t.Cleanup(func() {
   951  			if err := proxyApp.Stop(); err != nil {
   952  				t.Error(err)
   953  			}
   954  		})
   955  
   956  		assert.Panics(t, func() {
   957  			h := NewHandshaker(stateStore, state, store, genDoc)
   958  			if err = h.Handshake(proxyApp); err != nil {
   959  				t.Log(err)
   960  			}
   961  		})
   962  	}
   963  }
   964  
   965  func makeBlocks(n int, state *sm.State, privVal types.PrivValidator) []*types.Block {
   966  	blocks := make([]*types.Block, 0)
   967  
   968  	var (
   969  		prevBlock     *types.Block
   970  		prevBlockMeta *types.BlockMeta
   971  	)
   972  
   973  	appHeight := byte(0x01)
   974  	for i := 0; i < n; i++ {
   975  		height := int64(i + 1)
   976  
   977  		block, parts := makeBlock(*state, prevBlock, prevBlockMeta, privVal, height)
   978  		blocks = append(blocks, block)
   979  
   980  		prevBlock = block
   981  		prevBlockMeta = types.NewBlockMeta(block, parts)
   982  
   983  		// update state
   984  		state.AppHash = []byte{appHeight}
   985  		appHeight++
   986  		state.LastBlockHeight = height
   987  	}
   988  
   989  	return blocks
   990  }
   991  
   992  func makeBlock(state sm.State, lastBlock *types.Block, lastBlockMeta *types.BlockMeta,
   993  	privVal types.PrivValidator, height int64,
   994  ) (*types.Block, *types.PartSet) {
   995  	lastCommit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
   996  	if height > 1 {
   997  		vote, _ := types.MakeVote(
   998  			lastBlock.Header.Height,
   999  			lastBlockMeta.BlockID,
  1000  			state.Validators,
  1001  			privVal,
  1002  			lastBlock.Header.ChainID,
  1003  			time.Now())
  1004  		lastCommit = types.NewCommit(vote.Height, vote.Round,
  1005  			lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
  1006  	}
  1007  
  1008  	return state.MakeBlock(height, []types.Tx{}, lastCommit, nil, state.Validators.GetProposer().Address)
  1009  }
  1010  
  1011  type badApp struct {
  1012  	abci.BaseApplication
  1013  	numBlocks           byte
  1014  	height              byte
  1015  	allHashesAreWrong   bool
  1016  	onlyLastHashIsWrong bool
  1017  }
  1018  
  1019  func (app *badApp) Commit() abci.ResponseCommit {
  1020  	app.height++
  1021  	if app.onlyLastHashIsWrong {
  1022  		if app.height == app.numBlocks {
  1023  			return abci.ResponseCommit{Data: tmrand.Bytes(8)}
  1024  		}
  1025  		return abci.ResponseCommit{Data: []byte{app.height}}
  1026  	} else if app.allHashesAreWrong {
  1027  		return abci.ResponseCommit{Data: tmrand.Bytes(8)}
  1028  	}
  1029  
  1030  	panic("either allHashesAreWrong or onlyLastHashIsWrong must be set")
  1031  }
  1032  
  1033  //--------------------------
  1034  // utils for making blocks
  1035  
  1036  func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
  1037  	var height int64
  1038  
  1039  	// Search for height marker
  1040  	gr, found, err := wal.SearchForEndHeight(height, &WALSearchOptions{})
  1041  	if err != nil {
  1042  		return nil, nil, err
  1043  	}
  1044  	if !found {
  1045  		return nil, nil, fmt.Errorf("wal does not contain height %d", height)
  1046  	}
  1047  	defer gr.Close()
  1048  
  1049  	// log.Notice("Build a blockchain by reading from the WAL")
  1050  
  1051  	var (
  1052  		blocks          []*types.Block
  1053  		commits         []*types.Commit
  1054  		thisBlockParts  *types.PartSet
  1055  		thisBlockCommit *types.Commit
  1056  	)
  1057  
  1058  	dec := NewWALDecoder(gr)
  1059  	for {
  1060  		msg, err := dec.Decode()
  1061  		if err == io.EOF {
  1062  			break
  1063  		} else if err != nil {
  1064  			return nil, nil, err
  1065  		}
  1066  
  1067  		piece := readPieceFromWAL(msg)
  1068  		if piece == nil {
  1069  			continue
  1070  		}
  1071  
  1072  		switch p := piece.(type) {
  1073  		case EndHeightMessage:
  1074  			// if its not the first one, we have a full block
  1075  			if thisBlockParts != nil {
  1076  				pbb := new(tmproto.Block)
  1077  				bz, err := io.ReadAll(thisBlockParts.GetReader())
  1078  				if err != nil {
  1079  					panic(err)
  1080  				}
  1081  				err = proto.Unmarshal(bz, pbb)
  1082  				if err != nil {
  1083  					panic(err)
  1084  				}
  1085  				block, err := types.BlockFromProto(pbb)
  1086  				if err != nil {
  1087  					panic(err)
  1088  				}
  1089  
  1090  				if block.Height != height+1 {
  1091  					panic(fmt.Sprintf("read bad block from wal. got height %d, expected %d", block.Height, height+1))
  1092  				}
  1093  				commitHeight := thisBlockCommit.Height
  1094  				if commitHeight != height+1 {
  1095  					panic(fmt.Sprintf("commit doesnt match. got height %d, expected %d", commitHeight, height+1))
  1096  				}
  1097  				blocks = append(blocks, block)
  1098  				commits = append(commits, thisBlockCommit)
  1099  				height++
  1100  			}
  1101  		case *types.PartSetHeader:
  1102  			thisBlockParts = types.NewPartSetFromHeader(*p)
  1103  		case *types.Part:
  1104  			_, err := thisBlockParts.AddPart(p)
  1105  			if err != nil {
  1106  				return nil, nil, err
  1107  			}
  1108  		case *types.Vote:
  1109  			if p.Type == tmproto.PrecommitType {
  1110  				thisBlockCommit = types.NewCommit(p.Height, p.Round,
  1111  					p.BlockID, []types.CommitSig{p.CommitSig()})
  1112  			}
  1113  		}
  1114  	}
  1115  	// grab the last block too
  1116  	bz, err := io.ReadAll(thisBlockParts.GetReader())
  1117  	if err != nil {
  1118  		panic(err)
  1119  	}
  1120  	pbb := new(tmproto.Block)
  1121  	err = proto.Unmarshal(bz, pbb)
  1122  	if err != nil {
  1123  		panic(err)
  1124  	}
  1125  	block, err := types.BlockFromProto(pbb)
  1126  	if err != nil {
  1127  		panic(err)
  1128  	}
  1129  	if block.Height != height+1 {
  1130  		panic(fmt.Sprintf("read bad block from wal. got height %d, expected %d", block.Height, height+1))
  1131  	}
  1132  	commitHeight := thisBlockCommit.Height
  1133  	if commitHeight != height+1 {
  1134  		panic(fmt.Sprintf("commit doesnt match. got height %d, expected %d", commitHeight, height+1))
  1135  	}
  1136  	blocks = append(blocks, block)
  1137  	commits = append(commits, thisBlockCommit)
  1138  	return blocks, commits, nil
  1139  }
  1140  
  1141  func readPieceFromWAL(msg *TimedWALMessage) interface{} {
  1142  	// for logging
  1143  	switch m := msg.Msg.(type) {
  1144  	case msgInfo:
  1145  		switch msg := m.Msg.(type) {
  1146  		case *ProposalMessage:
  1147  			return &msg.Proposal.BlockID.PartSetHeader
  1148  		case *BlockPartMessage:
  1149  			return msg.Part
  1150  		case *VoteMessage:
  1151  			return msg.Vote
  1152  		}
  1153  	case EndHeightMessage:
  1154  		return m
  1155  	}
  1156  
  1157  	return nil
  1158  }
  1159  
  1160  // fresh state and mock store
  1161  func stateAndStore(
  1162  	config *cfg.Config,
  1163  	pubKey crypto.PubKey,
  1164  	appVersion uint64,
  1165  ) (dbm.DB, sm.State, *mockBlockStore) {
  1166  	stateDB := dbm.NewMemDB()
  1167  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
  1168  		DiscardABCIResponses: false,
  1169  	})
  1170  	state, _ := sm.MakeGenesisStateFromFile(config.GenesisFile())
  1171  	state.Version.Consensus.App = appVersion
  1172  	store := newMockBlockStore(config, state.ConsensusParams)
  1173  	if err := stateStore.Save(state); err != nil {
  1174  		panic(err)
  1175  	}
  1176  	return stateDB, state, store
  1177  }
  1178  
  1179  //----------------------------------
  1180  // mock block store
  1181  
  1182  type mockBlockStore struct {
  1183  	config  *cfg.Config
  1184  	params  tmproto.ConsensusParams
  1185  	chain   []*types.Block
  1186  	commits []*types.Commit
  1187  	base    int64
  1188  }
  1189  
  1190  // TODO: NewBlockStore(db.NewMemDB) ...
  1191  func newMockBlockStore(config *cfg.Config, params tmproto.ConsensusParams) *mockBlockStore {
  1192  	return &mockBlockStore{config, params, nil, nil, 0}
  1193  }
  1194  
  1195  func (bs *mockBlockStore) Height() int64                       { return int64(len(bs.chain)) }
  1196  func (bs *mockBlockStore) Base() int64                         { return bs.base }
  1197  func (bs *mockBlockStore) Size() int64                         { return bs.Height() - bs.Base() + 1 }
  1198  func (bs *mockBlockStore) LoadBaseMeta() *types.BlockMeta      { return bs.LoadBlockMeta(bs.base) }
  1199  func (bs *mockBlockStore) LoadBlock(height int64) *types.Block { return bs.chain[height-1] }
  1200  func (bs *mockBlockStore) LoadBlockByHash(hash []byte) *types.Block {
  1201  	return bs.chain[int64(len(bs.chain))-1]
  1202  }
  1203  
  1204  func (bs *mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
  1205  	block := bs.chain[height-1]
  1206  	return &types.BlockMeta{
  1207  		BlockID: types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(types.BlockPartSizeBytes).Header()},
  1208  		Header:  block.Header,
  1209  	}
  1210  }
  1211  func (bs *mockBlockStore) LoadBlockPart(height int64, index int) *types.Part { return nil }
  1212  func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
  1213  }
  1214  
  1215  func (bs *mockBlockStore) LoadBlockCommit(height int64) *types.Commit {
  1216  	return bs.commits[height-1]
  1217  }
  1218  
  1219  func (bs *mockBlockStore) LoadSeenCommit(height int64) *types.Commit {
  1220  	return bs.commits[height-1]
  1221  }
  1222  
  1223  func (bs *mockBlockStore) PruneBlocks(height int64) (uint64, error) {
  1224  	pruned := uint64(0)
  1225  	for i := int64(0); i < height-1; i++ {
  1226  		bs.chain[i] = nil
  1227  		bs.commits[i] = nil
  1228  		pruned++
  1229  	}
  1230  	bs.base = height
  1231  	return pruned, nil
  1232  }
  1233  
  1234  //---------------------------------------
  1235  // Test handshake/init chain
  1236  
  1237  func TestHandshakeUpdatesValidators(t *testing.T) {
  1238  	val, _ := types.RandValidator(true, 10)
  1239  	vals := types.NewValidatorSet([]*types.Validator{val})
  1240  	app := &initChainApp{vals: types.TM2PB.ValidatorUpdates(vals)}
  1241  	clientCreator := proxy.NewLocalClientCreator(app)
  1242  
  1243  	config := ResetConfig("handshake_test_")
  1244  	defer os.RemoveAll(config.RootDir)
  1245  	privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
  1246  	pubKey, err := privVal.GetPubKey()
  1247  	require.NoError(t, err)
  1248  	stateDB, state, store := stateAndStore(config, pubKey, 0x0)
  1249  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
  1250  		DiscardABCIResponses: false,
  1251  	})
  1252  
  1253  	oldValAddr := state.Validators.Validators[0].Address
  1254  
  1255  	// now start the app using the handshake - it should sync
  1256  	genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
  1257  	handshaker := NewHandshaker(stateStore, state, store, genDoc)
  1258  	proxyApp := proxy.NewAppConns(clientCreator)
  1259  	if err := proxyApp.Start(); err != nil {
  1260  		t.Fatalf("Error starting proxy app connections: %v", err)
  1261  	}
  1262  	t.Cleanup(func() {
  1263  		if err := proxyApp.Stop(); err != nil {
  1264  			t.Error(err)
  1265  		}
  1266  	})
  1267  	if err := handshaker.Handshake(proxyApp); err != nil {
  1268  		t.Fatalf("Error on abci handshake: %v", err)
  1269  	}
  1270  	// reload the state, check the validator set was updated
  1271  	state, err = stateStore.Load()
  1272  	require.NoError(t, err)
  1273  
  1274  	newValAddr := state.Validators.Validators[0].Address
  1275  	expectValAddr := val.Address
  1276  	assert.NotEqual(t, oldValAddr, newValAddr)
  1277  	assert.Equal(t, newValAddr, expectValAddr)
  1278  }
  1279  
  1280  // returns the vals on InitChain
  1281  type initChainApp struct {
  1282  	abci.BaseApplication
  1283  	vals []abci.ValidatorUpdate
  1284  }
  1285  
  1286  func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
  1287  	return abci.ResponseInitChain{
  1288  		Validators: ica.vals,
  1289  	}
  1290  }