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

     1  package consensus
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	cstypes "github.com/devwanda/aphelion-staking/consensus/types"
    14  	"github.com/devwanda/aphelion-staking/libs/log"
    15  	tmpubsub "github.com/devwanda/aphelion-staking/libs/pubsub"
    16  	tmrand "github.com/devwanda/aphelion-staking/libs/rand"
    17  	p2pmock "github.com/devwanda/aphelion-staking/p2p/mock"
    18  	"github.com/devwanda/aphelion-staking/types"
    19  )
    20  
    21  /*
    22  
    23  ProposeSuite
    24  x * TestProposerSelection0 - round robin ordering, round 0
    25  x * TestProposerSelection2 - round robin ordering, round 2++
    26  x * TestEnterProposeNoValidator - timeout into prevote round
    27  x * TestEnterPropose - finish propose without timing out (we have the proposal)
    28  x * TestBadProposal - 2 vals, bad proposal (bad block state hash), should prevote and precommit nil
    29  FullRoundSuite
    30  x * TestFullRound1 - 1 val, full successful round
    31  x * TestFullRoundNil - 1 val, full round of nil
    32  x * TestFullRound2 - 2 vals, both required for full round
    33  LockSuite
    34  x * TestLockNoPOL - 2 vals, 4 rounds. one val locked, precommits nil every round except first.
    35  x * TestLockPOLRelock - 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
    36  x * TestLockPOLUnlock - 4 vals, one precommits, other 3 polka nil at next round, so we unlock and precomit nil
    37  x * TestLockPOLSafety1 - 4 vals. We shouldn't change lock based on polka at earlier round
    38  x * TestLockPOLSafety2 - 4 vals. After unlocking, we shouldn't relock based on polka at earlier round
    39    * TestNetworkLock - once +1/3 precommits, network should be locked
    40    * TestNetworkLockPOL - once +1/3 precommits, the block with more recent polka is committed
    41  SlashingSuite
    42  x * TestSlashingPrevotes - a validator prevoting twice in a round gets slashed
    43  x * TestSlashingPrecommits - a validator precomitting twice in a round gets slashed
    44  CatchupSuite
    45    * TestCatchup - if we might be behind and we've seen any 2/3 prevotes, round skip to new round, precommit, or prevote
    46  HaltSuite
    47  x * TestHalt1 - if we see +2/3 precommits after timing out into new round, we should still commit
    48  
    49  */
    50  
    51  //----------------------------------------------------------------------------------------------------
    52  // ProposeSuite
    53  
    54  func TestStateProposerSelection0(t *testing.T) {
    55  	cs1, vss := randState(4)
    56  	height, round := cs1.Height, cs1.Round
    57  
    58  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
    59  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
    60  
    61  	startTestRound(cs1, height, round)
    62  
    63  	// Wait for new round so proposer is set.
    64  	ensureNewRound(newRoundCh, height, round)
    65  
    66  	// Commit a block and ensure proposer for the next height is correct.
    67  	prop := cs1.GetRoundState().Validators.GetProposer()
    68  	pv, err := cs1.privValidator.GetPubKey()
    69  	require.NoError(t, err)
    70  	address := pv.Address()
    71  	if !bytes.Equal(prop.Address, address) {
    72  		t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address)
    73  	}
    74  
    75  	// Wait for complete proposal.
    76  	ensureNewProposal(proposalCh, height, round)
    77  
    78  	rs := cs1.GetRoundState()
    79  	signAddVotes(cs1, types.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:]...)
    80  
    81  	// Wait for new round so next validator is set.
    82  	ensureNewRound(newRoundCh, height+1, 0)
    83  
    84  	prop = cs1.GetRoundState().Validators.GetProposer()
    85  	pv1, err := vss[1].GetPubKey()
    86  	require.NoError(t, err)
    87  	addr := pv1.Address()
    88  	if !bytes.Equal(prop.Address, addr) {
    89  		panic(fmt.Sprintf("expected proposer to be validator %d. Got %X", 1, prop.Address))
    90  	}
    91  }
    92  
    93  // Now let's do it all again, but starting from round 2 instead of 0
    94  func TestStateProposerSelection2(t *testing.T) {
    95  	cs1, vss := randState(4) // test needs more work for more than 3 validators
    96  	height := cs1.Height
    97  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
    98  
    99  	// this time we jump in at round 2
   100  	incrementRound(vss[1:]...)
   101  	incrementRound(vss[1:]...)
   102  
   103  	round := 2
   104  	startTestRound(cs1, height, round)
   105  
   106  	ensureNewRound(newRoundCh, height, round) // wait for the new round
   107  
   108  	// everyone just votes nil. we get a new proposer each round
   109  	for i := 0; i < len(vss); i++ {
   110  		prop := cs1.GetRoundState().Validators.GetProposer()
   111  		pvk, err := vss[(i+round)%len(vss)].GetPubKey()
   112  		require.NoError(t, err)
   113  		addr := pvk.Address()
   114  		correctProposer := addr
   115  		if !bytes.Equal(prop.Address, correctProposer) {
   116  			panic(fmt.Sprintf(
   117  				"expected RoundState.Validators.GetProposer() to be validator %d. Got %X",
   118  				(i+2)%len(vss),
   119  				prop.Address))
   120  		}
   121  
   122  		rs := cs1.GetRoundState()
   123  		signAddVotes(cs1, types.PrecommitType, nil, rs.ProposalBlockParts.Header(), vss[1:]...)
   124  		ensureNewRound(newRoundCh, height, i+round+1) // wait for the new round event each round
   125  		incrementRound(vss[1:]...)
   126  	}
   127  
   128  }
   129  
   130  // a non-validator should timeout into the prevote round
   131  func TestStateEnterProposeNoPrivValidator(t *testing.T) {
   132  	cs, _ := randState(1)
   133  	cs.SetPrivValidator(nil)
   134  	height, round := cs.Height, cs.Round
   135  
   136  	// Listen for propose timeout event
   137  	timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose)
   138  
   139  	startTestRound(cs, height, round)
   140  
   141  	// if we're not a validator, EnterPropose should timeout
   142  	ensureNewTimeout(timeoutCh, height, round, cs.config.TimeoutPropose.Nanoseconds())
   143  
   144  	if cs.GetRoundState().Proposal != nil {
   145  		t.Error("Expected to make no proposal, since no privValidator")
   146  	}
   147  }
   148  
   149  // a validator should not timeout of the prevote round (TODO: unless the block is really big!)
   150  func TestStateEnterProposeYesPrivValidator(t *testing.T) {
   151  	cs, _ := randState(1)
   152  	height, round := cs.Height, cs.Round
   153  
   154  	// Listen for propose timeout event
   155  
   156  	timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose)
   157  	proposalCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal)
   158  
   159  	cs.enterNewRound(height, round)
   160  	cs.startRoutines(3)
   161  
   162  	ensureNewProposal(proposalCh, height, round)
   163  
   164  	// Check that Proposal, ProposalBlock, ProposalBlockParts are set.
   165  	rs := cs.GetRoundState()
   166  	if rs.Proposal == nil {
   167  		t.Error("rs.Proposal should be set")
   168  	}
   169  	if rs.ProposalBlock == nil {
   170  		t.Error("rs.ProposalBlock should be set")
   171  	}
   172  	if rs.ProposalBlockParts.Total() == 0 {
   173  		t.Error("rs.ProposalBlockParts should be set")
   174  	}
   175  
   176  	// if we're a validator, enterPropose should not timeout
   177  	ensureNoNewTimeout(timeoutCh, cs.config.TimeoutPropose.Nanoseconds())
   178  }
   179  
   180  func TestStateBadProposal(t *testing.T) {
   181  	cs1, vss := randState(2)
   182  	height, round := cs1.Height, cs1.Round
   183  	vs2 := vss[1]
   184  
   185  	partSize := types.BlockPartSizeBytes
   186  
   187  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
   188  	voteCh := subscribe(cs1.eventBus, types.EventQueryVote)
   189  
   190  	propBlock, _ := cs1.createProposalBlock() //changeProposer(t, cs1, vs2)
   191  
   192  	// make the second validator the proposer by incrementing round
   193  	round++
   194  	incrementRound(vss[1:]...)
   195  
   196  	// make the block bad by tampering with statehash
   197  	stateHash := propBlock.AppHash
   198  	if len(stateHash) == 0 {
   199  		stateHash = make([]byte, 32)
   200  	}
   201  	stateHash[0] = (stateHash[0] + 1) % 255
   202  	propBlock.AppHash = stateHash
   203  	propBlockParts := propBlock.MakePartSet(partSize)
   204  	blockID := types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()}
   205  	proposal := types.NewProposal(vs2.Height, round, -1, blockID)
   206  	if err := vs2.SignProposal(config.ChainID(), proposal); err != nil {
   207  		t.Fatal("failed to sign bad proposal", err)
   208  	}
   209  
   210  	// set the proposal block
   211  	if err := cs1.SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil {
   212  		t.Fatal(err)
   213  	}
   214  
   215  	// start the machine
   216  	startTestRound(cs1, height, round)
   217  
   218  	// wait for proposal
   219  	ensureProposal(proposalCh, height, round, blockID)
   220  
   221  	// wait for prevote
   222  	ensurePrevote(voteCh, height, round)
   223  	validatePrevote(t, cs1, round, vss[0], nil)
   224  
   225  	// add bad prevote from vs2 and wait for it
   226  	signAddVotes(cs1, types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
   227  	ensurePrevote(voteCh, height, round)
   228  
   229  	// wait for precommit
   230  	ensurePrecommit(voteCh, height, round)
   231  	validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
   232  	signAddVotes(cs1, types.PrecommitType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
   233  }
   234  
   235  //----------------------------------------------------------------------------------------------------
   236  // FullRoundSuite
   237  
   238  // propose, prevote, and precommit a block
   239  func TestStateFullRound1(t *testing.T) {
   240  	cs, vss := randState(1)
   241  	height, round := cs.Height, cs.Round
   242  
   243  	// NOTE: buffer capacity of 0 ensures we can validate prevote and last commit
   244  	// before consensus can move to the next height (and cause a race condition)
   245  	cs.eventBus.Stop()
   246  	eventBus := types.NewEventBusWithBufferCapacity(0)
   247  	eventBus.SetLogger(log.TestingLogger().With("module", "events"))
   248  	cs.SetEventBus(eventBus)
   249  	eventBus.Start()
   250  
   251  	voteCh := subscribeUnBuffered(cs.eventBus, types.EventQueryVote)
   252  	propCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal)
   253  	newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound)
   254  
   255  	// Maybe it would be better to call explicitly startRoutines(4)
   256  	startTestRound(cs, height, round)
   257  
   258  	ensureNewRound(newRoundCh, height, round)
   259  
   260  	ensureNewProposal(propCh, height, round)
   261  	propBlockHash := cs.GetRoundState().ProposalBlock.Hash()
   262  
   263  	ensurePrevote(voteCh, height, round) // wait for prevote
   264  	validatePrevote(t, cs, round, vss[0], propBlockHash)
   265  
   266  	ensurePrecommit(voteCh, height, round) // wait for precommit
   267  
   268  	// we're going to roll right into new height
   269  	ensureNewRound(newRoundCh, height+1, 0)
   270  
   271  	validateLastPrecommit(t, cs, vss[0], propBlockHash)
   272  }
   273  
   274  // nil is proposed, so prevote and precommit nil
   275  func TestStateFullRoundNil(t *testing.T) {
   276  	cs, vss := randState(1)
   277  	height, round := cs.Height, cs.Round
   278  
   279  	voteCh := subscribeUnBuffered(cs.eventBus, types.EventQueryVote)
   280  
   281  	cs.enterPrevote(height, round)
   282  	cs.startRoutines(4)
   283  
   284  	ensurePrevote(voteCh, height, round)   // prevote
   285  	ensurePrecommit(voteCh, height, round) // precommit
   286  
   287  	// should prevote and precommit nil
   288  	validatePrevoteAndPrecommit(t, cs, round, -1, vss[0], nil, nil)
   289  }
   290  
   291  // run through propose, prevote, precommit commit with two validators
   292  // where the first validator has to wait for votes from the second
   293  func TestStateFullRound2(t *testing.T) {
   294  	cs1, vss := randState(2)
   295  	vs2 := vss[1]
   296  	height, round := cs1.Height, cs1.Round
   297  
   298  	voteCh := subscribeUnBuffered(cs1.eventBus, types.EventQueryVote)
   299  	newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock)
   300  
   301  	// start round and wait for propose and prevote
   302  	startTestRound(cs1, height, round)
   303  
   304  	ensurePrevote(voteCh, height, round) // prevote
   305  
   306  	// we should be stuck in limbo waiting for more prevotes
   307  	rs := cs1.GetRoundState()
   308  	propBlockHash, propPartsHeader := rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header()
   309  
   310  	// prevote arrives from vs2:
   311  	signAddVotes(cs1, types.PrevoteType, propBlockHash, propPartsHeader, vs2)
   312  	ensurePrevote(voteCh, height, round) // prevote
   313  
   314  	ensurePrecommit(voteCh, height, round) //precommit
   315  	// the proposed block should now be locked and our precommit added
   316  	validatePrecommit(t, cs1, 0, 0, vss[0], propBlockHash, propBlockHash)
   317  
   318  	// we should be stuck in limbo waiting for more precommits
   319  
   320  	// precommit arrives from vs2:
   321  	signAddVotes(cs1, types.PrecommitType, propBlockHash, propPartsHeader, vs2)
   322  	ensurePrecommit(voteCh, height, round)
   323  
   324  	// wait to finish commit, propose in next height
   325  	ensureNewBlock(newBlockCh, height)
   326  }
   327  
   328  //------------------------------------------------------------------------------------------
   329  // LockSuite
   330  
   331  // two validators, 4 rounds.
   332  // two vals take turns proposing. val1 locks on first one, precommits nil on everything else
   333  func TestStateLockNoPOL(t *testing.T) {
   334  	cs1, vss := randState(2)
   335  	vs2 := vss[1]
   336  	height, round := cs1.Height, cs1.Round
   337  
   338  	partSize := types.BlockPartSizeBytes
   339  
   340  	timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
   341  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
   342  	voteCh := subscribeUnBuffered(cs1.eventBus, types.EventQueryVote)
   343  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
   344  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
   345  
   346  	/*
   347  		Round1 (cs1, B) // B B // B B2
   348  	*/
   349  
   350  	// start round and wait for prevote
   351  	cs1.enterNewRound(height, round)
   352  	cs1.startRoutines(0)
   353  
   354  	ensureNewRound(newRoundCh, height, round)
   355  
   356  	ensureNewProposal(proposalCh, height, round)
   357  	roundState := cs1.GetRoundState()
   358  	theBlockHash := roundState.ProposalBlock.Hash()
   359  	thePartSetHeader := roundState.ProposalBlockParts.Header()
   360  
   361  	ensurePrevote(voteCh, height, round) // prevote
   362  
   363  	// we should now be stuck in limbo forever, waiting for more prevotes
   364  	// prevote arrives from vs2:
   365  	signAddVotes(cs1, types.PrevoteType, theBlockHash, thePartSetHeader, vs2)
   366  	ensurePrevote(voteCh, height, round) // prevote
   367  
   368  	ensurePrecommit(voteCh, height, round) // precommit
   369  	// the proposed block should now be locked and our precommit added
   370  	validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash)
   371  
   372  	// we should now be stuck in limbo forever, waiting for more precommits
   373  	// lets add one for a different block
   374  	hash := make([]byte, len(theBlockHash))
   375  	copy(hash, theBlockHash)
   376  	hash[0] = (hash[0] + 1) % 255
   377  	signAddVotes(cs1, types.PrecommitType, hash, thePartSetHeader, vs2)
   378  	ensurePrecommit(voteCh, height, round) // precommit
   379  
   380  	// (note we're entering precommit for a second time this round)
   381  	// but with invalid args. then we enterPrecommitWait, and the timeout to new round
   382  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
   383  
   384  	///
   385  
   386  	round++ // moving to the next round
   387  	ensureNewRound(newRoundCh, height, round)
   388  	t.Log("#### ONTO ROUND 1")
   389  	/*
   390  		Round2 (cs1, B) // B B2
   391  	*/
   392  
   393  	incrementRound(vs2)
   394  
   395  	// now we're on a new round and not the proposer, so wait for timeout
   396  	ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds())
   397  
   398  	rs := cs1.GetRoundState()
   399  
   400  	if rs.ProposalBlock != nil {
   401  		panic("Expected proposal block to be nil")
   402  	}
   403  
   404  	// wait to finish prevote
   405  	ensurePrevote(voteCh, height, round)
   406  	// we should have prevoted our locked block
   407  	validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash())
   408  
   409  	// add a conflicting prevote from the other validator
   410  	signAddVotes(cs1, types.PrevoteType, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
   411  	ensurePrevote(voteCh, height, round)
   412  
   413  	// now we're going to enter prevote again, but with invalid args
   414  	// and then prevote wait, which should timeout. then wait for precommit
   415  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds())
   416  
   417  	ensurePrecommit(voteCh, height, round) // precommit
   418  	// the proposed block should still be locked and our precommit added
   419  	// we should precommit nil and be locked on the proposal
   420  	validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash)
   421  
   422  	// add conflicting precommit from vs2
   423  	signAddVotes(cs1, types.PrecommitType, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
   424  	ensurePrecommit(voteCh, height, round)
   425  
   426  	// (note we're entering precommit for a second time this round, but with invalid args
   427  	// then we enterPrecommitWait and timeout into NewRound
   428  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
   429  
   430  	round++ // entering new round
   431  	ensureNewRound(newRoundCh, height, round)
   432  	t.Log("#### ONTO ROUND 2")
   433  	/*
   434  		Round3 (vs2, _) // B, B2
   435  	*/
   436  
   437  	incrementRound(vs2)
   438  
   439  	ensureNewProposal(proposalCh, height, round)
   440  	rs = cs1.GetRoundState()
   441  
   442  	// now we're on a new round and are the proposer
   443  	if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) {
   444  		panic(fmt.Sprintf(
   445  			"Expected proposal block to be locked block. Got %v, Expected %v",
   446  			rs.ProposalBlock,
   447  			rs.LockedBlock))
   448  	}
   449  
   450  	ensurePrevote(voteCh, height, round) // prevote
   451  	validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash())
   452  
   453  	signAddVotes(cs1, types.PrevoteType, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
   454  	ensurePrevote(voteCh, height, round)
   455  
   456  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds())
   457  	ensurePrecommit(voteCh, height, round) // precommit
   458  
   459  	validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) // precommit nil but be locked on proposal
   460  
   461  	signAddVotes(
   462  		cs1,
   463  		types.PrecommitType,
   464  		hash,
   465  		rs.ProposalBlock.MakePartSet(partSize).Header(),
   466  		vs2) // NOTE: conflicting precommits at same height
   467  	ensurePrecommit(voteCh, height, round)
   468  
   469  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
   470  
   471  	cs2, _ := randState(2) // needed so generated block is different than locked block
   472  	// before we time out into new round, set next proposal block
   473  	prop, propBlock := decideProposal(cs2, vs2, vs2.Height, vs2.Round+1)
   474  	if prop == nil || propBlock == nil {
   475  		t.Fatal("Failed to create proposal block with vs2")
   476  	}
   477  
   478  	incrementRound(vs2)
   479  
   480  	round++ // entering new round
   481  	ensureNewRound(newRoundCh, height, round)
   482  	t.Log("#### ONTO ROUND 3")
   483  	/*
   484  		Round4 (vs2, C) // B C // B C
   485  	*/
   486  
   487  	// now we're on a new round and not the proposer
   488  	// so set the proposal block
   489  	if err := cs1.SetProposalAndBlock(prop, propBlock, propBlock.MakePartSet(partSize), ""); err != nil {
   490  		t.Fatal(err)
   491  	}
   492  
   493  	ensureNewProposal(proposalCh, height, round)
   494  	ensurePrevote(voteCh, height, round) // prevote
   495  	// prevote for locked block (not proposal)
   496  	validatePrevote(t, cs1, 3, vss[0], cs1.LockedBlock.Hash())
   497  
   498  	// prevote for proposed block
   499  	signAddVotes(cs1, types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
   500  	ensurePrevote(voteCh, height, round)
   501  
   502  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds())
   503  	ensurePrecommit(voteCh, height, round)
   504  	validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) // precommit nil but locked on proposal
   505  
   506  	signAddVotes(
   507  		cs1,
   508  		types.PrecommitType,
   509  		propBlock.Hash(),
   510  		propBlock.MakePartSet(partSize).Header(),
   511  		vs2) // NOTE: conflicting precommits at same height
   512  	ensurePrecommit(voteCh, height, round)
   513  }
   514  
   515  // 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
   516  func TestStateLockPOLRelock(t *testing.T) {
   517  	cs1, vss := randState(4)
   518  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
   519  	height, round := cs1.Height, cs1.Round
   520  
   521  	partSize := types.BlockPartSizeBytes
   522  
   523  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
   524  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
   525  	pv1, err := cs1.privValidator.GetPubKey()
   526  	require.NoError(t, err)
   527  	addr := pv1.Address()
   528  	voteCh := subscribeToVoter(cs1, addr)
   529  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
   530  	newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader)
   531  
   532  	// everything done from perspective of cs1
   533  
   534  	/*
   535  		Round1 (cs1, B) // B B B B// B nil B nil
   536  
   537  		eg. vs2 and vs4 didn't see the 2/3 prevotes
   538  	*/
   539  
   540  	// start round and wait for propose and prevote
   541  	startTestRound(cs1, height, round)
   542  
   543  	ensureNewRound(newRoundCh, height, round)
   544  	ensureNewProposal(proposalCh, height, round)
   545  	rs := cs1.GetRoundState()
   546  	theBlockHash := rs.ProposalBlock.Hash()
   547  	theBlockParts := rs.ProposalBlockParts.Header()
   548  
   549  	ensurePrevote(voteCh, height, round) // prevote
   550  
   551  	signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4)
   552  
   553  	ensurePrecommit(voteCh, height, round) // our precommit
   554  	// the proposed block should now be locked and our precommit added
   555  	validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash)
   556  
   557  	// add precommits from the rest
   558  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs4)
   559  	signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3)
   560  
   561  	// before we timeout to the new round set the new proposal
   562  	prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
   563  	propBlockParts := propBlock.MakePartSet(partSize)
   564  	propBlockHash := propBlock.Hash()
   565  
   566  	incrementRound(vs2, vs3, vs4)
   567  
   568  	// timeout to new round
   569  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
   570  
   571  	round++ // moving to the next round
   572  	//XXX: this isnt guaranteed to get there before the timeoutPropose ...
   573  	if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
   574  		t.Fatal(err)
   575  	}
   576  
   577  	ensureNewRound(newRoundCh, height, round)
   578  	t.Log("### ONTO ROUND 1")
   579  
   580  	/*
   581  		Round2 (vs2, C) // B C C C // C C C _)
   582  
   583  		cs1 changes lock!
   584  	*/
   585  
   586  	// now we're on a new round and not the proposer
   587  	// but we should receive the proposal
   588  	ensureNewProposal(proposalCh, height, round)
   589  
   590  	// go to prevote, prevote for locked block (not proposal), move on
   591  	ensurePrevote(voteCh, height, round)
   592  	validatePrevote(t, cs1, round, vss[0], theBlockHash)
   593  
   594  	// now lets add prevotes from everyone else for the new block
   595  	signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
   596  
   597  	ensurePrecommit(voteCh, height, round)
   598  	// we should have unlocked and locked on the new block
   599  	validatePrecommit(t, cs1, round, round, vss[0], propBlockHash, propBlockHash)
   600  
   601  	signAddVotes(cs1, types.PrecommitType, propBlockHash, propBlockParts.Header(), vs2, vs3)
   602  	ensureNewBlockHeader(newBlockCh, height, propBlockHash)
   603  
   604  	ensureNewRound(newRoundCh, height+1, 0)
   605  }
   606  
   607  // 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
   608  func TestStateLockPOLUnlock(t *testing.T) {
   609  	cs1, vss := randState(4)
   610  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
   611  	height, round := cs1.Height, cs1.Round
   612  
   613  	partSize := types.BlockPartSizeBytes
   614  
   615  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
   616  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
   617  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
   618  	unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
   619  	pv1, err := cs1.privValidator.GetPubKey()
   620  	require.NoError(t, err)
   621  	addr := pv1.Address()
   622  	voteCh := subscribeToVoter(cs1, addr)
   623  
   624  	// everything done from perspective of cs1
   625  
   626  	/*
   627  		Round1 (cs1, B) // B B B B // B nil B nil
   628  
   629  		eg. didn't see the 2/3 prevotes
   630  	*/
   631  
   632  	// start round and wait for propose and prevote
   633  	startTestRound(cs1, height, round)
   634  	ensureNewRound(newRoundCh, height, round)
   635  
   636  	ensureNewProposal(proposalCh, height, round)
   637  	rs := cs1.GetRoundState()
   638  	theBlockHash := rs.ProposalBlock.Hash()
   639  	theBlockParts := rs.ProposalBlockParts.Header()
   640  
   641  	ensurePrevote(voteCh, height, round)
   642  	validatePrevote(t, cs1, round, vss[0], theBlockHash)
   643  
   644  	signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4)
   645  
   646  	ensurePrecommit(voteCh, height, round)
   647  	// the proposed block should now be locked and our precommit added
   648  	validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash)
   649  
   650  	// add precommits from the rest
   651  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs4)
   652  	signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3)
   653  
   654  	// before we time out into new round, set next proposal block
   655  	prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
   656  	propBlockParts := propBlock.MakePartSet(partSize)
   657  
   658  	// timeout to new round
   659  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
   660  	rs = cs1.GetRoundState()
   661  	lockedBlockHash := rs.LockedBlock.Hash()
   662  
   663  	incrementRound(vs2, vs3, vs4)
   664  	round++ // moving to the next round
   665  
   666  	ensureNewRound(newRoundCh, height, round)
   667  	t.Log("#### ONTO ROUND 1")
   668  	/*
   669  		Round2 (vs2, C) // B nil nil nil // nil nil nil _
   670  
   671  		cs1 unlocks!
   672  	*/
   673  	//XXX: this isnt guaranteed to get there before the timeoutPropose ...
   674  	if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
   675  		t.Fatal(err)
   676  	}
   677  
   678  	ensureNewProposal(proposalCh, height, round)
   679  
   680  	// go to prevote, prevote for locked block (not proposal)
   681  	ensurePrevote(voteCh, height, round)
   682  	validatePrevote(t, cs1, round, vss[0], lockedBlockHash)
   683  	// now lets add prevotes from everyone else for nil (a polka!)
   684  	signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
   685  
   686  	// the polka makes us unlock and precommit nil
   687  	ensureNewUnlock(unlockCh, height, round)
   688  	ensurePrecommit(voteCh, height, round)
   689  
   690  	// we should have unlocked and committed nil
   691  	// NOTE: since we don't relock on nil, the lock round is -1
   692  	validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
   693  
   694  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3)
   695  	ensureNewRound(newRoundCh, height, round+1)
   696  }
   697  
   698  // 4 vals
   699  // a polka at round 1 but we miss it
   700  // then a polka at round 2 that we lock on
   701  // then we see the polka from round 1 but shouldn't unlock
   702  func TestStateLockPOLSafety1(t *testing.T) {
   703  	cs1, vss := randState(4)
   704  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
   705  	height, round := cs1.Height, cs1.Round
   706  
   707  	partSize := types.BlockPartSizeBytes
   708  
   709  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
   710  	timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
   711  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
   712  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
   713  	pv1, err := cs1.privValidator.GetPubKey()
   714  	require.NoError(t, err)
   715  	addr := pv1.Address()
   716  	voteCh := subscribeToVoter(cs1, addr)
   717  
   718  	// start round and wait for propose and prevote
   719  	startTestRound(cs1, cs1.Height, round)
   720  	ensureNewRound(newRoundCh, height, round)
   721  
   722  	ensureNewProposal(proposalCh, height, round)
   723  	rs := cs1.GetRoundState()
   724  	propBlock := rs.ProposalBlock
   725  
   726  	ensurePrevote(voteCh, height, round)
   727  	validatePrevote(t, cs1, round, vss[0], propBlock.Hash())
   728  
   729  	// the others sign a polka but we don't see it
   730  	prevotes := signVotes(types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4)
   731  
   732  	t.Logf("old prop hash %v", fmt.Sprintf("%X", propBlock.Hash()))
   733  
   734  	// we do see them precommit nil
   735  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
   736  
   737  	// cs1 precommit nil
   738  	ensurePrecommit(voteCh, height, round)
   739  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
   740  
   741  	t.Log("### ONTO ROUND 1")
   742  
   743  	prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
   744  	propBlockHash := propBlock.Hash()
   745  	propBlockParts := propBlock.MakePartSet(partSize)
   746  
   747  	incrementRound(vs2, vs3, vs4)
   748  
   749  	round++ // moving to the next round
   750  	ensureNewRound(newRoundCh, height, round)
   751  
   752  	//XXX: this isnt guaranteed to get there before the timeoutPropose ...
   753  	if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
   754  		t.Fatal(err)
   755  	}
   756  	/*Round2
   757  	// we timeout and prevote our lock
   758  	// a polka happened but we didn't see it!
   759  	*/
   760  
   761  	ensureNewProposal(proposalCh, height, round)
   762  
   763  	rs = cs1.GetRoundState()
   764  
   765  	if rs.LockedBlock != nil {
   766  		panic("we should not be locked!")
   767  	}
   768  	t.Logf("new prop hash %v", fmt.Sprintf("%X", propBlockHash))
   769  
   770  	// go to prevote, prevote for proposal block
   771  	ensurePrevote(voteCh, height, round)
   772  	validatePrevote(t, cs1, round, vss[0], propBlockHash)
   773  
   774  	// now we see the others prevote for it, so we should lock on it
   775  	signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
   776  
   777  	ensurePrecommit(voteCh, height, round)
   778  	// we should have precommitted
   779  	validatePrecommit(t, cs1, round, round, vss[0], propBlockHash, propBlockHash)
   780  
   781  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
   782  
   783  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
   784  
   785  	incrementRound(vs2, vs3, vs4)
   786  	round++ // moving to the next round
   787  
   788  	ensureNewRound(newRoundCh, height, round)
   789  
   790  	t.Log("### ONTO ROUND 2")
   791  	/*Round3
   792  	we see the polka from round 1 but we shouldn't unlock!
   793  	*/
   794  
   795  	// timeout of propose
   796  	ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds())
   797  
   798  	// finish prevote
   799  	ensurePrevote(voteCh, height, round)
   800  	// we should prevote what we're locked on
   801  	validatePrevote(t, cs1, round, vss[0], propBlockHash)
   802  
   803  	newStepCh := subscribe(cs1.eventBus, types.EventQueryNewRoundStep)
   804  
   805  	// before prevotes from the previous round are added
   806  	// add prevotes from the earlier round
   807  	addVotes(cs1, prevotes...)
   808  
   809  	t.Log("Done adding prevotes!")
   810  
   811  	ensureNoNewRoundStep(newStepCh)
   812  }
   813  
   814  // 4 vals.
   815  // polka P0 at R0, P1 at R1, and P2 at R2,
   816  // we lock on P0 at R0, don't see P1, and unlock using P2 at R2
   817  // then we should make sure we don't lock using P1
   818  
   819  // What we want:
   820  // dont see P0, lock on P1 at R1, dont unlock using P0 at R2
   821  func TestStateLockPOLSafety2(t *testing.T) {
   822  	cs1, vss := randState(4)
   823  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
   824  	height, round := cs1.Height, cs1.Round
   825  
   826  	partSize := types.BlockPartSizeBytes
   827  
   828  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
   829  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
   830  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
   831  	unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
   832  	pv1, err := cs1.privValidator.GetPubKey()
   833  	require.NoError(t, err)
   834  	addr := pv1.Address()
   835  	voteCh := subscribeToVoter(cs1, addr)
   836  
   837  	// the block for R0: gets polkad but we miss it
   838  	// (even though we signed it, shhh)
   839  	_, propBlock0 := decideProposal(cs1, vss[0], height, round)
   840  	propBlockHash0 := propBlock0.Hash()
   841  	propBlockParts0 := propBlock0.MakePartSet(partSize)
   842  	propBlockID0 := types.BlockID{Hash: propBlockHash0, PartsHeader: propBlockParts0.Header()}
   843  
   844  	// the others sign a polka but we don't see it
   845  	prevotes := signVotes(types.PrevoteType, propBlockHash0, propBlockParts0.Header(), vs2, vs3, vs4)
   846  
   847  	// the block for round 1
   848  	prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
   849  	propBlockHash1 := propBlock1.Hash()
   850  	propBlockParts1 := propBlock1.MakePartSet(partSize)
   851  
   852  	incrementRound(vs2, vs3, vs4)
   853  
   854  	round++ // moving to the next round
   855  	t.Log("### ONTO Round 1")
   856  	// jump in at round 1
   857  	startTestRound(cs1, height, round)
   858  	ensureNewRound(newRoundCh, height, round)
   859  
   860  	if err := cs1.SetProposalAndBlock(prop1, propBlock1, propBlockParts1, "some peer"); err != nil {
   861  		t.Fatal(err)
   862  	}
   863  	ensureNewProposal(proposalCh, height, round)
   864  
   865  	ensurePrevote(voteCh, height, round)
   866  	validatePrevote(t, cs1, round, vss[0], propBlockHash1)
   867  
   868  	signAddVotes(cs1, types.PrevoteType, propBlockHash1, propBlockParts1.Header(), vs2, vs3, vs4)
   869  
   870  	ensurePrecommit(voteCh, height, round)
   871  	// the proposed block should now be locked and our precommit added
   872  	validatePrecommit(t, cs1, round, round, vss[0], propBlockHash1, propBlockHash1)
   873  
   874  	// add precommits from the rest
   875  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs4)
   876  	signAddVotes(cs1, types.PrecommitType, propBlockHash1, propBlockParts1.Header(), vs3)
   877  
   878  	incrementRound(vs2, vs3, vs4)
   879  
   880  	// timeout of precommit wait to new round
   881  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
   882  
   883  	round++ // moving to the next round
   884  	// in round 2 we see the polkad block from round 0
   885  	newProp := types.NewProposal(height, round, 0, propBlockID0)
   886  	if err := vs3.SignProposal(config.ChainID(), newProp); err != nil {
   887  		t.Fatal(err)
   888  	}
   889  	if err := cs1.SetProposalAndBlock(newProp, propBlock0, propBlockParts0, "some peer"); err != nil {
   890  		t.Fatal(err)
   891  	}
   892  
   893  	// Add the pol votes
   894  	addVotes(cs1, prevotes...)
   895  
   896  	ensureNewRound(newRoundCh, height, round)
   897  	t.Log("### ONTO Round 2")
   898  	/*Round2
   899  	// now we see the polka from round 1, but we shouldnt unlock
   900  	*/
   901  	ensureNewProposal(proposalCh, height, round)
   902  
   903  	ensureNoNewUnlock(unlockCh)
   904  	ensurePrevote(voteCh, height, round)
   905  	validatePrevote(t, cs1, round, vss[0], propBlockHash1)
   906  
   907  }
   908  
   909  // 4 vals.
   910  // polka P0 at R0 for B0. We lock B0 on P0 at R0. P0 unlocks value at R1.
   911  
   912  // What we want:
   913  // P0 proposes B0 at R3.
   914  func TestProposeValidBlock(t *testing.T) {
   915  	cs1, vss := randState(4)
   916  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
   917  	height, round := cs1.Height, cs1.Round
   918  
   919  	partSize := types.BlockPartSizeBytes
   920  
   921  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
   922  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
   923  	timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
   924  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
   925  	unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
   926  	pv1, err := cs1.privValidator.GetPubKey()
   927  	require.NoError(t, err)
   928  	addr := pv1.Address()
   929  	voteCh := subscribeToVoter(cs1, addr)
   930  
   931  	// start round and wait for propose and prevote
   932  	startTestRound(cs1, cs1.Height, round)
   933  	ensureNewRound(newRoundCh, height, round)
   934  
   935  	ensureNewProposal(proposalCh, height, round)
   936  	rs := cs1.GetRoundState()
   937  	propBlock := rs.ProposalBlock
   938  	propBlockHash := propBlock.Hash()
   939  
   940  	ensurePrevote(voteCh, height, round)
   941  	validatePrevote(t, cs1, round, vss[0], propBlockHash)
   942  
   943  	// the others sign a polka
   944  	signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4)
   945  
   946  	ensurePrecommit(voteCh, height, round)
   947  	// we should have precommitted
   948  	validatePrecommit(t, cs1, round, round, vss[0], propBlockHash, propBlockHash)
   949  
   950  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
   951  
   952  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
   953  
   954  	incrementRound(vs2, vs3, vs4)
   955  	round++ // moving to the next round
   956  
   957  	ensureNewRound(newRoundCh, height, round)
   958  
   959  	t.Log("### ONTO ROUND 2")
   960  
   961  	// timeout of propose
   962  	ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds())
   963  
   964  	ensurePrevote(voteCh, height, round)
   965  	validatePrevote(t, cs1, round, vss[0], propBlockHash)
   966  
   967  	signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
   968  
   969  	ensureNewUnlock(unlockCh, height, round)
   970  
   971  	ensurePrecommit(voteCh, height, round)
   972  	// we should have precommitted
   973  	validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
   974  
   975  	incrementRound(vs2, vs3, vs4)
   976  	incrementRound(vs2, vs3, vs4)
   977  
   978  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
   979  
   980  	round += 2 // moving to the next round
   981  
   982  	ensureNewRound(newRoundCh, height, round)
   983  	t.Log("### ONTO ROUND 3")
   984  
   985  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
   986  
   987  	round++ // moving to the next round
   988  
   989  	ensureNewRound(newRoundCh, height, round)
   990  
   991  	t.Log("### ONTO ROUND 4")
   992  
   993  	ensureNewProposal(proposalCh, height, round)
   994  
   995  	rs = cs1.GetRoundState()
   996  	assert.True(t, bytes.Equal(rs.ProposalBlock.Hash(), propBlockHash))
   997  	assert.True(t, bytes.Equal(rs.ProposalBlock.Hash(), rs.ValidBlock.Hash()))
   998  	assert.True(t, rs.Proposal.POLRound == rs.ValidRound)
   999  	assert.True(t, bytes.Equal(rs.Proposal.BlockID.Hash, rs.ValidBlock.Hash()))
  1000  }
  1001  
  1002  // What we want:
  1003  // P0 miss to lock B but set valid block to B after receiving delayed prevote.
  1004  func TestSetValidBlockOnDelayedPrevote(t *testing.T) {
  1005  	cs1, vss := randState(4)
  1006  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1007  	height, round := cs1.Height, cs1.Round
  1008  
  1009  	partSize := types.BlockPartSizeBytes
  1010  
  1011  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  1012  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  1013  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1014  	validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
  1015  	pv1, err := cs1.privValidator.GetPubKey()
  1016  	require.NoError(t, err)
  1017  	addr := pv1.Address()
  1018  	voteCh := subscribeToVoter(cs1, addr)
  1019  
  1020  	// start round and wait for propose and prevote
  1021  	startTestRound(cs1, cs1.Height, round)
  1022  	ensureNewRound(newRoundCh, height, round)
  1023  
  1024  	ensureNewProposal(proposalCh, height, round)
  1025  	rs := cs1.GetRoundState()
  1026  	propBlock := rs.ProposalBlock
  1027  	propBlockHash := propBlock.Hash()
  1028  	propBlockParts := propBlock.MakePartSet(partSize)
  1029  
  1030  	ensurePrevote(voteCh, height, round)
  1031  	validatePrevote(t, cs1, round, vss[0], propBlockHash)
  1032  
  1033  	// vs2 send prevote for propBlock
  1034  	signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2)
  1035  
  1036  	// vs3 send prevote nil
  1037  	signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs3)
  1038  
  1039  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds())
  1040  
  1041  	ensurePrecommit(voteCh, height, round)
  1042  	// we should have precommitted
  1043  	validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
  1044  
  1045  	rs = cs1.GetRoundState()
  1046  
  1047  	assert.True(t, rs.ValidBlock == nil)
  1048  	assert.True(t, rs.ValidBlockParts == nil)
  1049  	assert.True(t, rs.ValidRound == -1)
  1050  
  1051  	// vs2 send (delayed) prevote for propBlock
  1052  	signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs4)
  1053  
  1054  	ensureNewValidBlock(validBlockCh, height, round)
  1055  
  1056  	rs = cs1.GetRoundState()
  1057  
  1058  	assert.True(t, bytes.Equal(rs.ValidBlock.Hash(), propBlockHash))
  1059  	assert.True(t, rs.ValidBlockParts.Header().Equals(propBlockParts.Header()))
  1060  	assert.True(t, rs.ValidRound == round)
  1061  }
  1062  
  1063  // What we want:
  1064  // P0 miss to lock B as Proposal Block is missing, but set valid block to B after
  1065  // receiving delayed Block Proposal.
  1066  func TestSetValidBlockOnDelayedProposal(t *testing.T) {
  1067  	cs1, vss := randState(4)
  1068  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1069  	height, round := cs1.Height, cs1.Round
  1070  
  1071  	partSize := types.BlockPartSizeBytes
  1072  
  1073  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  1074  	timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
  1075  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1076  	validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
  1077  	pv1, err := cs1.privValidator.GetPubKey()
  1078  	require.NoError(t, err)
  1079  	addr := pv1.Address()
  1080  	voteCh := subscribeToVoter(cs1, addr)
  1081  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  1082  
  1083  	round++ // move to round in which P0 is not proposer
  1084  	incrementRound(vs2, vs3, vs4)
  1085  
  1086  	startTestRound(cs1, cs1.Height, round)
  1087  	ensureNewRound(newRoundCh, height, round)
  1088  
  1089  	ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds())
  1090  
  1091  	ensurePrevote(voteCh, height, round)
  1092  	validatePrevote(t, cs1, round, vss[0], nil)
  1093  
  1094  	prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
  1095  	propBlockHash := propBlock.Hash()
  1096  	propBlockParts := propBlock.MakePartSet(partSize)
  1097  
  1098  	// vs2, vs3 and vs4 send prevote for propBlock
  1099  	signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
  1100  	ensureNewValidBlock(validBlockCh, height, round)
  1101  
  1102  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds())
  1103  
  1104  	ensurePrecommit(voteCh, height, round)
  1105  	validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
  1106  
  1107  	if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
  1108  		t.Fatal(err)
  1109  	}
  1110  
  1111  	ensureNewProposal(proposalCh, height, round)
  1112  	rs := cs1.GetRoundState()
  1113  
  1114  	assert.True(t, bytes.Equal(rs.ValidBlock.Hash(), propBlockHash))
  1115  	assert.True(t, rs.ValidBlockParts.Header().Equals(propBlockParts.Header()))
  1116  	assert.True(t, rs.ValidRound == round)
  1117  }
  1118  
  1119  // 4 vals, 3 Nil Precommits at P0
  1120  // What we want:
  1121  // P0 waits for timeoutPrecommit before starting next round
  1122  func TestWaitingTimeoutOnNilPolka(t *testing.T) {
  1123  	cs1, vss := randState(4)
  1124  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1125  	height, round := cs1.Height, cs1.Round
  1126  
  1127  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  1128  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1129  
  1130  	// start round
  1131  	startTestRound(cs1, height, round)
  1132  	ensureNewRound(newRoundCh, height, round)
  1133  
  1134  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
  1135  
  1136  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
  1137  	ensureNewRound(newRoundCh, height, round+1)
  1138  }
  1139  
  1140  // 4 vals, 3 Prevotes for nil from the higher round.
  1141  // What we want:
  1142  // P0 waits for timeoutPropose in the next round before entering prevote
  1143  func TestWaitingTimeoutProposeOnNewRound(t *testing.T) {
  1144  	cs1, vss := randState(4)
  1145  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1146  	height, round := cs1.Height, cs1.Round
  1147  
  1148  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
  1149  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1150  	pv1, err := cs1.privValidator.GetPubKey()
  1151  	require.NoError(t, err)
  1152  	addr := pv1.Address()
  1153  	voteCh := subscribeToVoter(cs1, addr)
  1154  
  1155  	// start round
  1156  	startTestRound(cs1, height, round)
  1157  	ensureNewRound(newRoundCh, height, round)
  1158  
  1159  	ensurePrevote(voteCh, height, round)
  1160  
  1161  	incrementRound(vss[1:]...)
  1162  	signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
  1163  
  1164  	round++ // moving to the next round
  1165  	ensureNewRound(newRoundCh, height, round)
  1166  
  1167  	rs := cs1.GetRoundState()
  1168  	assert.True(t, rs.Step == cstypes.RoundStepPropose) // P0 does not prevote before timeoutPropose expires
  1169  
  1170  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Propose(round).Nanoseconds())
  1171  
  1172  	ensurePrevote(voteCh, height, round)
  1173  	validatePrevote(t, cs1, round, vss[0], nil)
  1174  }
  1175  
  1176  // 4 vals, 3 Precommits for nil from the higher round.
  1177  // What we want:
  1178  // P0 jump to higher round, precommit and start precommit wait
  1179  func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) {
  1180  	cs1, vss := randState(4)
  1181  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1182  	height, round := cs1.Height, cs1.Round
  1183  
  1184  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  1185  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1186  	pv1, err := cs1.privValidator.GetPubKey()
  1187  	require.NoError(t, err)
  1188  	addr := pv1.Address()
  1189  	voteCh := subscribeToVoter(cs1, addr)
  1190  
  1191  	// start round
  1192  	startTestRound(cs1, height, round)
  1193  	ensureNewRound(newRoundCh, height, round)
  1194  
  1195  	ensurePrevote(voteCh, height, round)
  1196  
  1197  	incrementRound(vss[1:]...)
  1198  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
  1199  
  1200  	round++ // moving to the next round
  1201  	ensureNewRound(newRoundCh, height, round)
  1202  
  1203  	ensurePrecommit(voteCh, height, round)
  1204  	validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
  1205  
  1206  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
  1207  
  1208  	round++ // moving to the next round
  1209  	ensureNewRound(newRoundCh, height, round)
  1210  }
  1211  
  1212  // 4 vals, 3 Prevotes for nil in the current round.
  1213  // What we want:
  1214  // P0 wait for timeoutPropose to expire before sending prevote.
  1215  func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) {
  1216  	cs1, vss := randState(4)
  1217  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1218  	height, round := cs1.Height, 1
  1219  
  1220  	timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
  1221  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1222  	pv1, err := cs1.privValidator.GetPubKey()
  1223  	require.NoError(t, err)
  1224  	addr := pv1.Address()
  1225  	voteCh := subscribeToVoter(cs1, addr)
  1226  
  1227  	// start round in which PO is not proposer
  1228  	startTestRound(cs1, height, round)
  1229  	ensureNewRound(newRoundCh, height, round)
  1230  
  1231  	incrementRound(vss[1:]...)
  1232  	signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
  1233  
  1234  	ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds())
  1235  
  1236  	ensurePrevote(voteCh, height, round)
  1237  	validatePrevote(t, cs1, round, vss[0], nil)
  1238  }
  1239  
  1240  // What we want:
  1241  // P0 emit NewValidBlock event upon receiving 2/3+ Precommit for B but hasn't received block B yet
  1242  func TestEmitNewValidBlockEventOnCommitWithoutBlock(t *testing.T) {
  1243  	cs1, vss := randState(4)
  1244  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1245  	height, round := cs1.Height, 1
  1246  
  1247  	incrementRound(vs2, vs3, vs4)
  1248  
  1249  	partSize := types.BlockPartSizeBytes
  1250  
  1251  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1252  	validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
  1253  
  1254  	_, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round)
  1255  	propBlockHash := propBlock.Hash()
  1256  	propBlockParts := propBlock.MakePartSet(partSize)
  1257  
  1258  	// start round in which PO is not proposer
  1259  	startTestRound(cs1, height, round)
  1260  	ensureNewRound(newRoundCh, height, round)
  1261  
  1262  	// vs2, vs3 and vs4 send precommit for propBlock
  1263  	signAddVotes(cs1, types.PrecommitType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
  1264  	ensureNewValidBlock(validBlockCh, height, round)
  1265  
  1266  	rs := cs1.GetRoundState()
  1267  	assert.True(t, rs.Step == cstypes.RoundStepCommit)
  1268  	assert.True(t, rs.ProposalBlock == nil)
  1269  	assert.True(t, rs.ProposalBlockParts.Header().Equals(propBlockParts.Header()))
  1270  
  1271  }
  1272  
  1273  // What we want:
  1274  // P0 receives 2/3+ Precommit for B for round 0, while being in round 1. It emits NewValidBlock event.
  1275  // After receiving block, it executes block and moves to the next height.
  1276  func TestCommitFromPreviousRound(t *testing.T) {
  1277  	cs1, vss := randState(4)
  1278  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1279  	height, round := cs1.Height, 1
  1280  
  1281  	partSize := types.BlockPartSizeBytes
  1282  
  1283  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1284  	validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
  1285  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  1286  
  1287  	prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round)
  1288  	propBlockHash := propBlock.Hash()
  1289  	propBlockParts := propBlock.MakePartSet(partSize)
  1290  
  1291  	// start round in which PO is not proposer
  1292  	startTestRound(cs1, height, round)
  1293  	ensureNewRound(newRoundCh, height, round)
  1294  
  1295  	// vs2, vs3 and vs4 send precommit for propBlock for the previous round
  1296  	signAddVotes(cs1, types.PrecommitType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
  1297  
  1298  	ensureNewValidBlock(validBlockCh, height, round)
  1299  
  1300  	rs := cs1.GetRoundState()
  1301  	assert.True(t, rs.Step == cstypes.RoundStepCommit)
  1302  	assert.True(t, rs.CommitRound == vs2.Round)
  1303  	assert.True(t, rs.ProposalBlock == nil)
  1304  	assert.True(t, rs.ProposalBlockParts.Header().Equals(propBlockParts.Header()))
  1305  
  1306  	if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
  1307  		t.Fatal(err)
  1308  	}
  1309  
  1310  	ensureNewProposal(proposalCh, height, round)
  1311  	ensureNewRound(newRoundCh, height+1, 0)
  1312  }
  1313  
  1314  type fakeTxNotifier struct {
  1315  	ch chan struct{}
  1316  }
  1317  
  1318  func (n *fakeTxNotifier) TxsAvailable() <-chan struct{} {
  1319  	return n.ch
  1320  }
  1321  
  1322  func (n *fakeTxNotifier) Notify() {
  1323  	n.ch <- struct{}{}
  1324  }
  1325  
  1326  func TestStartNextHeightCorrectly(t *testing.T) {
  1327  	config.Consensus.SkipTimeoutCommit = false
  1328  	cs1, vss := randState(4)
  1329  	cs1.txNotifier = &fakeTxNotifier{ch: make(chan struct{})}
  1330  
  1331  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1332  	height, round := cs1.Height, cs1.Round
  1333  
  1334  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  1335  	timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
  1336  
  1337  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1338  	newBlockHeader := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader)
  1339  	pv1, err := cs1.privValidator.GetPubKey()
  1340  	require.NoError(t, err)
  1341  	addr := pv1.Address()
  1342  	voteCh := subscribeToVoter(cs1, addr)
  1343  
  1344  	// start round and wait for propose and prevote
  1345  	startTestRound(cs1, height, round)
  1346  	ensureNewRound(newRoundCh, height, round)
  1347  
  1348  	ensureNewProposal(proposalCh, height, round)
  1349  	rs := cs1.GetRoundState()
  1350  	theBlockHash := rs.ProposalBlock.Hash()
  1351  	theBlockParts := rs.ProposalBlockParts.Header()
  1352  
  1353  	ensurePrevote(voteCh, height, round)
  1354  	validatePrevote(t, cs1, round, vss[0], theBlockHash)
  1355  
  1356  	signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4)
  1357  
  1358  	ensurePrecommit(voteCh, height, round)
  1359  	// the proposed block should now be locked and our precommit added
  1360  	validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash)
  1361  
  1362  	// add precommits
  1363  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2)
  1364  	signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3)
  1365  	time.Sleep(5 * time.Millisecond)
  1366  	signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs4)
  1367  
  1368  	rs = cs1.GetRoundState()
  1369  	assert.True(t, rs.TriggeredTimeoutPrecommit)
  1370  
  1371  	ensureNewBlockHeader(newBlockHeader, height, theBlockHash)
  1372  
  1373  	cs1.txNotifier.(*fakeTxNotifier).Notify()
  1374  
  1375  	ensureNewTimeout(timeoutProposeCh, height+1, round, cs1.config.Propose(round).Nanoseconds())
  1376  	rs = cs1.GetRoundState()
  1377  	assert.False(
  1378  		t,
  1379  		rs.TriggeredTimeoutPrecommit,
  1380  		"triggeredTimeoutPrecommit should be false at the beginning of each round")
  1381  }
  1382  
  1383  func TestResetTimeoutPrecommitUponNewHeight(t *testing.T) {
  1384  	config.Consensus.SkipTimeoutCommit = false
  1385  	cs1, vss := randState(4)
  1386  
  1387  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1388  	height, round := cs1.Height, cs1.Round
  1389  
  1390  	partSize := types.BlockPartSizeBytes
  1391  
  1392  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  1393  
  1394  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1395  	newBlockHeader := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader)
  1396  	pv1, err := cs1.privValidator.GetPubKey()
  1397  	require.NoError(t, err)
  1398  	addr := pv1.Address()
  1399  	voteCh := subscribeToVoter(cs1, addr)
  1400  
  1401  	// start round and wait for propose and prevote
  1402  	startTestRound(cs1, height, round)
  1403  	ensureNewRound(newRoundCh, height, round)
  1404  
  1405  	ensureNewProposal(proposalCh, height, round)
  1406  	rs := cs1.GetRoundState()
  1407  	theBlockHash := rs.ProposalBlock.Hash()
  1408  	theBlockParts := rs.ProposalBlockParts.Header()
  1409  
  1410  	ensurePrevote(voteCh, height, round)
  1411  	validatePrevote(t, cs1, round, vss[0], theBlockHash)
  1412  
  1413  	signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4)
  1414  
  1415  	ensurePrecommit(voteCh, height, round)
  1416  	validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash)
  1417  
  1418  	// add precommits
  1419  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2)
  1420  	signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3)
  1421  	signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs4)
  1422  
  1423  	ensureNewBlockHeader(newBlockHeader, height, theBlockHash)
  1424  
  1425  	prop, propBlock := decideProposal(cs1, vs2, height+1, 0)
  1426  	propBlockParts := propBlock.MakePartSet(partSize)
  1427  
  1428  	if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
  1429  		t.Fatal(err)
  1430  	}
  1431  	ensureNewProposal(proposalCh, height+1, 0)
  1432  
  1433  	rs = cs1.GetRoundState()
  1434  	assert.False(
  1435  		t,
  1436  		rs.TriggeredTimeoutPrecommit,
  1437  		"triggeredTimeoutPrecommit should be false at the beginning of each height")
  1438  }
  1439  
  1440  //------------------------------------------------------------------------------------------
  1441  // SlashingSuite
  1442  // TODO: Slashing
  1443  
  1444  /*
  1445  func TestStateSlashingPrevotes(t *testing.T) {
  1446  	cs1, vss := randState(2)
  1447  	vs2 := vss[1]
  1448  
  1449  
  1450  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  1451  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  1452  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1453  	voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  1454  
  1455  	// start round and wait for propose and prevote
  1456  	startTestRound(cs1, cs1.Height, 0)
  1457  	<-newRoundCh
  1458  	re := <-proposalCh
  1459  	<-voteCh // prevote
  1460  
  1461  	rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  1462  
  1463  	// we should now be stuck in limbo forever, waiting for more prevotes
  1464  	// add one for a different block should cause us to go into prevote wait
  1465  	hash := rs.ProposalBlock.Hash()
  1466  	hash[0] = byte(hash[0]+1) % 255
  1467  	signAddVotes(cs1, types.PrevoteType, hash, rs.ProposalBlockParts.Header(), vs2)
  1468  
  1469  	<-timeoutWaitCh
  1470  
  1471  	// NOTE: we have to send the vote for different block first so we don't just go into precommit round right
  1472  	// away and ignore more prevotes (and thus fail to slash!)
  1473  
  1474  	// add the conflicting vote
  1475  	signAddVotes(cs1, types.PrevoteType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
  1476  
  1477  	// XXX: Check for existence of Dupeout info
  1478  }
  1479  
  1480  func TestStateSlashingPrecommits(t *testing.T) {
  1481  	cs1, vss := randState(2)
  1482  	vs2 := vss[1]
  1483  
  1484  
  1485  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  1486  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  1487  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1488  	voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  1489  
  1490  	// start round and wait for propose and prevote
  1491  	startTestRound(cs1, cs1.Height, 0)
  1492  	<-newRoundCh
  1493  	re := <-proposalCh
  1494  	<-voteCh // prevote
  1495  
  1496  	// add prevote from vs2
  1497  	signAddVotes(cs1, types.PrevoteType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
  1498  
  1499  	<-voteCh // precommit
  1500  
  1501  	// we should now be stuck in limbo forever, waiting for more prevotes
  1502  	// add one for a different block should cause us to go into prevote wait
  1503  	hash := rs.ProposalBlock.Hash()
  1504  	hash[0] = byte(hash[0]+1) % 255
  1505  	signAddVotes(cs1, types.PrecommitType, hash, rs.ProposalBlockParts.Header(), vs2)
  1506  
  1507  	// NOTE: we have to send the vote for different block first so we don't just go into precommit round right
  1508  	// away and ignore more prevotes (and thus fail to slash!)
  1509  
  1510  	// add precommit from vs2
  1511  	signAddVotes(cs1, types.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
  1512  
  1513  	// XXX: Check for existence of Dupeout info
  1514  }
  1515  */
  1516  
  1517  //------------------------------------------------------------------------------------------
  1518  // CatchupSuite
  1519  
  1520  //------------------------------------------------------------------------------------------
  1521  // HaltSuite
  1522  
  1523  // 4 vals.
  1524  // we receive a final precommit after going into next round, but others might have gone to commit already!
  1525  func TestStateHalt1(t *testing.T) {
  1526  	cs1, vss := randState(4)
  1527  	vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  1528  	height, round := cs1.Height, cs1.Round
  1529  	partSize := types.BlockPartSizeBytes
  1530  
  1531  	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  1532  	timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  1533  	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  1534  	newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock)
  1535  	pv1, err := cs1.privValidator.GetPubKey()
  1536  	require.NoError(t, err)
  1537  	addr := pv1.Address()
  1538  	voteCh := subscribeToVoter(cs1, addr)
  1539  
  1540  	// start round and wait for propose and prevote
  1541  	startTestRound(cs1, height, round)
  1542  	ensureNewRound(newRoundCh, height, round)
  1543  
  1544  	ensureNewProposal(proposalCh, height, round)
  1545  	rs := cs1.GetRoundState()
  1546  	propBlock := rs.ProposalBlock
  1547  	propBlockParts := propBlock.MakePartSet(partSize)
  1548  
  1549  	ensurePrevote(voteCh, height, round)
  1550  
  1551  	signAddVotes(cs1, types.PrevoteType, propBlock.Hash(), propBlockParts.Header(), vs2, vs3, vs4)
  1552  
  1553  	ensurePrecommit(voteCh, height, round)
  1554  	// the proposed block should now be locked and our precommit added
  1555  	validatePrecommit(t, cs1, round, round, vss[0], propBlock.Hash(), propBlock.Hash())
  1556  
  1557  	// add precommits from the rest
  1558  	signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2) // didnt receive proposal
  1559  	signAddVotes(cs1, types.PrecommitType, propBlock.Hash(), propBlockParts.Header(), vs3)
  1560  	// we receive this later, but vs3 might receive it earlier and with ours will go to commit!
  1561  	precommit4 := signVote(vs4, types.PrecommitType, propBlock.Hash(), propBlockParts.Header())
  1562  
  1563  	incrementRound(vs2, vs3, vs4)
  1564  
  1565  	// timeout to new round
  1566  	ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds())
  1567  
  1568  	round++ // moving to the next round
  1569  
  1570  	ensureNewRound(newRoundCh, height, round)
  1571  	rs = cs1.GetRoundState()
  1572  
  1573  	t.Log("### ONTO ROUND 1")
  1574  	/*Round2
  1575  	// we timeout and prevote our lock
  1576  	// a polka happened but we didn't see it!
  1577  	*/
  1578  
  1579  	// go to prevote, prevote for locked block
  1580  	ensurePrevote(voteCh, height, round)
  1581  	validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash())
  1582  
  1583  	// now we receive the precommit from the previous round
  1584  	addVotes(cs1, precommit4)
  1585  
  1586  	// receiving that precommit should take us straight to commit
  1587  	ensureNewBlock(newBlockCh, height)
  1588  
  1589  	ensureNewRound(newRoundCh, height+1, 0)
  1590  }
  1591  
  1592  func TestStateOutputsBlockPartsStats(t *testing.T) {
  1593  	// create dummy peer
  1594  	cs, _ := randState(1)
  1595  	peer := p2pmock.NewPeer(nil)
  1596  
  1597  	// 1) new block part
  1598  	parts := types.NewPartSetFromData(tmrand.Bytes(100), 10)
  1599  	msg := &BlockPartMessage{
  1600  		Height: 1,
  1601  		Round:  0,
  1602  		Part:   parts.GetPart(0),
  1603  	}
  1604  
  1605  	cs.ProposalBlockParts = types.NewPartSetFromHeader(parts.Header())
  1606  	cs.handleMsg(msgInfo{msg, peer.ID()})
  1607  
  1608  	statsMessage := <-cs.statsMsgQueue
  1609  	require.Equal(t, msg, statsMessage.Msg, "")
  1610  	require.Equal(t, peer.ID(), statsMessage.PeerID, "")
  1611  
  1612  	// sending the same part from different peer
  1613  	cs.handleMsg(msgInfo{msg, "peer2"})
  1614  
  1615  	// sending the part with the same height, but different round
  1616  	msg.Round = 1
  1617  	cs.handleMsg(msgInfo{msg, peer.ID()})
  1618  
  1619  	// sending the part from the smaller height
  1620  	msg.Height = 0
  1621  	cs.handleMsg(msgInfo{msg, peer.ID()})
  1622  
  1623  	// sending the part from the bigger height
  1624  	msg.Height = 3
  1625  	cs.handleMsg(msgInfo{msg, peer.ID()})
  1626  
  1627  	select {
  1628  	case <-cs.statsMsgQueue:
  1629  		t.Errorf("should not output stats message after receiving the known block part!")
  1630  	case <-time.After(50 * time.Millisecond):
  1631  	}
  1632  
  1633  }
  1634  
  1635  func TestStateOutputVoteStats(t *testing.T) {
  1636  	cs, vss := randState(2)
  1637  	// create dummy peer
  1638  	peer := p2pmock.NewPeer(nil)
  1639  
  1640  	vote := signVote(vss[1], types.PrecommitType, []byte("test"), types.PartSetHeader{})
  1641  
  1642  	voteMessage := &VoteMessage{vote}
  1643  	cs.handleMsg(msgInfo{voteMessage, peer.ID()})
  1644  
  1645  	statsMessage := <-cs.statsMsgQueue
  1646  	require.Equal(t, voteMessage, statsMessage.Msg, "")
  1647  	require.Equal(t, peer.ID(), statsMessage.PeerID, "")
  1648  
  1649  	// sending the same part from different peer
  1650  	cs.handleMsg(msgInfo{&VoteMessage{vote}, "peer2"})
  1651  
  1652  	// sending the vote for the bigger height
  1653  	incrementHeight(vss[1])
  1654  	vote = signVote(vss[1], types.PrecommitType, []byte("test"), types.PartSetHeader{})
  1655  
  1656  	cs.handleMsg(msgInfo{&VoteMessage{vote}, peer.ID()})
  1657  
  1658  	select {
  1659  	case <-cs.statsMsgQueue:
  1660  		t.Errorf("should not output stats message after receiving the known vote or vote from bigger height")
  1661  	case <-time.After(50 * time.Millisecond):
  1662  	}
  1663  
  1664  }
  1665  
  1666  // subscribe subscribes test client to the given query and returns a channel with cap = 1.
  1667  func subscribe(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpubsub.Message {
  1668  	sub, err := eventBus.Subscribe(context.Background(), testSubscriber, q)
  1669  	if err != nil {
  1670  		panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, q))
  1671  	}
  1672  	return sub.Out()
  1673  }
  1674  
  1675  // subscribe subscribes test client to the given query and returns a channel with cap = 0.
  1676  func subscribeUnBuffered(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpubsub.Message {
  1677  	sub, err := eventBus.SubscribeUnbuffered(context.Background(), testSubscriber, q)
  1678  	if err != nil {
  1679  		panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, q))
  1680  	}
  1681  	return sub.Out()
  1682  }