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