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