github.com/devwanda/aphelion-staking@v0.33.9/consensus/common_test.go (about)

     1  package consensus
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"sort"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/go-kit/kit/log/term"
    16  	"github.com/pkg/errors"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"path"
    20  
    21  	dbm "github.com/tendermint/tm-db"
    22  
    23  	abcicli "github.com/devwanda/aphelion-staking/abci/client"
    24  	"github.com/devwanda/aphelion-staking/abci/example/counter"
    25  	"github.com/devwanda/aphelion-staking/abci/example/kvstore"
    26  	abci "github.com/devwanda/aphelion-staking/abci/types"
    27  	cfg "github.com/devwanda/aphelion-staking/config"
    28  	cstypes "github.com/devwanda/aphelion-staking/consensus/types"
    29  	tmbytes "github.com/devwanda/aphelion-staking/libs/bytes"
    30  	"github.com/devwanda/aphelion-staking/libs/log"
    31  	tmos "github.com/devwanda/aphelion-staking/libs/os"
    32  	tmpubsub "github.com/devwanda/aphelion-staking/libs/pubsub"
    33  	mempl "github.com/devwanda/aphelion-staking/mempool"
    34  	"github.com/devwanda/aphelion-staking/p2p"
    35  	"github.com/devwanda/aphelion-staking/privval"
    36  	sm "github.com/devwanda/aphelion-staking/state"
    37  	"github.com/devwanda/aphelion-staking/store"
    38  	"github.com/devwanda/aphelion-staking/types"
    39  	tmtime "github.com/devwanda/aphelion-staking/types/time"
    40  )
    41  
    42  const (
    43  	testSubscriber = "test-client"
    44  )
    45  
    46  // A cleanupFunc cleans up any config / test files created for a particular
    47  // test.
    48  type cleanupFunc func()
    49  
    50  // genesis, chain_id, priv_val
    51  var config *cfg.Config // NOTE: must be reset for each _test.go file
    52  var consensusReplayConfig *cfg.Config
    53  var ensureTimeout = time.Millisecond * 100
    54  
    55  func ensureDir(dir string, mode os.FileMode) {
    56  	if err := tmos.EnsureDir(dir, mode); err != nil {
    57  		panic(err)
    58  	}
    59  }
    60  
    61  func ResetConfig(name string) *cfg.Config {
    62  	return cfg.ResetTestRoot(name)
    63  }
    64  
    65  //-------------------------------------------------------------------------------
    66  // validator stub (a kvstore consensus peer we control)
    67  
    68  type validatorStub struct {
    69  	Index  int // Validator index. NOTE: we don't assume validator set changes.
    70  	Height int64
    71  	Round  int
    72  	types.PrivValidator
    73  }
    74  
    75  var testMinPower int64 = 10
    76  
    77  func newValidatorStub(privValidator types.PrivValidator, valIndex int) *validatorStub {
    78  	return &validatorStub{
    79  		Index:         valIndex,
    80  		PrivValidator: privValidator,
    81  	}
    82  }
    83  
    84  func (vs *validatorStub) signVote(
    85  	voteType types.SignedMsgType,
    86  	hash []byte,
    87  	header types.PartSetHeader) (*types.Vote, error) {
    88  
    89  	pubKey, err := vs.PrivValidator.GetPubKey()
    90  	if err != nil {
    91  		return nil, errors.Wrap(err, "can't get pubkey")
    92  	}
    93  
    94  	vote := &types.Vote{
    95  		ValidatorIndex:   vs.Index,
    96  		ValidatorAddress: pubKey.Address(),
    97  		Height:           vs.Height,
    98  		Round:            vs.Round,
    99  		Timestamp:        tmtime.Now(),
   100  		Type:             voteType,
   101  		BlockID:          types.BlockID{Hash: hash, PartsHeader: header},
   102  	}
   103  
   104  	err = vs.PrivValidator.SignVote(config.ChainID(), vote)
   105  	return vote, err
   106  }
   107  
   108  // Sign vote for type/hash/header
   109  func signVote(vs *validatorStub, voteType types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote {
   110  	v, err := vs.signVote(voteType, hash, header)
   111  	if err != nil {
   112  		panic(fmt.Errorf("failed to sign vote: %v", err))
   113  	}
   114  	return v
   115  }
   116  
   117  func signVotes(
   118  	voteType types.SignedMsgType,
   119  	hash []byte,
   120  	header types.PartSetHeader,
   121  	vss ...*validatorStub) []*types.Vote {
   122  	votes := make([]*types.Vote, len(vss))
   123  	for i, vs := range vss {
   124  		votes[i] = signVote(vs, voteType, hash, header)
   125  	}
   126  	return votes
   127  }
   128  
   129  func incrementHeight(vss ...*validatorStub) {
   130  	for _, vs := range vss {
   131  		vs.Height++
   132  	}
   133  }
   134  
   135  func incrementRound(vss ...*validatorStub) {
   136  	for _, vs := range vss {
   137  		vs.Round++
   138  	}
   139  }
   140  
   141  type ValidatorStubsByAddress []*validatorStub
   142  
   143  func (vss ValidatorStubsByAddress) Len() int {
   144  	return len(vss)
   145  }
   146  
   147  func (vss ValidatorStubsByAddress) Less(i, j int) bool {
   148  	vssi, err := vss[i].GetPubKey()
   149  	if err != nil {
   150  		panic(err)
   151  	}
   152  	vssj, err := vss[j].GetPubKey()
   153  	if err != nil {
   154  		panic(err)
   155  	}
   156  	return bytes.Compare(vssi.Address(), vssj.Address()) == -1
   157  }
   158  
   159  func (vss ValidatorStubsByAddress) Swap(i, j int) {
   160  	it := vss[i]
   161  	vss[i] = vss[j]
   162  	vss[i].Index = i
   163  	vss[j] = it
   164  	vss[j].Index = j
   165  }
   166  
   167  //-------------------------------------------------------------------------------
   168  // Functions for transitioning the consensus state
   169  
   170  func startTestRound(cs *State, height int64, round int) {
   171  	cs.enterNewRound(height, round)
   172  	cs.startRoutines(0)
   173  }
   174  
   175  // Create proposal block from cs1 but sign it with vs.
   176  func decideProposal(
   177  	cs1 *State,
   178  	vs *validatorStub,
   179  	height int64,
   180  	round int,
   181  ) (proposal *types.Proposal, block *types.Block) {
   182  	cs1.mtx.Lock()
   183  	block, blockParts := cs1.createProposalBlock()
   184  	validRound := cs1.ValidRound
   185  	chainID := cs1.state.ChainID
   186  	cs1.mtx.Unlock()
   187  	if block == nil {
   188  		panic("Failed to createProposalBlock. Did you forget to add commit for previous block?")
   189  	}
   190  
   191  	// Make proposal
   192  	polRound, propBlockID := validRound, types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()}
   193  	proposal = types.NewProposal(height, round, polRound, propBlockID)
   194  	if err := vs.SignProposal(chainID, proposal); err != nil {
   195  		panic(err)
   196  	}
   197  	return
   198  }
   199  
   200  func addVotes(to *State, votes ...*types.Vote) {
   201  	for _, vote := range votes {
   202  		to.peerMsgQueue <- msgInfo{Msg: &VoteMessage{vote}}
   203  	}
   204  }
   205  
   206  func signAddVotes(
   207  	to *State,
   208  	voteType types.SignedMsgType,
   209  	hash []byte,
   210  	header types.PartSetHeader,
   211  	vss ...*validatorStub,
   212  ) {
   213  	votes := signVotes(voteType, hash, header, vss...)
   214  	addVotes(to, votes...)
   215  }
   216  
   217  func validatePrevote(t *testing.T, cs *State, round int, privVal *validatorStub, blockHash []byte) {
   218  	prevotes := cs.Votes.Prevotes(round)
   219  	pubKey, err := privVal.GetPubKey()
   220  	require.NoError(t, err)
   221  	address := pubKey.Address()
   222  	var vote *types.Vote
   223  	if vote = prevotes.GetByAddress(address); vote == nil {
   224  		panic("Failed to find prevote from validator")
   225  	}
   226  	if blockHash == nil {
   227  		if vote.BlockID.Hash != nil {
   228  			panic(fmt.Sprintf("Expected prevote to be for nil, got %X", vote.BlockID.Hash))
   229  		}
   230  	} else {
   231  		if !bytes.Equal(vote.BlockID.Hash, blockHash) {
   232  			panic(fmt.Sprintf("Expected prevote to be for %X, got %X", blockHash, vote.BlockID.Hash))
   233  		}
   234  	}
   235  }
   236  
   237  func validateLastPrecommit(t *testing.T, cs *State, privVal *validatorStub, blockHash []byte) {
   238  	votes := cs.LastCommit
   239  	pv, err := privVal.GetPubKey()
   240  	require.NoError(t, err)
   241  	address := pv.Address()
   242  	var vote *types.Vote
   243  	if vote = votes.GetByAddress(address); vote == nil {
   244  		panic("Failed to find precommit from validator")
   245  	}
   246  	if !bytes.Equal(vote.BlockID.Hash, blockHash) {
   247  		panic(fmt.Sprintf("Expected precommit to be for %X, got %X", blockHash, vote.BlockID.Hash))
   248  	}
   249  }
   250  
   251  func validatePrecommit(
   252  	t *testing.T,
   253  	cs *State,
   254  	thisRound,
   255  	lockRound int,
   256  	privVal *validatorStub,
   257  	votedBlockHash,
   258  	lockedBlockHash []byte,
   259  ) {
   260  	precommits := cs.Votes.Precommits(thisRound)
   261  	pv, err := privVal.GetPubKey()
   262  	require.NoError(t, err)
   263  	address := pv.Address()
   264  	var vote *types.Vote
   265  	if vote = precommits.GetByAddress(address); vote == nil {
   266  		panic("Failed to find precommit from validator")
   267  	}
   268  
   269  	if votedBlockHash == nil {
   270  		if vote.BlockID.Hash != nil {
   271  			panic("Expected precommit to be for nil")
   272  		}
   273  	} else {
   274  		if !bytes.Equal(vote.BlockID.Hash, votedBlockHash) {
   275  			panic("Expected precommit to be for proposal block")
   276  		}
   277  	}
   278  
   279  	if lockedBlockHash == nil {
   280  		if cs.LockedRound != lockRound || cs.LockedBlock != nil {
   281  			panic(fmt.Sprintf(
   282  				"Expected to be locked on nil at round %d. Got locked at round %d with block %v",
   283  				lockRound,
   284  				cs.LockedRound,
   285  				cs.LockedBlock))
   286  		}
   287  	} else {
   288  		if cs.LockedRound != lockRound || !bytes.Equal(cs.LockedBlock.Hash(), lockedBlockHash) {
   289  			panic(fmt.Sprintf(
   290  				"Expected block to be locked on round %d, got %d. Got locked block %X, expected %X",
   291  				lockRound,
   292  				cs.LockedRound,
   293  				cs.LockedBlock.Hash(),
   294  				lockedBlockHash))
   295  		}
   296  	}
   297  
   298  }
   299  
   300  func validatePrevoteAndPrecommit(
   301  	t *testing.T,
   302  	cs *State,
   303  	thisRound,
   304  	lockRound int,
   305  	privVal *validatorStub,
   306  	votedBlockHash,
   307  	lockedBlockHash []byte,
   308  ) {
   309  	// verify the prevote
   310  	validatePrevote(t, cs, thisRound, privVal, votedBlockHash)
   311  	// verify precommit
   312  	cs.mtx.Lock()
   313  	validatePrecommit(t, cs, thisRound, lockRound, privVal, votedBlockHash, lockedBlockHash)
   314  	cs.mtx.Unlock()
   315  }
   316  
   317  func subscribeToVoter(cs *State, addr []byte) <-chan tmpubsub.Message {
   318  	votesSub, err := cs.eventBus.SubscribeUnbuffered(context.Background(), testSubscriber, types.EventQueryVote)
   319  	if err != nil {
   320  		panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, types.EventQueryVote))
   321  	}
   322  	ch := make(chan tmpubsub.Message)
   323  	go func() {
   324  		for msg := range votesSub.Out() {
   325  			vote := msg.Data().(types.EventDataVote)
   326  			// we only fire for our own votes
   327  			if bytes.Equal(addr, vote.Vote.ValidatorAddress) {
   328  				ch <- msg
   329  			}
   330  		}
   331  	}()
   332  	return ch
   333  }
   334  
   335  //-------------------------------------------------------------------------------
   336  // consensus states
   337  
   338  func newState(state sm.State, pv types.PrivValidator, app abci.Application) *State {
   339  	config := cfg.ResetTestRoot("consensus_state_test")
   340  	return newStateWithConfig(config, state, pv, app)
   341  }
   342  
   343  func newStateWithConfig(
   344  	thisConfig *cfg.Config,
   345  	state sm.State,
   346  	pv types.PrivValidator,
   347  	app abci.Application,
   348  ) *State {
   349  	blockDB := dbm.NewMemDB()
   350  	return newStateWithConfigAndBlockStore(thisConfig, state, pv, app, blockDB)
   351  }
   352  
   353  func newStateWithConfigAndBlockStore(
   354  	thisConfig *cfg.Config,
   355  	state sm.State,
   356  	pv types.PrivValidator,
   357  	app abci.Application,
   358  	blockDB dbm.DB,
   359  ) *State {
   360  	// Get BlockStore
   361  	blockStore := store.NewBlockStore(blockDB)
   362  
   363  	// one for mempool, one for consensus
   364  	mtx := new(sync.Mutex)
   365  	proxyAppConnMem := abcicli.NewLocalClient(mtx, app)
   366  	proxyAppConnCon := abcicli.NewLocalClient(mtx, app)
   367  
   368  	// Make Mempool
   369  	mempool := mempl.NewCListMempool(thisConfig.Mempool, proxyAppConnMem, 0)
   370  	mempool.SetLogger(log.TestingLogger().With("module", "mempool"))
   371  	if thisConfig.Consensus.WaitForTxs() {
   372  		mempool.EnableTxsAvailable()
   373  	}
   374  
   375  	// mock the evidence pool
   376  	evpool := sm.MockEvidencePool{}
   377  
   378  	// Make State
   379  	stateDB := blockDB
   380  	sm.SaveState(stateDB, state) //for save height 1's validators info
   381  	blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
   382  	cs := NewState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool)
   383  	cs.SetLogger(log.TestingLogger().With("module", "consensus"))
   384  	cs.SetPrivValidator(pv)
   385  
   386  	eventBus := types.NewEventBus()
   387  	eventBus.SetLogger(log.TestingLogger().With("module", "events"))
   388  	eventBus.Start()
   389  	cs.SetEventBus(eventBus)
   390  	return cs
   391  }
   392  
   393  func loadPrivValidator(config *cfg.Config) *privval.FilePV {
   394  	privValidatorKeyFile := config.PrivValidatorKeyFile()
   395  	ensureDir(filepath.Dir(privValidatorKeyFile), 0700)
   396  	privValidatorStateFile := config.PrivValidatorStateFile()
   397  	privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile)
   398  	privValidator.Reset()
   399  	return privValidator
   400  }
   401  
   402  func randState(nValidators int) (*State, []*validatorStub) {
   403  	// Get State
   404  	state, privVals := randGenesisState(nValidators, false, 10)
   405  
   406  	vss := make([]*validatorStub, nValidators)
   407  
   408  	cs := newState(state, privVals[0], counter.NewApplication(true))
   409  
   410  	for i := 0; i < nValidators; i++ {
   411  		vss[i] = newValidatorStub(privVals[i], i)
   412  	}
   413  	// since cs1 starts at 1
   414  	incrementHeight(vss[1:]...)
   415  
   416  	return cs, vss
   417  }
   418  
   419  //-------------------------------------------------------------------------------
   420  
   421  func ensureNoNewEvent(ch <-chan tmpubsub.Message, timeout time.Duration,
   422  	errorMessage string) {
   423  	select {
   424  	case <-time.After(timeout):
   425  		break
   426  	case <-ch:
   427  		panic(errorMessage)
   428  	}
   429  }
   430  
   431  func ensureNoNewEventOnChannel(ch <-chan tmpubsub.Message) {
   432  	ensureNoNewEvent(
   433  		ch,
   434  		ensureTimeout,
   435  		"We should be stuck waiting, not receiving new event on the channel")
   436  }
   437  
   438  func ensureNoNewRoundStep(stepCh <-chan tmpubsub.Message) {
   439  	ensureNoNewEvent(
   440  		stepCh,
   441  		ensureTimeout,
   442  		"We should be stuck waiting, not receiving NewRoundStep event")
   443  }
   444  
   445  func ensureNoNewUnlock(unlockCh <-chan tmpubsub.Message) {
   446  	ensureNoNewEvent(
   447  		unlockCh,
   448  		ensureTimeout,
   449  		"We should be stuck waiting, not receiving Unlock event")
   450  }
   451  
   452  func ensureNoNewTimeout(stepCh <-chan tmpubsub.Message, timeout int64) {
   453  	timeoutDuration := time.Duration(timeout*10) * time.Nanosecond
   454  	ensureNoNewEvent(
   455  		stepCh,
   456  		timeoutDuration,
   457  		"We should be stuck waiting, not receiving NewTimeout event")
   458  }
   459  
   460  func ensureNewEvent(ch <-chan tmpubsub.Message, height int64, round int, timeout time.Duration, errorMessage string) {
   461  	select {
   462  	case <-time.After(timeout):
   463  		panic(errorMessage)
   464  	case msg := <-ch:
   465  		roundStateEvent, ok := msg.Data().(types.EventDataRoundState)
   466  		if !ok {
   467  			panic(fmt.Sprintf("expected a EventDataRoundState, got %T. Wrong subscription channel?",
   468  				msg.Data()))
   469  		}
   470  		if roundStateEvent.Height != height {
   471  			panic(fmt.Sprintf("expected height %v, got %v", height, roundStateEvent.Height))
   472  		}
   473  		if roundStateEvent.Round != round {
   474  			panic(fmt.Sprintf("expected round %v, got %v", round, roundStateEvent.Round))
   475  		}
   476  		// TODO: We could check also for a step at this point!
   477  	}
   478  }
   479  
   480  func ensureNewRound(roundCh <-chan tmpubsub.Message, height int64, round int) {
   481  	select {
   482  	case <-time.After(ensureTimeout):
   483  		panic("Timeout expired while waiting for NewRound event")
   484  	case msg := <-roundCh:
   485  		newRoundEvent, ok := msg.Data().(types.EventDataNewRound)
   486  		if !ok {
   487  			panic(fmt.Sprintf("expected a EventDataNewRound, got %T. Wrong subscription channel?",
   488  				msg.Data()))
   489  		}
   490  		if newRoundEvent.Height != height {
   491  			panic(fmt.Sprintf("expected height %v, got %v", height, newRoundEvent.Height))
   492  		}
   493  		if newRoundEvent.Round != round {
   494  			panic(fmt.Sprintf("expected round %v, got %v", round, newRoundEvent.Round))
   495  		}
   496  	}
   497  }
   498  
   499  func ensureNewTimeout(timeoutCh <-chan tmpubsub.Message, height int64, round int, timeout int64) {
   500  	timeoutDuration := time.Duration(timeout*10) * time.Nanosecond
   501  	ensureNewEvent(timeoutCh, height, round, timeoutDuration,
   502  		"Timeout expired while waiting for NewTimeout event")
   503  }
   504  
   505  func ensureNewProposal(proposalCh <-chan tmpubsub.Message, height int64, round int) {
   506  	select {
   507  	case <-time.After(ensureTimeout):
   508  		panic("Timeout expired while waiting for NewProposal event")
   509  	case msg := <-proposalCh:
   510  		proposalEvent, ok := msg.Data().(types.EventDataCompleteProposal)
   511  		if !ok {
   512  			panic(fmt.Sprintf("expected a EventDataCompleteProposal, got %T. Wrong subscription channel?",
   513  				msg.Data()))
   514  		}
   515  		if proposalEvent.Height != height {
   516  			panic(fmt.Sprintf("expected height %v, got %v", height, proposalEvent.Height))
   517  		}
   518  		if proposalEvent.Round != round {
   519  			panic(fmt.Sprintf("expected round %v, got %v", round, proposalEvent.Round))
   520  		}
   521  	}
   522  }
   523  
   524  func ensureNewValidBlock(validBlockCh <-chan tmpubsub.Message, height int64, round int) {
   525  	ensureNewEvent(validBlockCh, height, round, ensureTimeout,
   526  		"Timeout expired while waiting for NewValidBlock event")
   527  }
   528  
   529  func ensureNewBlock(blockCh <-chan tmpubsub.Message, height int64) {
   530  	select {
   531  	case <-time.After(ensureTimeout):
   532  		panic("Timeout expired while waiting for NewBlock event")
   533  	case msg := <-blockCh:
   534  		blockEvent, ok := msg.Data().(types.EventDataNewBlock)
   535  		if !ok {
   536  			panic(fmt.Sprintf("expected a EventDataNewBlock, got %T. Wrong subscription channel?",
   537  				msg.Data()))
   538  		}
   539  		if blockEvent.Block.Height != height {
   540  			panic(fmt.Sprintf("expected height %v, got %v", height, blockEvent.Block.Height))
   541  		}
   542  	}
   543  }
   544  
   545  func ensureNewBlockHeader(blockCh <-chan tmpubsub.Message, height int64, blockHash tmbytes.HexBytes) {
   546  	select {
   547  	case <-time.After(ensureTimeout):
   548  		panic("Timeout expired while waiting for NewBlockHeader event")
   549  	case msg := <-blockCh:
   550  		blockHeaderEvent, ok := msg.Data().(types.EventDataNewBlockHeader)
   551  		if !ok {
   552  			panic(fmt.Sprintf("expected a EventDataNewBlockHeader, got %T. Wrong subscription channel?",
   553  				msg.Data()))
   554  		}
   555  		if blockHeaderEvent.Header.Height != height {
   556  			panic(fmt.Sprintf("expected height %v, got %v", height, blockHeaderEvent.Header.Height))
   557  		}
   558  		if !bytes.Equal(blockHeaderEvent.Header.Hash(), blockHash) {
   559  			panic(fmt.Sprintf("expected header %X, got %X", blockHash, blockHeaderEvent.Header.Hash()))
   560  		}
   561  	}
   562  }
   563  
   564  func ensureNewUnlock(unlockCh <-chan tmpubsub.Message, height int64, round int) {
   565  	ensureNewEvent(unlockCh, height, round, ensureTimeout,
   566  		"Timeout expired while waiting for NewUnlock event")
   567  }
   568  
   569  func ensureProposal(proposalCh <-chan tmpubsub.Message, height int64, round int, propID types.BlockID) {
   570  	select {
   571  	case <-time.After(ensureTimeout):
   572  		panic("Timeout expired while waiting for NewProposal event")
   573  	case msg := <-proposalCh:
   574  		proposalEvent, ok := msg.Data().(types.EventDataCompleteProposal)
   575  		if !ok {
   576  			panic(fmt.Sprintf("expected a EventDataCompleteProposal, got %T. Wrong subscription channel?",
   577  				msg.Data()))
   578  		}
   579  		if proposalEvent.Height != height {
   580  			panic(fmt.Sprintf("expected height %v, got %v", height, proposalEvent.Height))
   581  		}
   582  		if proposalEvent.Round != round {
   583  			panic(fmt.Sprintf("expected round %v, got %v", round, proposalEvent.Round))
   584  		}
   585  		if !proposalEvent.BlockID.Equals(propID) {
   586  			panic("Proposed block does not match expected block")
   587  		}
   588  	}
   589  }
   590  
   591  func ensurePrecommit(voteCh <-chan tmpubsub.Message, height int64, round int) {
   592  	ensureVote(voteCh, height, round, types.PrecommitType)
   593  }
   594  
   595  func ensurePrevote(voteCh <-chan tmpubsub.Message, height int64, round int) {
   596  	ensureVote(voteCh, height, round, types.PrevoteType)
   597  }
   598  
   599  func ensureVote(voteCh <-chan tmpubsub.Message, height int64, round int,
   600  	voteType types.SignedMsgType) {
   601  	select {
   602  	case <-time.After(ensureTimeout):
   603  		panic("Timeout expired while waiting for NewVote event")
   604  	case msg := <-voteCh:
   605  		voteEvent, ok := msg.Data().(types.EventDataVote)
   606  		if !ok {
   607  			panic(fmt.Sprintf("expected a EventDataVote, got %T. Wrong subscription channel?",
   608  				msg.Data()))
   609  		}
   610  		vote := voteEvent.Vote
   611  		if vote.Height != height {
   612  			panic(fmt.Sprintf("expected height %v, got %v", height, vote.Height))
   613  		}
   614  		if vote.Round != round {
   615  			panic(fmt.Sprintf("expected round %v, got %v", round, vote.Round))
   616  		}
   617  		if vote.Type != voteType {
   618  			panic(fmt.Sprintf("expected type %v, got %v", voteType, vote.Type))
   619  		}
   620  	}
   621  }
   622  
   623  func ensureNewEventOnChannel(ch <-chan tmpubsub.Message) {
   624  	select {
   625  	case <-time.After(ensureTimeout):
   626  		panic("Timeout expired while waiting for new activity on the channel")
   627  	case <-ch:
   628  	}
   629  }
   630  
   631  //-------------------------------------------------------------------------------
   632  // consensus nets
   633  
   634  // consensusLogger is a TestingLogger which uses a different
   635  // color for each validator ("validator" key must exist).
   636  func consensusLogger() log.Logger {
   637  	return log.TestingLoggerWithColorFn(func(keyvals ...interface{}) term.FgBgColor {
   638  		for i := 0; i < len(keyvals)-1; i += 2 {
   639  			if keyvals[i] == "validator" {
   640  				return term.FgBgColor{Fg: term.Color(uint8(keyvals[i+1].(int) + 1))}
   641  			}
   642  		}
   643  		return term.FgBgColor{}
   644  	}).With("module", "consensus")
   645  }
   646  
   647  func randConsensusNet(nValidators int, testName string, tickerFunc func() TimeoutTicker,
   648  	appFunc func() abci.Application, configOpts ...func(*cfg.Config)) ([]*State, cleanupFunc) {
   649  	genDoc, privVals := randGenesisDoc(nValidators, false, 30)
   650  	css := make([]*State, nValidators)
   651  	logger := consensusLogger()
   652  	configRootDirs := make([]string, 0, nValidators)
   653  	for i := 0; i < nValidators; i++ {
   654  		stateDB := dbm.NewMemDB() // each state needs its own db
   655  		state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
   656  		thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
   657  		configRootDirs = append(configRootDirs, thisConfig.RootDir)
   658  		for _, opt := range configOpts {
   659  			opt(thisConfig)
   660  		}
   661  		ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
   662  		app := appFunc()
   663  		vals := types.TM2PB.ValidatorUpdates(state.Validators)
   664  		app.InitChain(abci.RequestInitChain{Validators: vals})
   665  
   666  		css[i] = newStateWithConfigAndBlockStore(thisConfig, state, privVals[i], app, stateDB)
   667  		css[i].SetTimeoutTicker(tickerFunc())
   668  		css[i].SetLogger(logger.With("validator", i, "module", "consensus"))
   669  	}
   670  	return css, func() {
   671  		for _, dir := range configRootDirs {
   672  			os.RemoveAll(dir)
   673  		}
   674  	}
   675  }
   676  
   677  // nPeers = nValidators + nNotValidator
   678  func randConsensusNetWithPeers(
   679  	nValidators,
   680  	nPeers int,
   681  	testName string,
   682  	tickerFunc func() TimeoutTicker,
   683  	appFunc func(string) abci.Application,
   684  ) ([]*State, *types.GenesisDoc, *cfg.Config, cleanupFunc) {
   685  	genDoc, privVals := randGenesisDoc(nValidators, false, testMinPower)
   686  	css := make([]*State, nPeers)
   687  	logger := consensusLogger()
   688  	var peer0Config *cfg.Config
   689  	configRootDirs := make([]string, 0, nPeers)
   690  	for i := 0; i < nPeers; i++ {
   691  		stateDB := dbm.NewMemDB() // each state needs its own db
   692  		state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
   693  		thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
   694  		configRootDirs = append(configRootDirs, thisConfig.RootDir)
   695  		ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
   696  		if i == 0 {
   697  			peer0Config = thisConfig
   698  		}
   699  		var privVal types.PrivValidator
   700  		if i < nValidators {
   701  			privVal = privVals[i]
   702  		} else {
   703  			tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
   704  			if err != nil {
   705  				panic(err)
   706  			}
   707  			tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
   708  			if err != nil {
   709  				panic(err)
   710  			}
   711  
   712  			privVal = privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
   713  		}
   714  
   715  		app := appFunc(path.Join(config.DBDir(), fmt.Sprintf("%s_%d", testName, i)))
   716  		vals := types.TM2PB.ValidatorUpdates(state.Validators)
   717  		if _, ok := app.(*kvstore.PersistentKVStoreApplication); ok {
   718  			// simulate handshake, receive app version. If don't do this, replay test will fail
   719  			state.Version.Consensus.App = kvstore.ProtocolVersion
   720  		}
   721  		app.InitChain(abci.RequestInitChain{Validators: vals})
   722  		//sm.SaveState(stateDB,state)	//height 1's validatorsInfo already saved in LoadStateFromDBOrGenesisDoc above
   723  
   724  		css[i] = newStateWithConfig(thisConfig, state, privVal, app)
   725  		css[i].SetTimeoutTicker(tickerFunc())
   726  		css[i].SetLogger(logger.With("validator", i, "module", "consensus"))
   727  	}
   728  	return css, genDoc, peer0Config, func() {
   729  		for _, dir := range configRootDirs {
   730  			os.RemoveAll(dir)
   731  		}
   732  	}
   733  }
   734  
   735  func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int {
   736  	for i, s := range switches {
   737  		if peer.NodeInfo().ID() == s.NodeInfo().ID() {
   738  			return i
   739  		}
   740  	}
   741  	panic("didnt find peer in switches")
   742  }
   743  
   744  //-------------------------------------------------------------------------------
   745  // genesis
   746  
   747  func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) {
   748  	validators := make([]types.GenesisValidator, numValidators)
   749  	privValidators := make([]types.PrivValidator, numValidators)
   750  	for i := 0; i < numValidators; i++ {
   751  		val, privVal := types.RandValidator(randPower, minPower)
   752  		validators[i] = types.GenesisValidator{
   753  			PubKey: val.PubKey,
   754  			Power:  val.VotingPower,
   755  		}
   756  		privValidators[i] = privVal
   757  	}
   758  	sort.Sort(types.PrivValidatorsByAddress(privValidators))
   759  
   760  	return &types.GenesisDoc{
   761  		GenesisTime: tmtime.Now(),
   762  		ChainID:     config.ChainID(),
   763  		Validators:  validators,
   764  	}, privValidators
   765  }
   766  
   767  func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) {
   768  	genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
   769  	s0, _ := sm.MakeGenesisState(genDoc)
   770  	return s0, privValidators
   771  }
   772  
   773  //------------------------------------
   774  // mock ticker
   775  
   776  func newMockTickerFunc(onlyOnce bool) func() TimeoutTicker {
   777  	return func() TimeoutTicker {
   778  		return &mockTicker{
   779  			c:        make(chan timeoutInfo, 10),
   780  			onlyOnce: onlyOnce,
   781  		}
   782  	}
   783  }
   784  
   785  // mock ticker only fires on RoundStepNewHeight
   786  // and only once if onlyOnce=true
   787  type mockTicker struct {
   788  	c chan timeoutInfo
   789  
   790  	mtx      sync.Mutex
   791  	onlyOnce bool
   792  	fired    bool
   793  }
   794  
   795  func (m *mockTicker) Start() error {
   796  	return nil
   797  }
   798  
   799  func (m *mockTicker) Stop() error {
   800  	return nil
   801  }
   802  
   803  func (m *mockTicker) ScheduleTimeout(ti timeoutInfo) {
   804  	m.mtx.Lock()
   805  	defer m.mtx.Unlock()
   806  	if m.onlyOnce && m.fired {
   807  		return
   808  	}
   809  	if ti.Step == cstypes.RoundStepNewHeight {
   810  		m.c <- ti
   811  		m.fired = true
   812  	}
   813  }
   814  
   815  func (m *mockTicker) Chan() <-chan timeoutInfo {
   816  	return m.c
   817  }
   818  
   819  func (*mockTicker) SetLogger(log.Logger) {}
   820  
   821  //------------------------------------
   822  
   823  func newCounter() abci.Application {
   824  	return counter.NewApplication(true)
   825  }
   826  
   827  func newPersistentKVStore() abci.Application {
   828  	dir, err := ioutil.TempDir("", "persistent-kvstore")
   829  	if err != nil {
   830  		panic(err)
   831  	}
   832  	return kvstore.NewPersistentKVStoreApplication(dir)
   833  }
   834  
   835  func newPersistentKVStoreWithPath(dbDir string) abci.Application {
   836  	return kvstore.NewPersistentKVStoreApplication(dbDir)
   837  }