tendermint/consensus/state_test.go

1023 lines
34 KiB
Go
Raw Normal View History

2015-09-22 18:12:34 -07:00
package consensus
import (
"bytes"
"fmt"
"testing"
"time"
"github.com/tendermint/tendermint/config/tendermint_test"
2016-01-12 13:54:27 -08:00
//"github.com/tendermint/go-events"
2016-07-11 18:10:05 -07:00
. "github.com/tendermint/go-common"
2015-09-22 18:12:34 -07:00
"github.com/tendermint/tendermint/types"
)
func init() {
2016-05-08 15:00:58 -07:00
config = tendermint_test.ResetConfig("consensus_state_test")
}
func (tp *TimeoutParams) ensureProposeTimeout() time.Duration {
return time.Duration(tp.Propose0*2) * time.Millisecond
}
2015-09-22 18:12:34 -07:00
/*
ProposeSuite
2015-12-01 20:12:01 -08:00
x * TestProposerSelection0 - round robin ordering, round 0
x * TestProposerSelection2 - round robin ordering, round 2++
2015-09-22 18:12:34 -07:00
x * TestEnterProposeNoValidator - timeout into prevote round
x * TestEnterPropose - finish propose without timing out (we have the proposal)
x * TestBadProposal - 2 vals, bad proposal (bad block state hash), should prevote and precommit nil
FullRoundSuite
x * TestFullRound1 - 1 val, full successful round
x * TestFullRoundNil - 1 val, full round of nil
x * TestFullRound2 - 2 vals, both required for fuill round
LockSuite
x * TestLockNoPOL - 2 vals, 4 rounds. one val locked, precommits nil every round except first.
x * TestLockPOLRelock - 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
x * TestLockPOLUnlock - 4 vals, one precommits, other 3 polka nil at next round, so we unlock and precomit nil
x * TestLockPOLSafety1 - 4 vals. We shouldn't change lock based on polka at earlier round
x * TestLockPOLSafety2 - 4 vals. After unlocking, we shouldn't relock based on polka at earlier round
* TestNetworkLock - once +1/3 precommits, network should be locked
* TestNetworkLockPOL - once +1/3 precommits, the block with more recent polka is committed
SlashingSuite
x * TestSlashingPrevotes - a validator prevoting twice in a round gets slashed
x * TestSlashingPrecommits - a validator precomitting twice in a round gets slashed
CatchupSuite
* TestCatchup - if we might be behind and we've seen any 2/3 prevotes, round skip to new round, precommit, or prevote
HaltSuite
x * TestHalt1 - if we see +2/3 precommits after timing out into new round, we should still commit
*/
//----------------------------------------------------------------------------------------------------
// ProposeSuite
2015-12-01 20:12:01 -08:00
func TestProposerSelection0(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(4)
2015-12-01 20:12:01 -08:00
height, round := cs1.Height, cs1.Round
2015-10-28 10:49:35 -07:00
2016-01-18 12:18:09 -08:00
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
startTestRound(cs1, height, round)
// wait for new round so proposer is set
<-newRoundCh
2015-10-28 10:49:35 -07:00
// lets commit a block and ensure proposer for the next height is correct
prop := cs1.GetRoundState().Validators.Proposer()
2015-10-28 10:49:35 -07:00
if !bytes.Equal(prop.Address, cs1.privValidator.Address) {
2016-07-11 18:10:05 -07:00
panic(Fmt("expected proposer to be validator %d. Got %X", 0, prop.Address))
2015-10-28 10:49:35 -07:00
}
2015-12-01 20:12:01 -08:00
// wait for complete proposal
<-proposalCh
2015-12-01 20:12:01 -08:00
rs := cs1.GetRoundState()
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil, vss[1:]...)
2015-12-01 20:12:01 -08:00
// wait for new round so next validator is set
<-newRoundCh
2015-12-01 20:12:01 -08:00
prop = cs1.GetRoundState().Validators.Proposer()
2015-12-01 20:12:01 -08:00
if !bytes.Equal(prop.Address, vss[1].Address) {
2016-07-11 18:10:05 -07:00
panic(Fmt("expected proposer to be validator %d. Got %X", 1, prop.Address))
2015-10-28 10:49:35 -07:00
}
2015-12-01 20:12:01 -08:00
}
2015-10-28 10:49:35 -07:00
2015-12-01 20:12:01 -08:00
// Now let's do it all again, but starting from round 2 instead of 0
func TestProposerSelection2(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(4) // test needs more work for more than 3 validators
2015-10-28 10:49:35 -07:00
2016-01-18 12:18:09 -08:00
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
2015-10-28 10:49:35 -07:00
// this time we jump in at round 2
2015-12-01 20:12:01 -08:00
incrementRound(vss[1:]...)
incrementRound(vss[1:]...)
startTestRound(cs1, cs1.Height, 2)
<-newRoundCh // wait for the new round
2015-10-28 10:49:35 -07:00
// everyone just votes nil. we get a new proposer each round
2015-12-01 20:12:01 -08:00
for i := 0; i < len(vss); i++ {
prop := cs1.GetRoundState().Validators.Proposer()
2015-12-01 20:12:01 -08:00
if !bytes.Equal(prop.Address, vss[(i+2)%len(vss)].Address) {
2016-07-11 18:10:05 -07:00
panic(Fmt("expected proposer to be validator %d. Got %X", (i+2)%len(vss), prop.Address))
2015-10-28 10:49:35 -07:00
}
rs := cs1.GetRoundState()
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, rs.ProposalBlockParts.Header(), nil, vss[1:]...)
<-newRoundCh // wait for the new round event each round
2015-12-01 20:12:01 -08:00
incrementRound(vss[1:]...)
2015-10-28 10:49:35 -07:00
}
2015-09-22 18:12:34 -07:00
}
// a non-validator should timeout into the prevote round
func TestEnterProposeNoPrivValidator(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs, _ := randConsensusState(1)
2015-09-22 18:12:34 -07:00
cs.SetPrivValidator(nil)
2015-12-01 20:12:01 -08:00
height, round := cs.Height, cs.Round
2015-09-22 18:12:34 -07:00
2015-12-01 20:12:01 -08:00
// Listen for propose timeout event
2016-01-18 12:18:09 -08:00
timeoutCh := subscribeToEvent(cs.evsw, "tester", types.EventStringTimeoutPropose(), 1)
2015-09-22 18:12:34 -07:00
startTestRound(cs, height, round)
2015-09-22 18:12:34 -07:00
// if we're not a validator, EnterPropose should timeout
ticker := time.NewTicker(cs.timeoutParams.ensureProposeTimeout())
select {
case <-timeoutCh:
case <-ticker.C:
2016-07-11 18:10:05 -07:00
panic("Expected EnterPropose to timeout")
2015-12-01 20:12:01 -08:00
}
2015-12-01 20:12:01 -08:00
if cs.GetRoundState().Proposal != nil {
t.Error("Expected to make no proposal, since no privValidator")
2015-09-22 18:12:34 -07:00
}
}
// a validator should not timeout of the prevote round (TODO: unless the block is really big!)
func TestEnterProposeYesPrivValidator(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs, _ := randConsensusState(1)
2015-12-01 20:12:01 -08:00
height, round := cs.Height, cs.Round
2015-09-22 18:12:34 -07:00
2015-12-01 20:12:01 -08:00
// Listen for propose timeout event
2016-01-18 12:18:09 -08:00
timeoutCh := subscribeToEvent(cs.evsw, "tester", types.EventStringTimeoutPropose(), 1)
proposalCh := subscribeToEvent(cs.evsw, "tester", types.EventStringCompleteProposal(), 1)
2015-09-22 18:12:34 -07:00
cs.enterNewRound(height, round)
cs.startRoutines(3)
2015-09-22 18:12:34 -07:00
<-proposalCh
2015-09-22 18:12:34 -07:00
2015-12-01 20:12:01 -08:00
// Check that Proposal, ProposalBlock, ProposalBlockParts are set.
rs := cs.GetRoundState()
if rs.Proposal == nil {
t.Error("rs.Proposal should be set")
}
if rs.ProposalBlock == nil {
t.Error("rs.ProposalBlock should be set")
}
if rs.ProposalBlockParts.Total() == 0 {
t.Error("rs.ProposalBlockParts should be set")
}
2015-09-22 18:12:34 -07:00
// if we're a validator, enterPropose should not timeout
ticker := time.NewTicker(cs.timeoutParams.ensureProposeTimeout())
select {
case <-timeoutCh:
2016-07-11 18:10:05 -07:00
panic("Expected EnterPropose not to timeout")
case <-ticker.C:
2015-09-22 18:12:34 -07:00
}
}
func TestBadProposal(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(2)
2015-12-01 20:12:01 -08:00
height, round := cs1.Height, cs1.Round
cs2 := vss[1]
2015-09-22 18:12:34 -07:00
2016-01-18 12:18:09 -08:00
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
2015-09-22 18:12:34 -07:00
propBlock, _ := cs1.createProposalBlock() //changeProposer(t, cs1, cs2)
// make the second validator the proposer by incrementing round
round = round + 1
incrementRound(vss[1:]...)
2015-09-22 18:12:34 -07:00
// make the block bad by tampering with statehash
2015-12-01 20:12:01 -08:00
stateHash := propBlock.AppHash
if len(stateHash) == 0 {
stateHash = make([]byte, 32)
}
2015-09-22 18:12:34 -07:00
stateHash[0] = byte((stateHash[0] + 1) % 255)
2015-12-01 20:12:01 -08:00
propBlock.AppHash = stateHash
2015-09-22 18:12:34 -07:00
propBlockParts := propBlock.MakePartSet()
proposal := types.NewProposal(cs2.Height, round, propBlockParts.Header(), -1)
2016-05-08 15:00:58 -07:00
if err := cs2.SignProposal(config.GetString("chain_id"), proposal); err != nil {
2016-07-11 18:10:05 -07:00
panic("failed to sign bad proposal: " + err.Error())
2015-09-22 18:12:34 -07:00
}
// set the proposal block
cs1.SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer")
2015-09-22 18:12:34 -07:00
// start the machine
startTestRound(cs1, height, round)
2015-09-22 18:12:34 -07:00
// wait for proposal
<-proposalCh
// wait for prevote
<-voteCh
2015-12-01 20:12:01 -08:00
validatePrevote(t, cs1, round, vss[0], nil)
2015-09-22 18:12:34 -07:00
// add bad prevote from cs2 and wait for it
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh)
2015-12-01 20:12:01 -08:00
// wait for precommit
<-voteCh
2015-12-01 20:12:01 -08:00
validatePrecommit(t, cs1, round, 0, vss[0], nil, nil)
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh)
2015-09-22 18:12:34 -07:00
}
//----------------------------------------------------------------------------------------------------
2015-10-28 10:49:35 -07:00
// FullRoundSuite
2015-09-22 18:12:34 -07:00
// propose, prevote, and precommit a block
func TestFullRound1(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs, vss := randConsensusState(1)
2015-12-01 20:12:01 -08:00
height, round := cs.Height, cs.Round
2015-09-22 18:12:34 -07:00
2016-01-18 12:18:09 -08:00
voteCh := subscribeToEvent(cs.evsw, "tester", types.EventStringVote(), 1)
propCh := subscribeToEvent(cs.evsw, "tester", types.EventStringCompleteProposal(), 1)
newRoundCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewRound(), 1)
2015-12-01 20:12:01 -08:00
2015-12-13 11:56:05 -08:00
startTestRound(cs, height, round)
<-newRoundCh
// grab proposal
re := <-propCh
propBlockHash := re.(types.EventDataRoundState).RoundState.(*RoundState).ProposalBlock.Hash()
2015-09-22 18:12:34 -07:00
<-voteCh // wait for prevote
2015-12-13 11:56:05 -08:00
validatePrevote(t, cs, round, vss[0], propBlockHash)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-voteCh // wait for precommit
2015-12-01 20:12:01 -08:00
2015-12-13 11:56:05 -08:00
// we're going to roll right into new height
<-newRoundCh
2015-12-13 11:56:05 -08:00
validateLastPrecommit(t, cs, vss[0], propBlockHash)
2015-09-22 18:12:34 -07:00
}
// nil is proposed, so prevote and precommit nil
func TestFullRoundNil(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs, vss := randConsensusState(1)
2015-12-01 20:12:01 -08:00
height, round := cs.Height, cs.Round
2015-09-22 18:12:34 -07:00
2016-01-18 12:18:09 -08:00
voteCh := subscribeToEvent(cs.evsw, "tester", types.EventStringVote(), 1)
cs.enterPrevote(height, round)
cs.startRoutines(4)
2015-09-22 18:12:34 -07:00
<-voteCh // prevote
<-voteCh // precommit
2015-09-22 18:12:34 -07:00
// should prevote and precommit nil
2015-12-01 20:12:01 -08:00
validatePrevoteAndPrecommit(t, cs, round, 0, vss[0], nil, nil)
2015-09-22 18:12:34 -07:00
}
// run through propose, prevote, precommit commit with two validators
// where the first validator has to wait for votes from the second
func TestFullRound2(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(2)
2015-12-01 20:12:01 -08:00
cs2 := vss[1]
height, round := cs1.Height, cs1.Round
2015-09-22 18:12:34 -07:00
2016-01-18 12:18:09 -08:00
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlock(), 1)
2015-09-22 18:12:34 -07:00
// start round and wait for propose and prevote
startTestRound(cs1, height, round)
2015-09-22 18:12:34 -07:00
<-voteCh // prevote
// we should be stuck in limbo waiting for more prevotes
2016-07-11 17:40:48 -07:00
rs := cs1.GetRoundState()
propBlockHash, propPartsHeader := rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header()
2015-09-22 18:12:34 -07:00
// prevote arrives from cs2:
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlockHash, propPartsHeader, voteCh)
2015-09-22 18:12:34 -07:00
<-voteCh //precommit
2015-09-22 18:12:34 -07:00
// the proposed block should now be locked and our precommit added
2015-12-01 20:12:01 -08:00
validatePrecommit(t, cs1, 0, 0, vss[0], propBlockHash, propBlockHash)
2015-09-22 18:12:34 -07:00
// we should be stuck in limbo waiting for more precommits
2015-09-22 18:12:34 -07:00
// precommit arrives from cs2:
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlockHash, propPartsHeader, voteCh)
2015-09-22 18:12:34 -07:00
// wait to finish commit, propose in next height
<-newBlockCh
2015-09-22 18:12:34 -07:00
}
//------------------------------------------------------------------------------------------
// LockSuite
// two validators, 4 rounds.
// two vals take turns proposing. val1 locks on first one, precommits nil on everything else
2015-09-22 18:12:34 -07:00
func TestLockNoPOL(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(2)
2015-12-01 20:12:01 -08:00
cs2 := vss[1]
height := cs1.Height
2015-09-22 18:12:34 -07:00
2016-01-18 12:18:09 -08:00
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
2015-09-22 18:12:34 -07:00
/*
Round1 (cs1, B) // B B // B B2
*/
// start round and wait for prevote
cs1.enterNewRound(height, 0)
2015-12-13 11:56:05 -08:00
cs1.startRoutines(0)
re := <-proposalCh
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-12-13 11:56:05 -08:00
theBlockHash := rs.ProposalBlock.Hash()
<-voteCh // prevote
2015-09-22 18:12:34 -07:00
// we should now be stuck in limbo forever, waiting for more prevotes
// prevote arrives from cs2:
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), voteCh)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-voteCh // precommit
2015-09-22 18:12:34 -07:00
// the proposed block should now be locked and our precommit added
2015-12-01 20:12:01 -08:00
validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
2015-09-22 18:12:34 -07:00
// we should now be stuck in limbo forever, waiting for more precommits
// lets add one for a different block
// NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
2015-12-13 11:56:05 -08:00
hash := make([]byte, len(theBlockHash))
copy(hash, theBlockHash)
2015-09-22 18:12:34 -07:00
hash[0] = byte((hash[0] + 1) % 255)
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
2015-09-22 18:12:34 -07:00
// (note we're entering precommit for a second time this round)
// but with invalid args. then we enterPrecommitWait, and the timeout to new round
2015-12-13 11:56:05 -08:00
<-timeoutWaitCh
///
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-newRoundCh
log.Notice("#### ONTO ROUND 1")
2015-09-22 18:12:34 -07:00
/*
Round2 (cs1, B) // B B2
*/
incrementRound(cs2)
2015-10-28 10:49:35 -07:00
// now we're on a new round and not the proposer, so wait for timeout
2015-12-13 11:56:05 -08:00
re = <-timeoutProposeCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-12-01 20:12:01 -08:00
2015-12-13 11:56:05 -08:00
if rs.ProposalBlock != nil {
2016-07-11 18:10:05 -07:00
panic("Expected proposal block to be nil")
2015-09-22 18:12:34 -07:00
}
// wait to finish prevote
2015-12-13 11:56:05 -08:00
<-voteCh
2015-09-22 18:12:34 -07:00
// we should have prevoted our locked block
2015-12-13 11:56:05 -08:00
validatePrevote(t, cs1, 1, vss[0], rs.LockedBlock.Hash())
2015-09-22 18:12:34 -07:00
// add a conflicting prevote from the other validator
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
2015-09-22 18:12:34 -07:00
// now we're going to enter prevote again, but with invalid args
// and then prevote wait, which should timeout. then wait for precommit
2015-12-13 11:56:05 -08:00
<-timeoutWaitCh
<-voteCh // precommit
2015-09-22 18:12:34 -07:00
// the proposed block should still be locked and our precommit added
// we should precommit nil and be locked on the proposal
2015-12-01 20:12:01 -08:00
validatePrecommit(t, cs1, 1, 0, vss[0], nil, theBlockHash)
2015-09-22 18:12:34 -07:00
// add conflicting precommit from cs2
// NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
2015-09-22 18:12:34 -07:00
// (note we're entering precommit for a second time this round, but with invalid args
// then we enterPrecommitWait and timeout into NewRound
2015-12-13 11:56:05 -08:00
<-timeoutWaitCh
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-newRoundCh
log.Notice("#### ONTO ROUND 2")
2015-09-22 18:12:34 -07:00
/*
Round3 (cs2, _) // B, B2
*/
incrementRound(cs2)
2015-12-13 11:56:05 -08:00
re = <-proposalCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-10-28 10:49:35 -07:00
// now we're on a new round and are the proposer
2015-12-13 11:56:05 -08:00
if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) {
2016-07-11 18:10:05 -07:00
panic(Fmt("Expected proposal block to be locked block. Got %v, Expected %v", rs.ProposalBlock, rs.LockedBlock))
2015-09-22 18:12:34 -07:00
}
2015-12-13 11:56:05 -08:00
<-voteCh // prevote
2015-12-01 20:12:01 -08:00
validatePrevote(t, cs1, 2, vss[0], rs.LockedBlock.Hash())
2015-09-22 18:12:34 -07:00
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
2015-12-01 20:12:01 -08:00
2015-12-13 11:56:05 -08:00
<-timeoutWaitCh // prevote wait
<-voteCh // precommit
2015-12-01 20:12:01 -08:00
2016-07-11 17:40:48 -07:00
validatePrecommit(t, cs1, 2, 0, vss[0], nil, theBlockHash) // precommit nil but be locked on proposal
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh) // NOTE: conflicting precommits at same height
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-timeoutWaitCh
2015-09-22 18:12:34 -07:00
2015-10-28 10:49:35 -07:00
// before we time out into new round, set next proposal block
2015-12-01 20:12:01 -08:00
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
2015-09-22 18:12:34 -07:00
if prop == nil || propBlock == nil {
2016-07-11 18:10:05 -07:00
panic("Failed to create proposal block with cs2")
2015-09-22 18:12:34 -07:00
}
incrementRound(cs2)
2015-12-13 11:56:05 -08:00
<-newRoundCh
log.Notice("#### ONTO ROUND 3")
2015-09-22 18:12:34 -07:00
/*
Round4 (cs2, C) // B C // B C
*/
// now we're on a new round and not the proposer
// so set the proposal block
2015-12-13 11:56:05 -08:00
cs1.SetProposalAndBlock(prop, propBlock, propBlock.MakePartSet(), "")
<-proposalCh
2015-12-13 11:56:05 -08:00
<-voteCh // prevote
2015-12-01 20:12:01 -08:00
2015-12-13 11:56:05 -08:00
// prevote for locked block (not proposal)
2015-12-01 20:12:01 -08:00
validatePrevote(t, cs1, 0, vss[0], cs1.LockedBlock.Hash())
2015-09-22 18:12:34 -07:00
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh)
2015-12-01 20:12:01 -08:00
2015-12-13 11:56:05 -08:00
<-timeoutWaitCh
<-voteCh
2015-12-01 20:12:01 -08:00
2016-07-11 17:40:48 -07:00
validatePrecommit(t, cs1, 2, 0, vss[0], nil, theBlockHash) // precommit nil but locked on proposal
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh) // NOTE: conflicting precommits at same height
2015-09-22 18:12:34 -07:00
}
// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
func TestLockPOLRelock(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(4)
2015-12-01 20:12:01 -08:00
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
2015-12-13 11:56:05 -08:00
2016-01-18 12:18:09 -08:00
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
2016-04-19 17:59:52 -07:00
newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlockHeader(), 1)
2015-12-13 11:56:05 -08:00
log.Debug("cs2 last round", "lr", cs2.PrivValidator.LastRound)
2015-09-22 18:12:34 -07:00
// everything done from perspective of cs1
/*
Round1 (cs1, B) // B B B B// B nil B nil
eg. cs2 and cs4 didn't see the 2/3 prevotes
*/
// start round and wait for propose and prevote
2015-12-13 11:56:05 -08:00
startTestRound(cs1, cs1.Height, 0)
<-newRoundCh
re := <-proposalCh
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-12-13 11:56:05 -08:00
theBlockHash := rs.ProposalBlock.Hash()
2016-07-11 17:40:48 -07:00
theBlockPartsHeader := rs.ProposalBlockParts.Header()
2015-12-13 11:56:05 -08:00
<-voteCh // prevote
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrevote, cs1, theBlockHash, theBlockPartsHeader, voteCh, cs2, cs3, cs4)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-voteCh // our precommit
2015-09-22 18:12:34 -07:00
// the proposed block should now be locked and our precommit added
2015-12-01 20:12:01 -08:00
validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
2015-09-22 18:12:34 -07:00
// add precommits from the rest
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, voteCh, cs2, cs4)
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, theBlockHash, theBlockPartsHeader, voteCh)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
// before we timeout to the new round set the new proposal
2015-12-01 20:12:01 -08:00
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
2015-12-13 11:56:05 -08:00
propBlockParts := propBlock.MakePartSet()
propBlockHash := propBlock.Hash()
2015-09-22 18:12:34 -07:00
incrementRound(cs2, cs3, cs4)
// timeout to new round
2015-12-13 11:56:05 -08:00
<-timeoutWaitCh
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
//XXX: this isnt gauranteed to get there before the timeoutPropose ...
cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
<-newRoundCh
log.Notice("### ONTO ROUND 1")
2015-09-22 18:12:34 -07:00
/*
Round2 (cs2, C) // B C C C // C C C _)
cs1 changes lock!
*/
// now we're on a new round and not the proposer
2015-12-13 11:56:05 -08:00
// but we should receive the proposal
select {
case <-proposalCh:
case <-timeoutProposeCh:
<-proposalCh
}
2015-09-22 18:12:34 -07:00
// go to prevote, prevote for locked block (not proposal), move on
2015-12-13 11:56:05 -08:00
<-voteCh
2015-12-01 20:12:01 -08:00
validatePrevote(t, cs1, 0, vss[0], theBlockHash)
2015-09-22 18:12:34 -07:00
// now lets add prevotes from everyone else for the new block
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash, propBlockParts.Header(), voteCh, cs2, cs3, cs4)
2015-12-13 11:56:05 -08:00
// now either we go to PrevoteWait or Precommit
select {
case <-timeoutWaitCh: // we're in PrevoteWait, go to Precommit
<-voteCh
case <-voteCh: // we went straight to Precommit
}
2015-09-22 18:12:34 -07:00
// we should have unlocked and locked on the new block
2015-12-01 20:12:01 -08:00
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
2015-09-22 18:12:34 -07:00
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, propBlockHash, propBlockParts.Header(), voteCh, cs2, cs3)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
be := <-newBlockCh
2016-04-19 17:59:52 -07:00
b := be.(types.EventDataNewBlockHeader)
2015-12-13 11:56:05 -08:00
re = <-newRoundCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-09-22 18:12:34 -07:00
if rs.Height != 2 {
2016-07-11 18:10:05 -07:00
panic("Expected height to increment")
2015-09-22 18:12:34 -07:00
}
2016-04-19 17:59:52 -07:00
if !bytes.Equal(b.Header.Hash(), propBlockHash) {
2016-07-11 18:10:05 -07:00
panic("Expected new block to be proposal block")
2015-09-22 18:12:34 -07:00
}
}
// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
func TestLockPOLUnlock(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(4)
2015-12-01 20:12:01 -08:00
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
2015-12-13 11:56:05 -08:00
2016-01-18 12:18:09 -08:00
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
unlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringUnlock(), 1)
2015-12-13 11:56:05 -08:00
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
2015-09-22 18:12:34 -07:00
// everything done from perspective of cs1
/*
Round1 (cs1, B) // B B B B // B nil B nil
eg. didn't see the 2/3 prevotes
*/
// start round and wait for propose and prevote
2015-12-13 11:56:05 -08:00
startTestRound(cs1, cs1.Height, 0)
<-newRoundCh
re := <-proposalCh
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-12-13 11:56:05 -08:00
theBlockHash := rs.ProposalBlock.Hash()
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-voteCh // prevote
2015-09-22 18:12:34 -07:00
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrevote, cs1, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil, cs2, cs3, cs4)
2015-12-13 11:56:05 -08:00
<-voteCh //precommit
2015-09-22 18:12:34 -07:00
// the proposed block should now be locked and our precommit added
2015-12-01 20:12:01 -08:00
validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
2015-09-22 18:12:34 -07:00
2016-07-11 17:40:48 -07:00
rs = cs1.GetRoundState()
2015-09-22 18:12:34 -07:00
// add precommits from the rest
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs4)
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
// before we time out into new round, set next proposal block
2015-12-01 20:12:01 -08:00
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
2015-12-13 11:56:05 -08:00
propBlockParts := propBlock.MakePartSet()
2015-09-22 18:12:34 -07:00
incrementRound(cs2, cs3, cs4)
// timeout to new round
2015-12-13 11:56:05 -08:00
re = <-timeoutWaitCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-12-13 11:56:05 -08:00
lockedBlockHash := rs.LockedBlock.Hash()
//XXX: this isnt gauranteed to get there before the timeoutPropose ...
cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-newRoundCh
log.Notice("#### ONTO ROUND 1")
2015-09-22 18:12:34 -07:00
/*
Round2 (cs2, C) // B nil nil nil // nil nil nil _
cs1 unlocks!
*/
// now we're on a new round and not the proposer,
2015-12-13 11:56:05 -08:00
// but we should receive the proposal
select {
case <-proposalCh:
case <-timeoutProposeCh:
<-proposalCh
}
2015-09-22 18:12:34 -07:00
// go to prevote, prevote for locked block (not proposal)
2015-12-13 11:56:05 -08:00
<-voteCh
2015-12-01 20:12:01 -08:00
validatePrevote(t, cs1, 0, vss[0], lockedBlockHash)
2015-12-13 11:56:05 -08:00
// now lets add prevotes from everyone else for nil (a polka!)
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrevote, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3, cs4)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
// the polka makes us unlock and precommit nil
<-unlockCh
<-voteCh // precommit
// we should have unlocked and committed nil
// NOTE: since we don't relock on nil, the lock round is 0
2015-12-01 20:12:01 -08:00
validatePrecommit(t, cs1, 1, 0, vss[0], nil, nil)
2015-09-22 18:12:34 -07:00
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3)
2015-12-13 11:56:05 -08:00
<-newRoundCh
2015-09-22 18:12:34 -07:00
}
// 4 vals
// a polka at round 1 but we miss it
// then a polka at round 2 that we lock on
// then we see the polka from round 1 but shouldn't unlock
func TestLockPOLSafety1(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(4)
2015-12-01 20:12:01 -08:00
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
2015-12-13 11:56:05 -08:00
2016-01-18 12:18:09 -08:00
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
2015-12-13 11:56:05 -08:00
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
2015-09-22 18:12:34 -07:00
// start round and wait for propose and prevote
2015-12-13 11:56:05 -08:00
startTestRound(cs1, cs1.Height, 0)
<-newRoundCh
re := <-proposalCh
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-12-13 11:56:05 -08:00
propBlock := rs.ProposalBlock
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-voteCh // prevote
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
validatePrevote(t, cs1, 0, vss[0], propBlock.Hash())
2015-09-22 18:12:34 -07:00
// the others sign a polka but we don't see it
prevotes := signVoteMany(types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet().Header(), cs2, cs3, cs4)
// before we time out into new round, set next proposer
// and next proposal block
2015-12-13 11:56:05 -08:00
/*
_, v1 := cs1.Validators.GetByAddress(vss[0].Address)
v1.VotingPower = 1
if updated := cs1.Validators.Update(v1); !updated {
2016-07-11 18:10:05 -07:00
panic("failed to update validator")
2015-12-13 11:56:05 -08:00
}*/
2015-09-22 18:12:34 -07:00
log.Warn("old prop", "hash", fmt.Sprintf("%X", propBlock.Hash()))
// we do see them precommit nil
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3, cs4)
2015-09-22 18:12:34 -07:00
2015-12-01 20:12:01 -08:00
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
2015-12-13 11:56:05 -08:00
propBlockHash := propBlock.Hash()
propBlockParts := propBlock.MakePartSet()
2015-09-22 18:12:34 -07:00
incrementRound(cs2, cs3, cs4)
2015-12-13 11:56:05 -08:00
//XXX: this isnt gauranteed to get there before the timeoutPropose ...
cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
<-newRoundCh
log.Notice("### ONTO ROUND 1")
2015-09-22 18:12:34 -07:00
/*Round2
// we timeout and prevote our lock
// a polka happened but we didn't see it!
*/
// now we're on a new round and not the proposer,
2015-12-13 11:56:05 -08:00
// but we should receive the proposal
select {
case re = <-proposalCh:
case <-timeoutProposeCh:
re = <-proposalCh
}
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-12-13 11:56:05 -08:00
if rs.LockedBlock != nil {
2016-07-11 18:10:05 -07:00
panic("we should not be locked!")
2015-09-22 18:12:34 -07:00
}
log.Warn("new prop", "hash", fmt.Sprintf("%X", propBlockHash))
// go to prevote, prevote for proposal block
2015-12-13 11:56:05 -08:00
<-voteCh
2015-12-01 20:12:01 -08:00
validatePrevote(t, cs1, 1, vss[0], propBlockHash)
2015-09-22 18:12:34 -07:00
// now we see the others prevote for it, so we should lock on it
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash, propBlockParts.Header(), nil, cs2, cs3, cs4)
2015-12-13 11:56:05 -08:00
<-voteCh // precommit
2015-09-22 18:12:34 -07:00
// we should have precommitted
2015-12-01 20:12:01 -08:00
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
2015-09-22 18:12:34 -07:00
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3)
2015-12-13 11:56:05 -08:00
<-timeoutWaitCh
2015-09-22 18:12:34 -07:00
incrementRound(cs2, cs3, cs4)
2015-12-13 11:56:05 -08:00
<-newRoundCh
log.Notice("### ONTO ROUND 2")
2015-09-22 18:12:34 -07:00
/*Round3
we see the polka from round 1 but we shouldn't unlock!
*/
// timeout of propose
2015-12-13 11:56:05 -08:00
<-timeoutProposeCh
2015-09-22 18:12:34 -07:00
// finish prevote
2015-12-13 11:56:05 -08:00
<-voteCh
2015-09-22 18:12:34 -07:00
// we should prevote what we're locked on
2015-12-01 20:12:01 -08:00
validatePrevote(t, cs1, 2, vss[0], propBlockHash)
2015-09-22 18:12:34 -07:00
2016-01-18 12:18:09 -08:00
newStepCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRoundStep(), 1)
2015-09-22 18:12:34 -07:00
// add prevotes from the earlier round
addVoteToFromMany(cs1, prevotes, cs2, cs3, cs4)
log.Warn("Done adding prevotes!")
ensureNoNewStep(newStepCh)
2015-09-22 18:12:34 -07:00
}
// 4 vals.
2015-12-13 11:56:05 -08:00
// polka P0 at R0, P1 at R1, and P2 at R2,
// we lock on P0 at R0, don't see P1, and unlock using P2 at R2
// then we should make sure we don't lock using P1
// What we want:
// dont see P0, lock on P1 at R1, dont unlock using P0 at R2
2015-09-22 18:12:34 -07:00
func TestLockPOLSafety2(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(4)
2015-12-01 20:12:01 -08:00
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
2015-09-22 18:12:34 -07:00
2016-01-18 12:18:09 -08:00
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
unlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringUnlock(), 1)
2015-12-13 11:56:05 -08:00
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
// the block for R0: gets polkad but we miss it
// (even though we signed it, shhh)
_, propBlock0 := decideProposal(cs1, vss[0], cs1.Height, cs1.Round)
propBlockHash0 := propBlock0.Hash()
propBlockParts0 := propBlock0.MakePartSet()
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
// the others sign a polka but we don't see it
prevotes := signVoteMany(types.VoteTypePrevote, propBlockHash0, propBlockParts0.Header(), cs2, cs3, cs4)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
// the block for round 1
prop1, propBlock1 := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
propBlockHash1 := propBlock1.Hash()
propBlockParts1 := propBlock1.MakePartSet()
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
incrementRound(cs2, cs3, cs4)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
cs1.updateRoundStep(0, RoundStepPrecommitWait)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
log.Notice("### ONTO Round 1")
// jump in at round 1
height := cs1.Height
startTestRound(cs1, height, 1)
<-newRoundCh
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
cs1.SetProposalAndBlock(prop1, propBlock1, propBlockParts1, "some peer")
<-proposalCh
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-voteCh // prevote
2015-09-22 18:12:34 -07:00
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash1, propBlockParts1.Header(), nil, cs2, cs3, cs4)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-voteCh // precommit
// the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash1, propBlockHash1)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
// add precommits from the rest
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs4)
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, propBlockHash1, propBlockParts1.Header(), nil)
2015-09-22 18:12:34 -07:00
incrementRound(cs2, cs3, cs4)
2015-12-13 11:56:05 -08:00
// timeout of precommit wait to new round
<-timeoutWaitCh
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
// in round 2 we see the polkad block from round 0
newProp := types.NewProposal(height, 2, propBlockParts0.Header(), 0)
2016-05-08 15:00:58 -07:00
if err := cs3.SignProposal(config.GetString("chain_id"), newProp); err != nil {
2016-07-11 18:10:05 -07:00
panic(err)
2015-12-13 11:56:05 -08:00
}
cs1.SetProposalAndBlock(newProp, propBlock0, propBlockParts0, "some peer")
addVoteToFromMany(cs1, prevotes, cs2, cs3, cs4) // add the pol votes
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-newRoundCh
log.Notice("### ONTO Round 2")
/*Round2
// now we see the polka from round 1, but we shouldnt unlock
2015-09-22 18:12:34 -07:00
*/
2015-12-13 11:56:05 -08:00
select {
case <-timeoutProposeCh:
<-proposalCh
case <-proposalCh:
}
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
select {
case <-unlockCh:
2016-07-11 18:10:05 -07:00
panic("validator unlocked using an old polka")
2015-12-13 11:56:05 -08:00
case <-voteCh:
// prevote our locked block
2015-09-22 18:12:34 -07:00
}
2015-12-13 11:56:05 -08:00
validatePrevote(t, cs1, 2, vss[0], propBlockHash1)
2015-09-22 18:12:34 -07:00
}
//------------------------------------------------------------------------------------------
// SlashingSuite
2015-12-13 11:56:05 -08:00
// TODO: Slashing
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
/*
2015-09-22 18:12:34 -07:00
func TestSlashingPrevotes(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(2)
2015-12-01 20:12:01 -08:00
cs2 := vss[1]
2015-12-13 11:56:05 -08:00
2016-01-18 12:18:09 -08:00
proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringTimeoutWait() , 1)
newRoundCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringNewRound() , 1)
2015-12-13 11:56:05 -08:00
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
2015-09-22 18:12:34 -07:00
// start round and wait for propose and prevote
2015-12-13 11:56:05 -08:00
startTestRound(cs1, cs1.Height, 0)
<-newRoundCh
re := <-proposalCh
<-voteCh // prevote
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-09-22 18:12:34 -07:00
// we should now be stuck in limbo forever, waiting for more prevotes
// add one for a different block should cause us to go into prevote wait
2016-07-11 17:40:48 -07:00
hash := rs.ProposalBlock.Hash()
2015-09-22 18:12:34 -07:00
hash[0] = byte(hash[0]+1) % 255
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, rs.ProposalBlockParts.Header(), nil)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-timeoutWaitCh
2015-09-22 18:12:34 -07:00
// NOTE: we have to send the vote for different block first so we don't just go into precommit round right
// away and ignore more prevotes (and thus fail to slash!)
// add the conflicting vote
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(),nil)
2015-09-22 18:12:34 -07:00
2015-11-01 11:34:08 -08:00
// XXX: Check for existence of Dupeout info
2015-09-22 18:12:34 -07:00
}
func TestSlashingPrecommits(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(2)
2015-12-01 20:12:01 -08:00
cs2 := vss[1]
2015-12-13 11:56:05 -08:00
2016-01-18 12:18:09 -08:00
proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringTimeoutWait() , 1)
newRoundCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringNewRound() , 1)
2015-12-13 11:56:05 -08:00
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
2015-09-22 18:12:34 -07:00
// start round and wait for propose and prevote
2015-12-13 11:56:05 -08:00
startTestRound(cs1, cs1.Height, 0)
<-newRoundCh
re := <-proposalCh
<-voteCh // prevote
2015-09-22 18:12:34 -07:00
// add prevote from cs2
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-voteCh // precommit
2015-09-22 18:12:34 -07:00
// we should now be stuck in limbo forever, waiting for more prevotes
// add one for a different block should cause us to go into prevote wait
2015-12-13 11:56:05 -08:00
hash := rs.ProposalBlock.Hash()
2015-09-22 18:12:34 -07:00
hash[0] = byte(hash[0]+1) % 255
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlockParts.Header(),nil)
2015-09-22 18:12:34 -07:00
// NOTE: we have to send the vote for different block first so we don't just go into precommit round right
// away and ignore more prevotes (and thus fail to slash!)
// add precommit from cs2
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(),nil)
2015-09-22 18:12:34 -07:00
2015-11-01 11:34:08 -08:00
// XXX: Check for existence of Dupeout info
2015-09-22 18:12:34 -07:00
}
2015-12-13 11:56:05 -08:00
*/
2015-09-22 18:12:34 -07:00
//------------------------------------------------------------------------------------------
// CatchupSuite
//------------------------------------------------------------------------------------------
// HaltSuite
// 4 vals.
// we receive a final precommit after going into next round, but others might have gone to commit already!
func TestHalt1(t *testing.T) {
2016-01-18 12:57:57 -08:00
cs1, vss := randConsensusState(4)
2015-12-01 20:12:01 -08:00
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
2015-09-22 18:12:34 -07:00
2016-01-18 12:18:09 -08:00
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlock(), 1)
2015-12-13 11:56:05 -08:00
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
2015-09-22 18:12:34 -07:00
// start round and wait for propose and prevote
2015-12-13 11:56:05 -08:00
startTestRound(cs1, cs1.Height, 0)
<-newRoundCh
re := <-proposalCh
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-12-13 11:56:05 -08:00
propBlock := rs.ProposalBlock
propBlockParts := propBlock.MakePartSet()
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
<-voteCh // prevote
2015-09-22 18:12:34 -07:00
2016-07-11 17:40:48 -07:00
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlock.Hash(), propBlockParts.Header(), nil, cs3, cs4)
2015-12-13 11:56:05 -08:00
<-voteCh // precommit
2015-09-22 18:12:34 -07:00
// the proposed block should now be locked and our precommit added
2015-12-13 11:56:05 -08:00
validatePrecommit(t, cs1, 0, 0, vss[0], propBlock.Hash(), propBlock.Hash())
2015-09-22 18:12:34 -07:00
// add precommits from the rest
2016-07-11 17:40:48 -07:00
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, nil, types.PartSetHeader{}, nil) // didnt receive proposal
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, propBlock.Hash(), propBlockParts.Header(), nil)
2015-09-22 18:12:34 -07:00
// we receive this later, but cs3 might receive it earlier and with ours will go to commit!
2015-12-13 11:56:05 -08:00
precommit4 := signVote(cs4, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header())
2015-09-22 18:12:34 -07:00
incrementRound(cs2, cs3, cs4)
// timeout to new round
2015-12-13 11:56:05 -08:00
<-timeoutWaitCh
re = <-newRoundCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
log.Notice("### ONTO ROUND 1")
2015-09-22 18:12:34 -07:00
/*Round2
// we timeout and prevote our lock
// a polka happened but we didn't see it!
*/
// go to prevote, prevote for locked block
2015-12-13 11:56:05 -08:00
<-voteCh // prevote
validatePrevote(t, cs1, 0, vss[0], rs.LockedBlock.Hash())
2015-09-22 18:12:34 -07:00
// now we receive the precommit from the previous round
addVoteToFrom(cs1, cs4, precommit4)
// receiving that precommit should take us straight to commit
2015-12-13 11:56:05 -08:00
<-newBlockCh
re = <-newRoundCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
2015-09-22 18:12:34 -07:00
2015-12-13 11:56:05 -08:00
if rs.Height != 2 {
2016-07-11 18:10:05 -07:00
panic("expected height to increment")
2015-09-22 18:12:34 -07:00
}
}