fix tests: propose and full round suites

This commit is contained in:
Ethan Buchman 2015-12-12 01:28:53 -05:00
parent 736bc1f02f
commit 334cab82c2
2 changed files with 125 additions and 128 deletions

View File

@ -137,7 +137,7 @@ func addVoteToFromMany(to *ConsensusState, votes []*types.Vote, froms ...*valida
func addVoteToFrom(to *ConsensusState, from *validatorStub, vote *types.Vote) { func addVoteToFrom(to *ConsensusState, from *validatorStub, vote *types.Vote) {
valIndex, _ := to.Validators.GetByAddress(from.PrivValidator.Address) valIndex, _ := to.Validators.GetByAddress(from.PrivValidator.Address)
to.msgQueue <- msgInfo{msg: &VoteMessage{valIndex, vote}} to.peerMsgQueue <- msgInfo{msg: &VoteMessage{valIndex, vote}}
// added, err := to.TryAddVote(valIndex, vote, "") // added, err := to.TryAddVote(valIndex, vote, "")
/* /*
if _, ok := err.(*types.ErrVoteConflictingSignature); ok { if _, ok := err.(*types.ErrVoteConflictingSignature); ok {
@ -298,14 +298,20 @@ func simpleConsensusState(nValidators int) (*ConsensusState, []*validatorStub) {
cs := NewConsensusState(state, proxyAppCtxCon, blockStore, mempool) cs := NewConsensusState(state, proxyAppCtxCon, blockStore, mempool)
cs.SetPrivValidator(privVals[0]) cs.SetPrivValidator(privVals[0])
evsw := events.NewEventSwitch() // from the updateToState in NewConsensusState
cs.SetFireable(evsw)
// read off the NewHeightStep from updateToState
<-cs.NewStepCh() <-cs.NewStepCh()
evsw := events.NewEventSwitch()
cs.SetFireable(evsw)
evsw.OnStart()
go func() {
for {
<-cs.NewStepCh()
}
}()
// start the transition routines // start the transition routines
cs.startRoutines() // cs.startRoutines()
for i := 0; i < nValidators; i++ { for i := 0; i < nValidators; i++ {
vss[i] = NewValidatorStub(privVals[i]) vss[i] = NewValidatorStub(privVals[i])
@ -316,6 +322,16 @@ func simpleConsensusState(nValidators int) (*ConsensusState, []*validatorStub) {
return cs, vss return cs, vss
} }
func subscribeToEvent(cs *ConsensusState, eventID string) chan interface{} {
evsw := cs.evsw.(*events.EventSwitch)
// listen for new round
ch := make(chan interface{}, 10)
evsw.AddListenerForEvent("tester", eventID, func(data types.EventData) {
ch <- data
})
return ch
}
func randGenesisState(numValidators int, randPower bool, minPower int64) (*sm.State, []*types.PrivValidator) { func randGenesisState(numValidators int, randPower bool, minPower int64) (*sm.State, []*types.PrivValidator) {
db := dbm.NewMemDB() db := dbm.NewMemDB()
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower) genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
@ -343,3 +359,8 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
}, privValidators }, privValidators
} }
func startTestRound(cs *ConsensusState, height, round int) {
cs.EnterNewRound(height, round)
cs.startRoutines(0)
}

View File

@ -51,30 +51,33 @@ func init() {
} }
func TestProposerSelection0(t *testing.T) { func TestProposerSelection0(t *testing.T) {
cs1, vss := simpleConsensusState(3) // test needs more work for more than 3 validators cs1, vss := simpleConsensusState(4)
cs1.newStepCh = make(chan *RoundState) // so it blocks
height, round := cs1.Height, cs1.Round height, round := cs1.Height, cs1.Round
go cs1.EnterNewRound(height, round, false) newRoundCh := subscribeToEvent(cs1, types.EventStringNewRound())
proposalCh := subscribeToEvent(cs1, types.EventStringCompleteProposal())
startTestRound(cs1, height, round)
// wait for new round so proposer is set
<-newRoundCh
// lets commit a block and ensure proposer for the next height is correct // lets commit a block and ensure proposer for the next height is correct
prop := cs1.Validators.Proposer() prop := cs1.GetRoundState().Validators.Proposer()
if !bytes.Equal(prop.Address, cs1.privValidator.Address) { if !bytes.Equal(prop.Address, cs1.privValidator.Address) {
t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address) t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address)
} }
waitFor(t, cs1, height, round, RoundStepPrevote) // wait for complete proposal
<-proposalCh
signAddVoteToFromMany(types.VoteTypePrevote, cs1, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vss[1:]...) rs := cs1.GetRoundState()
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:]...)
waitFor(t, cs1, height, round, RoundStepPrecommit) // wait for new round so next validator is set
<-newRoundCh
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vss[1:]...) prop = cs1.GetRoundState().Validators.Proposer()
waitFor(t, cs1, height, round, RoundStepPrecommit)
waitFor(t, cs1, height, round+1, RoundStepPropose)
prop = cs1.Validators.Proposer()
if !bytes.Equal(prop.Address, vss[1].Address) { if !bytes.Equal(prop.Address, vss[1].Address) {
t.Fatalf("expected proposer to be validator %d. Got %X", 1, prop.Address) t.Fatalf("expected proposer to be validator %d. Got %X", 1, prop.Address)
} }
@ -82,33 +85,27 @@ func TestProposerSelection0(t *testing.T) {
// Now let's do it all again, but starting from round 2 instead of 0 // Now let's do it all again, but starting from round 2 instead of 0
func TestProposerSelection2(t *testing.T) { func TestProposerSelection2(t *testing.T) {
cs1, vss := simpleConsensusState(3) // test needs more work for more than 3 validators cs1, vss := simpleConsensusState(4) // test needs more work for more than 3 validators
cs1.newStepCh = make(chan *RoundState) // so it blocks
// listen for new round newRoundCh := subscribeToEvent(cs1, types.EventStringNewRound())
ch := make(chan struct{})
evsw := events.NewEventSwitch()
evsw.OnStart()
evsw.AddListenerForEvent("tester", types.EventStringNewRound(), func(data types.EventData) {
ch <- struct{}{}
})
cs1.SetFireable(evsw)
// this time we jump in at round 2 // this time we jump in at round 2
incrementRound(vss[1:]...) incrementRound(vss[1:]...)
incrementRound(vss[1:]...) incrementRound(vss[1:]...)
go cs1.EnterNewRound(cs1.Height, 2, false) startTestRound(cs1, cs1.Height, 2)
<-ch // wait for the new round <-newRoundCh // wait for the new round
// everyone just votes nil. we get a new proposer each round // everyone just votes nil. we get a new proposer each round
for i := 0; i < len(vss); i++ { for i := 0; i < len(vss); i++ {
prop := cs1.Validators.Proposer() prop := cs1.GetRoundState().Validators.Proposer()
if !bytes.Equal(prop.Address, vss[(i+2)%len(vss)].Address) { if !bytes.Equal(prop.Address, vss[(i+2)%len(vss)].Address) {
t.Fatalf("expected proposer to be validator %d. Got %X", (i+2)%len(vss), prop.Address) t.Fatalf("expected proposer to be validator %d. Got %X", (i+2)%len(vss), prop.Address)
} }
go nilRound(t, cs1, vss[1:]...)
<-ch // wait for the new round event each round rs := cs1.GetRoundState()
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, rs.ProposalBlockParts.Header(), vss[1:]...)
<-newRoundCh // wait for the new round event each round
incrementRound(vss[1:]...) incrementRound(vss[1:]...)
} }
@ -122,24 +119,19 @@ func TestEnterProposeNoPrivValidator(t *testing.T) {
height, round := cs.Height, cs.Round height, round := cs.Height, cs.Round
// Listen for propose timeout event // Listen for propose timeout event
timeoutEventReceived := false timeoutCh := subscribeToEvent(cs, types.EventStringTimeoutPropose())
evsw := events.NewEventSwitch()
evsw.OnStart()
evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
timeoutEventReceived = true
})
cs.SetFireable(evsw)
// starts a go routine for EnterPropose startTestRound(cs, height, round)
go cs.EnterNewRound(height, round, false)
// Wait until the prevote step
waitFor(t, cs, height, round, RoundStepPrevote)
// if we're not a validator, EnterPropose should timeout // if we're not a validator, EnterPropose should timeout
if timeoutEventReceived == false { ticker := time.NewTicker(timeoutPropose * 2)
select {
case <-timeoutCh:
case <-ticker.C:
t.Fatal("Expected EnterPropose to timeout") t.Fatal("Expected EnterPropose to timeout")
} }
if cs.GetRoundState().Proposal != nil { if cs.GetRoundState().Proposal != nil {
t.Error("Expected to make no proposal, since no privValidator") t.Error("Expected to make no proposal, since no privValidator")
} }
@ -151,19 +143,14 @@ func TestEnterProposeYesPrivValidator(t *testing.T) {
height, round := cs.Height, cs.Round height, round := cs.Height, cs.Round
// Listen for propose timeout event // Listen for propose timeout event
timeoutEventReceived := false timeoutCh := subscribeToEvent(cs, types.EventStringTimeoutPropose())
evsw := events.NewEventSwitch() proposalCh := subscribeToEvent(cs, types.EventStringCompleteProposal())
evsw.OnStart()
evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
timeoutEventReceived = true
})
cs.SetFireable(evsw)
// starts a go routine for the round //startTestRound(cs, height, round)
go cs.EnterNewRound(height, round, false) cs.EnterNewRound(height, round)
cs.startRoutines(3)
// Wait until the prevote step <-proposalCh
waitFor(t, cs, height, round, RoundStepPrevote)
// Check that Proposal, ProposalBlock, ProposalBlockParts are set. // Check that Proposal, ProposalBlock, ProposalBlockParts are set.
rs := cs.GetRoundState() rs := cs.GetRoundState()
@ -178,30 +165,28 @@ func TestEnterProposeYesPrivValidator(t *testing.T) {
} }
// if we're a validator, EnterPropose should not timeout // if we're a validator, EnterPropose should not timeout
if timeoutEventReceived == true { ticker := time.NewTicker(timeoutPropose * 2)
select {
case <-timeoutCh:
t.Fatal("Expected EnterPropose not to timeout") t.Fatal("Expected EnterPropose not to timeout")
case <-ticker.C:
} }
} }
func TestBadProposal(t *testing.T) { func TestBadProposal(t *testing.T) {
cs1, vss := simpleConsensusState(2) cs1, vss := simpleConsensusState(2)
cs1.newStepCh = make(chan *RoundState) // so it blocks
height, round := cs1.Height, cs1.Round height, round := cs1.Height, cs1.Round
cs2 := vss[1] cs2 := vss[1]
timeoutChan := make(chan struct{}) proposalCh := subscribeToEvent(cs1, types.EventStringCompleteProposal())
evsw := events.NewEventSwitch() voteCh := subscribeToEvent(cs1, types.EventStringVote())
evsw.OnStart()
evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
timeoutChan <- struct{}{}
})
evsw.AddListenerForEvent("tester", types.EventStringTimeoutWait(), func(data types.EventData) {
timeoutChan <- struct{}{}
})
cs1.SetFireable(evsw)
// make the second validator the proposer propBlock, _ := cs1.createProposalBlock() //changeProposer(t, cs1, cs2)
propBlock := changeProposer(t, cs1, cs2)
// make the second validator the proposer by incrementing round
round = round + 1
incrementRound(vss[1:]...)
// make the block bad by tampering with statehash // make the block bad by tampering with statehash
stateHash := propBlock.AppHash stateHash := propBlock.AppHash
@ -211,35 +196,31 @@ func TestBadProposal(t *testing.T) {
stateHash[0] = byte((stateHash[0] + 1) % 255) stateHash[0] = byte((stateHash[0] + 1) % 255)
propBlock.AppHash = stateHash propBlock.AppHash = stateHash
propBlockParts := propBlock.MakePartSet() propBlockParts := propBlock.MakePartSet()
proposal := types.NewProposal(cs2.Height, cs2.Round, propBlockParts.Header(), -1) proposal := types.NewProposal(cs2.Height, round, propBlockParts.Header(), -1)
if err := cs2.SignProposal(chainID, proposal); err != nil { if err := cs2.SignProposal(chainID, proposal); err != nil {
t.Fatal("failed to sign bad proposal", err) t.Fatal("failed to sign bad proposal", err)
} }
// start round // set the proposal block
go cs1.EnterNewRound(height, round, false) cs1.SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer")
// now we're on a new round and not the proposer // start the machine
waitFor(t, cs1, height, round, RoundStepPropose) startTestRound(cs1, height, round)
// so set the proposal block (and fix voting power)
cs1.mtx.Lock()
cs1.Proposal, cs1.ProposalBlock, cs1.ProposalBlockParts = proposal, propBlock, propBlockParts
fixVotingPower(t, cs1, vss[1].Address)
cs1.mtx.Unlock()
// and wait for timeout
<-timeoutChan
// go to prevote, prevote for nil (proposal is bad) // wait for proposal
waitFor(t, cs1, height, round, RoundStepPrevote) <-proposalCh
//wait for prevote
<-voteCh
validatePrevote(t, cs1, round, vss[0], nil) validatePrevote(t, cs1, round, vss[0], nil)
// add bad prevote from cs2. we should precommit nil // add bad prevote from cs2 and wait for it
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header()) signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header())
<-voteCh
waitFor(t, cs1, height, round, RoundStepPrevoteWait) // wait for precommit
<-timeoutChan <-voteCh
waitFor(t, cs1, height, round, RoundStepPrecommit)
validatePrecommit(t, cs1, round, 0, vss[0], nil, nil) validatePrecommit(t, cs1, round, 0, vss[0], nil, nil)
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header()) signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header())
@ -253,79 +234,74 @@ func TestFullRound1(t *testing.T) {
cs, vss := simpleConsensusState(1) cs, vss := simpleConsensusState(1)
height, round := cs.Height, cs.Round height, round := cs.Height, cs.Round
// starts a go routine for EnterPropose voteCh := subscribeToEvent(cs, types.EventStringVote())
go cs.EnterNewRound(height, round, false)
// wait to finish propose and prevote cs.EnterNewRound(height, round)
waitFor(t, cs, height, round, RoundStepPrevote) cs.startRoutines(5)
// we should now be in precommit <-voteCh // wait for prevote
// verify our prevote is there <-voteCh // wait for precommit
cs.mtx.Lock()
propBlockHash := cs.ProposalBlock.Hash()
cs.mtx.Unlock()
// Wait until Precommit propBlockHash := cs.GetRoundState().ProposalBlock.Hash()
waitFor(t, cs, height, round, RoundStepPrecommit)
// the proposed block should be prevoted, precommitted, and locked // the proposed block should be prevoted, precommitted, and locked
validatePrevoteAndPrecommit(t, cs, round, round, vss[0], propBlockHash, propBlockHash) validatePrevoteAndPrecommit(t, cs, round, round, vss[0], propBlockHash, propBlockHash)
} }
/*
// nil is proposed, so prevote and precommit nil // nil is proposed, so prevote and precommit nil
func TestFullRoundNil(t *testing.T) { func TestFullRoundNil(t *testing.T) {
cs, vss := simpleConsensusState(1) cs, vss := simpleConsensusState(1)
height, round := cs.Height, cs.Round height, round := cs.Height, cs.Round
// TODO: This is not easy to test now because we need receiveRoutine to start things off voteCh := subscribeToEvent(cs, types.EventStringVote())
// and we want to not be the proposer but still vote ....
// Skip the propose step cs.EnterPrevote(height, round)
cs.EnterPrevote(height, round, true) cs.startRoutines(4)
// Wait until Precommit <-voteCh // prevote
waitFor(t, cs, height, round, RoundStepPrecommit) <-voteCh // precommit
// should prevote and precommit nil // should prevote and precommit nil
validatePrevoteAndPrecommit(t, cs, round, 0, vss[0], nil, nil) validatePrevoteAndPrecommit(t, cs, round, 0, vss[0], nil, nil)
} }
*/
// run through propose, prevote, precommit commit with two validators // run through propose, prevote, precommit commit with two validators
// where the first validator has to wait for votes from the second // where the first validator has to wait for votes from the second
func TestFullRound2(t *testing.T) { func TestFullRound2(t *testing.T) {
cs1, vss := simpleConsensusState(2) cs1, vss := simpleConsensusState(2)
cs2 := vss[1] cs2 := vss[1]
cs1.newStepCh = make(chan *RoundState) // so it blocks
height, round := cs1.Height, cs1.Round height, round := cs1.Height, cs1.Round
// start round and wait for propose and prevote voteCh := subscribeToEvent(cs1, types.EventStringVote())
go cs1.EnterNewRound(height, round, false) newBlockCh := subscribeToEvent(cs1, types.EventStringNewBlock())
waitFor(t, cs1, height, round, RoundStepPrevote)
// we should now be stuck in limbo forever, waiting for more prevotes // start round and wait for propose and prevote
ensureNoNewStep(t, cs1) startTestRound(cs1, height, round)
<-voteCh // prevote
// we should be stuck in limbo waiting for more prevotes
propBlockHash, propPartsHeader := cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header() propBlockHash, propPartsHeader := cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header()
// prevote arrives from cs2: // prevote arrives from cs2:
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlockHash, propPartsHeader) signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlockHash, propPartsHeader)
<-voteCh
// wait to finish precommit <-voteCh //precommit
waitFor(t, cs1, cs1.Height, 0, RoundStepPrecommit)
// the proposed block should now be locked and our precommit added // the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, 0, 0, vss[0], propBlockHash, propBlockHash) validatePrecommit(t, cs1, 0, 0, vss[0], propBlockHash, propBlockHash)
// we should now be stuck in limbo forever, waiting for more precommits // we should be stuck in limbo waiting for more precommits
ensureNoNewStep(t, cs1)
// precommit arrives from cs2: // precommit arrives from cs2:
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlockHash, propPartsHeader) signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlockHash, propPartsHeader)
<-voteCh
// wait to finish commit, propose in next height // wait to finish commit, propose in next height
waitFor(t, cs1, height+1, 0, RoundStepNewHeight) <-newBlockCh
} }
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
@ -356,7 +332,7 @@ func TestLockNoPOL(t *testing.T) {
*/ */
// start round and wait for prevote // start round and wait for prevote
go cs1.EnterNewRound(height, 0, false) go cs1.EnterNewRound(height, 0)
waitFor(t, cs1, height, 0, RoundStepPrevote) waitFor(t, cs1, height, 0, RoundStepPrevote)
// we should now be stuck in limbo forever, waiting for more prevotes // we should now be stuck in limbo forever, waiting for more prevotes
@ -537,7 +513,7 @@ func TestLockPOLRelock(t *testing.T) {
*/ */
// start round and wait for propose and prevote // start round and wait for propose and prevote
go cs1.EnterNewRound(cs1.Height, 0, false) go cs1.EnterNewRound(cs1.Height, 0)
_, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh() _, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh()
theBlockHash := cs1.ProposalBlock.Hash() theBlockHash := cs1.ProposalBlock.Hash()
@ -681,7 +657,7 @@ func TestLockPOLUnlock(t *testing.T) {
*/ */
// start round and wait for propose and prevote // start round and wait for propose and prevote
go cs1.EnterNewRound(cs1.Height, 0, false) go cs1.EnterNewRound(cs1.Height, 0)
_, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh() _, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh()
theBlockHash := cs1.ProposalBlock.Hash() theBlockHash := cs1.ProposalBlock.Hash()
@ -799,7 +775,7 @@ func TestLockPOLSafety1(t *testing.T) {
cs1.SetFireable(evsw) cs1.SetFireable(evsw)
// start round and wait for propose and prevote // start round and wait for propose and prevote
go cs1.EnterNewRound(cs1.Height, 0, false) go cs1.EnterNewRound(cs1.Height, 0)
_, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh() _, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh()
propBlock := cs1.ProposalBlock propBlock := cs1.ProposalBlock
@ -931,7 +907,7 @@ func TestLockPOLSafety2(t *testing.T) {
cs1.SetFireable(evsw) cs1.SetFireable(evsw)
// start round and wait for propose and prevote // start round and wait for propose and prevote
go cs1.EnterNewRound(cs1.Height, 0, false) go cs1.EnterNewRound(cs1.Height, 0)
_, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh() _, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh()
theBlockHash := cs1.ProposalBlock.Hash() theBlockHash := cs1.ProposalBlock.Hash()
@ -1066,7 +1042,7 @@ func TestSlashingPrevotes(t *testing.T) {
cs1.newStepCh = make(chan *RoundState) // so it blocks cs1.newStepCh = make(chan *RoundState) // so it blocks
// start round and wait for propose and prevote // start round and wait for propose and prevote
go cs1.EnterNewRound(cs1.Height, 0, false) go cs1.EnterNewRound(cs1.Height, 0)
_, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh() _, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh()
// we should now be stuck in limbo forever, waiting for more prevotes // we should now be stuck in limbo forever, waiting for more prevotes
@ -1093,7 +1069,7 @@ func TestSlashingPrecommits(t *testing.T) {
cs1.newStepCh = make(chan *RoundState) // so it blocks cs1.newStepCh = make(chan *RoundState) // so it blocks
// start round and wait for propose and prevote // start round and wait for propose and prevote
go cs1.EnterNewRound(cs1.Height, 0, false) go cs1.EnterNewRound(cs1.Height, 0)
_, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh() _, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh()
// add prevote from cs2 // add prevote from cs2
@ -1142,7 +1118,7 @@ func TestHalt1(t *testing.T) {
cs1.SetFireable(evsw) cs1.SetFireable(evsw)
// start round and wait for propose and prevote // start round and wait for propose and prevote
go cs1.EnterNewRound(cs1.Height, 0, false) go cs1.EnterNewRound(cs1.Height, 0)
_, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh() _, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh()
theBlockHash := cs1.ProposalBlock.Hash() theBlockHash := cs1.ProposalBlock.Hash()