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