2015-09-22 21:12:34 -04:00
package consensus
import (
"bytes"
"fmt"
"testing"
"time"
2016-07-11 21:10:05 -04:00
. "github.com/tendermint/go-common"
2016-10-10 02:58:13 -04:00
"github.com/tendermint/tendermint/config/tendermint_test"
2015-09-22 21:12:34 -04:00
"github.com/tendermint/tendermint/types"
)
2016-03-06 15:05:50 -08:00
func init ( ) {
2016-05-08 15:00:58 -07:00
config = tendermint_test . ResetConfig ( "consensus_state_test" )
2016-03-06 15:05:50 -08:00
}
2016-02-29 16:15:23 -05:00
func ( tp * TimeoutParams ) ensureProposeTimeout ( ) time . Duration {
return time . Duration ( tp . Propose0 * 2 ) * time . Millisecond
}
2015-09-22 21:12:34 -04: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 21:12:34 -04: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 15:57:57 -05:00
cs1 , vss := randConsensusState ( 4 )
2015-12-01 20:12:01 -08:00
height , round := cs1 . Height , cs1 . Round
2015-10-28 19:49:35 +02: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 )
2015-12-12 01:28:53 -05:00
startTestRound ( cs1 , height , round )
// wait for new round so proposer is set
<- newRoundCh
2015-10-28 19:49:35 +02:00
// lets commit a block and ensure proposer for the next height is correct
2015-12-12 01:28:53 -05:00
prop := cs1 . GetRoundState ( ) . Validators . Proposer ( )
2015-10-28 19:49:35 +02:00
if ! bytes . Equal ( prop . Address , cs1 . privValidator . Address ) {
2016-07-11 21:10:05 -04:00
panic ( Fmt ( "expected proposer to be validator %d. Got %X" , 0 , prop . Address ) )
2015-10-28 19:49:35 +02:00
}
2015-12-01 20:12:01 -08:00
2015-12-12 01:28:53 -05:00
// wait for complete proposal
<- proposalCh
2015-12-01 20:12:01 -08:00
2015-12-12 01:28:53 -05:00
rs := cs1 . GetRoundState ( )
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrecommit , cs1 , rs . ProposalBlock . Hash ( ) , rs . ProposalBlockParts . Header ( ) , nil , vss [ 1 : ] ... )
2015-12-01 20:12:01 -08:00
2015-12-12 01:28:53 -05:00
// wait for new round so next validator is set
<- newRoundCh
2015-12-01 20:12:01 -08:00
2015-12-12 01:28:53 -05: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 21:10:05 -04:00
panic ( Fmt ( "expected proposer to be validator %d. Got %X" , 1 , prop . Address ) )
2015-10-28 19:49:35 +02:00
}
2015-12-01 20:12:01 -08:00
}
2015-10-28 19:49:35 +02: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 15:57:57 -05:00
cs1 , vss := randConsensusState ( 4 ) // test needs more work for more than 3 validators
2015-10-28 19:49:35 +02:00
2016-01-18 12:18:09 -08:00
newRoundCh := subscribeToEvent ( cs1 . evsw , "tester" , types . EventStringNewRound ( ) , 1 )
2015-12-10 11:41:18 -05:00
2015-10-28 19:49:35 +02:00
// this time we jump in at round 2
2015-12-01 20:12:01 -08:00
incrementRound ( vss [ 1 : ] ... )
incrementRound ( vss [ 1 : ] ... )
2015-12-12 01:28:53 -05:00
startTestRound ( cs1 , cs1 . Height , 2 )
2015-12-10 11:41:18 -05:00
2015-12-12 01:28:53 -05:00
<- newRoundCh // wait for the new round
2015-10-28 19:49:35 +02: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 ++ {
2015-12-12 01:28:53 -05:00
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 21:10:05 -04:00
panic ( Fmt ( "expected proposer to be validator %d. Got %X" , ( i + 2 ) % len ( vss ) , prop . Address ) )
2015-10-28 19:49:35 +02:00
}
2015-12-12 01:28:53 -05:00
rs := cs1 . GetRoundState ( )
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrecommit , cs1 , nil , rs . ProposalBlockParts . Header ( ) , nil , vss [ 1 : ] ... )
2015-12-12 01:28:53 -05:00
<- newRoundCh // wait for the new round event each round
2015-12-10 11:41:18 -05:00
2015-12-01 20:12:01 -08:00
incrementRound ( vss [ 1 : ] ... )
2015-10-28 19:49:35 +02:00
}
2015-09-22 21:12:34 -04:00
}
// a non-validator should timeout into the prevote round
func TestEnterProposeNoPrivValidator ( t * testing . T ) {
2016-01-18 15:57:57 -05:00
cs , _ := randConsensusState ( 1 )
2015-09-22 21:12:34 -04:00
cs . SetPrivValidator ( nil )
2015-12-01 20:12:01 -08:00
height , round := cs . Height , cs . Round
2015-09-22 21:12:34 -04: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 21:12:34 -04:00
2015-12-12 01:28:53 -05:00
startTestRound ( cs , height , round )
2015-09-22 21:12:34 -04:00
// if we're not a validator, EnterPropose should timeout
2016-02-29 16:15:23 -05:00
ticker := time . NewTicker ( cs . timeoutParams . ensureProposeTimeout ( ) )
2015-12-12 01:28:53 -05:00
select {
case <- timeoutCh :
case <- ticker . C :
2016-07-11 21:10:05 -04:00
panic ( "Expected EnterPropose to timeout" )
2015-12-12 01:28:53 -05:00
2015-12-01 20:12:01 -08:00
}
2015-12-12 01:28:53 -05: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 21:12:34 -04:00
}
}
// a validator should not timeout of the prevote round (TODO: unless the block is really big!)
2015-12-10 11:41:18 -05:00
func TestEnterProposeYesPrivValidator ( t * testing . T ) {
2016-01-18 15:57:57 -05:00
cs , _ := randConsensusState ( 1 )
2015-12-01 20:12:01 -08:00
height , round := cs . Height , cs . Round
2015-09-22 21:12:34 -04:00
2015-12-01 20:12:01 -08:00
// Listen for propose timeout event
2015-12-14 00:38:19 -05:00
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 21:12:34 -04:00
2015-12-13 19:33:05 -05:00
cs . enterNewRound ( height , round )
2015-12-12 01:28:53 -05:00
cs . startRoutines ( 3 )
2015-09-22 21:12:34 -04:00
2015-12-12 01:28:53 -05:00
<- proposalCh
2015-09-22 21:12:34 -04: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 21:12:34 -04:00
2015-12-13 19:33:05 -05:00
// if we're a validator, enterPropose should not timeout
2016-02-29 16:15:23 -05:00
ticker := time . NewTicker ( cs . timeoutParams . ensureProposeTimeout ( ) )
2015-12-12 01:28:53 -05:00
select {
case <- timeoutCh :
2016-07-11 21:10:05 -04:00
panic ( "Expected EnterPropose not to timeout" )
2015-12-12 01:28:53 -05:00
case <- ticker . C :
2015-09-22 21:12:34 -04:00
}
}
func TestBadProposal ( t * testing . T ) {
2016-01-18 15:57:57 -05: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 21:12:34 -04: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 21:12:34 -04:00
2015-12-12 01:28:53 -05: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 21:12:34 -04:00
// make the block bad by tampering with statehash
2015-12-01 20:12:01 -08:00
stateHash := propBlock . AppHash
2015-12-04 00:59:02 -08:00
if len ( stateHash ) == 0 {
stateHash = make ( [ ] byte , 32 )
}
2015-09-22 21:12:34 -04:00
stateHash [ 0 ] = byte ( ( stateHash [ 0 ] + 1 ) % 255 )
2015-12-01 20:12:01 -08:00
propBlock . AppHash = stateHash
2015-09-22 21:12:34 -04:00
propBlockParts := propBlock . MakePartSet ( )
2015-12-12 01:28:53 -05:00
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 21:10:05 -04:00
panic ( "failed to sign bad proposal: " + err . Error ( ) )
2015-09-22 21:12:34 -04:00
}
2015-12-12 01:28:53 -05:00
// set the proposal block
cs1 . SetProposalAndBlock ( proposal , propBlock , propBlockParts , "some peer" )
2015-09-22 21:12:34 -04:00
2015-12-12 01:28:53 -05:00
// start the machine
startTestRound ( cs1 , height , round )
2015-09-22 21:12:34 -04:00
2015-12-12 01:28:53 -05:00
// wait for proposal
<- proposalCh
2016-01-06 17:14:20 -08:00
// wait for prevote
2015-12-12 01:28:53 -05:00
<- voteCh
2015-12-01 20:12:01 -08:00
validatePrevote ( t , cs1 , round , vss [ 0 ] , nil )
2015-09-22 21:12:34 -04:00
2015-12-12 01:28:53 -05:00
// add bad prevote from cs2 and wait for it
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrevote , cs1 , cs2 , propBlock . Hash ( ) , propBlock . MakePartSet ( ) . Header ( ) , voteCh )
2015-12-01 20:12:01 -08:00
2015-12-12 01:28:53 -05: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 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrecommit , cs1 , cs2 , propBlock . Hash ( ) , propBlock . MakePartSet ( ) . Header ( ) , voteCh )
2015-09-22 21:12:34 -04:00
}
//----------------------------------------------------------------------------------------------------
2015-10-28 19:49:35 +02:00
// FullRoundSuite
2015-09-22 21:12:34 -04:00
// propose, prevote, and precommit a block
func TestFullRound1 ( t * testing . T ) {
2016-01-18 15:57:57 -05:00
cs , vss := randConsensusState ( 1 )
2015-12-01 20:12:01 -08:00
height , round := cs . Height , cs . Round
2015-09-22 21:12:34 -04:00
2016-09-08 18:06:25 -04:00
voteCh := subscribeToEvent ( cs . evsw , "tester" , types . EventStringVote ( ) , 0 )
2016-01-18 12:18:09 -08:00
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 14:56:05 -05:00
startTestRound ( cs , height , round )
<- newRoundCh
// grab proposal
re := <- propCh
2016-01-28 19:44:44 -08:00
propBlockHash := re . ( types . EventDataRoundState ) . RoundState . ( * RoundState ) . ProposalBlock . Hash ( )
2015-09-22 21:12:34 -04:00
2015-12-12 01:28:53 -05:00
<- voteCh // wait for prevote
2016-09-08 18:06:25 -04:00
// NOTE: voteChan cap of 0 ensures we can complete this
// before consensus can move to the next height (and cause a race condition)
2015-12-13 14:56:05 -05:00
validatePrevote ( t , cs , round , vss [ 0 ] , propBlockHash )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- voteCh // wait for precommit
2015-12-01 20:12:01 -08:00
2015-12-13 14:56:05 -05:00
// we're going to roll right into new height
<- newRoundCh
2015-12-12 01:28:53 -05:00
2015-12-13 14:56:05 -05:00
validateLastPrecommit ( t , cs , vss [ 0 ] , propBlockHash )
2015-09-22 21:12:34 -04:00
}
// nil is proposed, so prevote and precommit nil
func TestFullRoundNil ( t * testing . T ) {
2016-01-18 15:57:57 -05:00
cs , vss := randConsensusState ( 1 )
2015-12-01 20:12:01 -08:00
height , round := cs . Height , cs . Round
2015-09-22 21:12:34 -04:00
2016-01-18 12:18:09 -08:00
voteCh := subscribeToEvent ( cs . evsw , "tester" , types . EventStringVote ( ) , 1 )
2015-12-10 11:41:18 -05:00
2015-12-13 19:33:05 -05:00
cs . enterPrevote ( height , round )
2015-12-12 01:28:53 -05:00
cs . startRoutines ( 4 )
2015-09-22 21:12:34 -04:00
2015-12-12 01:28:53 -05:00
<- voteCh // prevote
<- voteCh // precommit
2015-09-22 21:12:34 -04: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 21:12:34 -04: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 15:57:57 -05: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 21:12:34 -04: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-12-12 01:28:53 -05:00
2015-09-22 21:12:34 -04:00
// start round and wait for propose and prevote
2015-12-12 01:28:53 -05:00
startTestRound ( cs1 , height , round )
2015-09-22 21:12:34 -04:00
2015-12-12 01:28:53 -05:00
<- voteCh // prevote
// we should be stuck in limbo waiting for more prevotes
2016-07-11 20:40:48 -04:00
rs := cs1 . GetRoundState ( )
propBlockHash , propPartsHeader := rs . ProposalBlock . Hash ( ) , rs . ProposalBlockParts . Header ( )
2015-09-22 21:12:34 -04:00
// prevote arrives from cs2:
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrevote , cs1 , cs2 , propBlockHash , propPartsHeader , voteCh )
2015-09-22 21:12:34 -04:00
2015-12-12 01:28:53 -05:00
<- voteCh //precommit
2015-09-22 21:12:34 -04: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 21:12:34 -04:00
2015-12-12 01:28:53 -05:00
// we should be stuck in limbo waiting for more precommits
2015-09-22 21:12:34 -04:00
// precommit arrives from cs2:
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrecommit , cs1 , cs2 , propBlockHash , propPartsHeader , voteCh )
2015-09-22 21:12:34 -04:00
// wait to finish commit, propose in next height
2015-12-12 01:28:53 -05:00
<- newBlockCh
2015-09-22 21:12:34 -04:00
}
//------------------------------------------------------------------------------------------
// LockSuite
// two validators, 4 rounds.
2015-12-13 19:30:15 -05:00
// two vals take turns proposing. val1 locks on first one, precommits nil on everything else
2015-09-22 21:12:34 -04:00
func TestLockNoPOL ( t * testing . T ) {
2016-01-18 15:57:57 -05:00
cs1 , vss := randConsensusState ( 2 )
2015-12-01 20:12:01 -08:00
cs2 := vss [ 1 ]
height := cs1 . Height
2015-09-22 21:12:34 -04: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 21:12:34 -04:00
/ *
Round1 ( cs1 , B ) // B B // B B2
* /
2015-12-10 11:41:18 -05:00
// start round and wait for prevote
2015-12-13 19:33:05 -05:00
cs1 . enterNewRound ( height , 0 )
2015-12-13 14:56:05 -05:00
cs1 . startRoutines ( 0 )
re := <- proposalCh
2016-01-28 19:44:44 -08:00
rs := re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-12-13 14:56:05 -05:00
theBlockHash := rs . ProposalBlock . Hash ( )
<- voteCh // prevote
2015-09-22 21:12:34 -04:00
// we should now be stuck in limbo forever, waiting for more prevotes
// prevote arrives from cs2:
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrevote , cs1 , cs2 , rs . ProposalBlock . Hash ( ) , rs . ProposalBlockParts . Header ( ) , voteCh )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- voteCh // precommit
2015-09-22 21:12:34 -04: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 21:12:34 -04: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 14:56:05 -05:00
hash := make ( [ ] byte , len ( theBlockHash ) )
copy ( hash , theBlockHash )
2015-09-22 21:12:34 -04:00
hash [ 0 ] = byte ( ( hash [ 0 ] + 1 ) % 255 )
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrecommit , cs1 , cs2 , hash , rs . ProposalBlock . MakePartSet ( ) . Header ( ) , voteCh )
2015-09-22 21:12:34 -04:00
// (note we're entering precommit for a second time this round)
2015-12-13 19:33:05 -05:00
// but with invalid args. then we enterPrecommitWait, and the timeout to new round
2015-12-13 14:56:05 -05:00
<- timeoutWaitCh
///
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- newRoundCh
log . Notice ( "#### ONTO ROUND 1" )
2015-09-22 21:12:34 -04:00
/ *
Round2 ( cs1 , B ) // B B2
* /
incrementRound ( cs2 )
2015-10-28 19:49:35 +02:00
// now we're on a new round and not the proposer, so wait for timeout
2015-12-13 14:56:05 -05:00
re = <- timeoutProposeCh
2016-01-28 19:44:44 -08:00
rs = re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-12-01 20:12:01 -08:00
2015-12-13 14:56:05 -05:00
if rs . ProposalBlock != nil {
2016-07-11 21:10:05 -04:00
panic ( "Expected proposal block to be nil" )
2015-09-22 21:12:34 -04:00
}
// wait to finish prevote
2015-12-13 14:56:05 -05:00
<- voteCh
2015-09-22 21:12:34 -04:00
// we should have prevoted our locked block
2015-12-13 14:56:05 -05:00
validatePrevote ( t , cs1 , 1 , vss [ 0 ] , rs . LockedBlock . Hash ( ) )
2015-09-22 21:12:34 -04:00
// add a conflicting prevote from the other validator
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrevote , cs1 , cs2 , hash , rs . ProposalBlock . MakePartSet ( ) . Header ( ) , voteCh )
2015-09-22 21:12:34 -04: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 14:56:05 -05:00
<- timeoutWaitCh
<- voteCh // precommit
2015-09-22 21:12:34 -04: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 21:12:34 -04: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 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrecommit , cs1 , cs2 , hash , rs . ProposalBlock . MakePartSet ( ) . Header ( ) , voteCh )
2015-09-22 21:12:34 -04:00
// (note we're entering precommit for a second time this round, but with invalid args
2015-12-13 19:33:05 -05:00
// then we enterPrecommitWait and timeout into NewRound
2015-12-13 14:56:05 -05:00
<- timeoutWaitCh
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- newRoundCh
log . Notice ( "#### ONTO ROUND 2" )
2015-09-22 21:12:34 -04:00
/ *
Round3 ( cs2 , _ ) // B, B2
* /
incrementRound ( cs2 )
2015-12-13 14:56:05 -05:00
re = <- proposalCh
2016-01-28 19:44:44 -08:00
rs = re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-10-28 19:49:35 +02:00
// now we're on a new round and are the proposer
2015-12-13 14:56:05 -05:00
if ! bytes . Equal ( rs . ProposalBlock . Hash ( ) , rs . LockedBlock . Hash ( ) ) {
2016-07-11 21:10:05 -04:00
panic ( Fmt ( "Expected proposal block to be locked block. Got %v, Expected %v" , rs . ProposalBlock , rs . LockedBlock ) )
2015-09-22 21:12:34 -04:00
}
2015-12-13 14:56:05 -05:00
<- voteCh // prevote
2015-12-01 20:12:01 -08:00
2015-12-13 19:30:15 -05:00
validatePrevote ( t , cs1 , 2 , vss [ 0 ] , rs . LockedBlock . Hash ( ) )
2015-09-22 21:12:34 -04:00
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrevote , cs1 , cs2 , hash , rs . ProposalBlock . MakePartSet ( ) . Header ( ) , voteCh )
2015-12-01 20:12:01 -08:00
2015-12-13 14:56:05 -05:00
<- timeoutWaitCh // prevote wait
<- voteCh // precommit
2015-12-01 20:12:01 -08:00
2016-07-11 20:40:48 -04: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 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- timeoutWaitCh
2015-09-22 21:12:34 -04:00
2015-10-28 19:49:35 +02: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 21:12:34 -04:00
if prop == nil || propBlock == nil {
2016-07-11 21:10:05 -04:00
panic ( "Failed to create proposal block with cs2" )
2015-09-22 21:12:34 -04:00
}
incrementRound ( cs2 )
2015-12-13 14:56:05 -05:00
<- newRoundCh
log . Notice ( "#### ONTO ROUND 3" )
2015-09-22 21:12:34 -04: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 14:56:05 -05:00
cs1 . SetProposalAndBlock ( prop , propBlock , propBlock . MakePartSet ( ) , "" )
2015-12-10 11:41:18 -05:00
2015-12-13 19:33:05 -05:00
<- proposalCh
2015-12-13 14:56:05 -05:00
<- voteCh // prevote
2015-12-01 20:12:01 -08:00
2015-12-13 14:56:05 -05: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 21:12:34 -04:00
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrevote , cs1 , cs2 , propBlock . Hash ( ) , propBlock . MakePartSet ( ) . Header ( ) , voteCh )
2015-12-01 20:12:01 -08:00
2015-12-13 14:56:05 -05:00
<- timeoutWaitCh
<- voteCh
2015-12-01 20:12:01 -08:00
2016-07-11 20:40:48 -04: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 21:12:34 -04: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 15:57:57 -05: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 14:56:05 -05: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 20:59:52 -04:00
newBlockCh := subscribeToEvent ( cs1 . evsw , "tester" , types . EventStringNewBlockHeader ( ) , 1 )
2015-12-13 14:56:05 -05:00
log . Debug ( "cs2 last round" , "lr" , cs2 . PrivValidator . LastRound )
2015-09-22 21:12:34 -04: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 14:56:05 -05:00
startTestRound ( cs1 , cs1 . Height , 0 )
<- newRoundCh
re := <- proposalCh
2016-01-28 19:44:44 -08:00
rs := re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-12-13 14:56:05 -05:00
theBlockHash := rs . ProposalBlock . Hash ( )
2016-07-11 20:40:48 -04:00
theBlockPartsHeader := rs . ProposalBlockParts . Header ( )
2015-12-13 14:56:05 -05:00
<- voteCh // prevote
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrevote , cs1 , theBlockHash , theBlockPartsHeader , voteCh , cs2 , cs3 , cs4 )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- voteCh // our precommit
2015-09-22 21:12:34 -04: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 21:12:34 -04:00
// add precommits from the rest
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrecommit , cs1 , nil , types . PartSetHeader { } , voteCh , cs2 , cs4 )
signAddVoteToFrom ( types . VoteTypePrecommit , cs1 , cs3 , theBlockHash , theBlockPartsHeader , voteCh )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05: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 14:56:05 -05:00
propBlockParts := propBlock . MakePartSet ( )
propBlockHash := propBlock . Hash ( )
2015-09-22 21:12:34 -04:00
incrementRound ( cs2 , cs3 , cs4 )
// timeout to new round
2015-12-13 14:56:05 -05:00
<- timeoutWaitCh
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05: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 21:12:34 -04: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 14:56:05 -05:00
// but we should receive the proposal
select {
case <- proposalCh :
case <- timeoutProposeCh :
<- proposalCh
}
2015-12-10 11:41:18 -05:00
2015-09-22 21:12:34 -04:00
// go to prevote, prevote for locked block (not proposal), move on
2015-12-13 14:56:05 -05:00
<- voteCh
2015-12-01 20:12:01 -08:00
validatePrevote ( t , cs1 , 0 , vss [ 0 ] , theBlockHash )
2015-09-22 21:12:34 -04:00
// now lets add prevotes from everyone else for the new block
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrevote , cs1 , propBlockHash , propBlockParts . Header ( ) , voteCh , cs2 , cs3 , cs4 )
2015-12-13 14:56:05 -05:00
// now either we go to PrevoteWait or Precommit
select {
case <- timeoutWaitCh : // we're in PrevoteWait, go to Precommit
2016-07-12 14:08:59 -04:00
// XXX: there's no guarantee we see the polka, this might be a precommit for nil,
// in which case the test fails!
2015-12-13 14:56:05 -05:00
<- voteCh
case <- voteCh : // we went straight to Precommit
}
2015-09-22 21:12:34 -04: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 21:12:34 -04:00
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrecommit , cs1 , propBlockHash , propBlockParts . Header ( ) , voteCh , cs2 , cs3 )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
be := <- newBlockCh
2016-04-19 20:59:52 -04:00
b := be . ( types . EventDataNewBlockHeader )
2015-12-13 14:56:05 -05:00
re = <- newRoundCh
2016-01-28 19:44:44 -08:00
rs = re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-09-22 21:12:34 -04:00
if rs . Height != 2 {
2016-07-11 21:10:05 -04:00
panic ( "Expected height to increment" )
2015-09-22 21:12:34 -04:00
}
2016-04-19 20:59:52 -04:00
if ! bytes . Equal ( b . Header . Hash ( ) , propBlockHash ) {
2016-07-11 21:10:05 -04:00
panic ( "Expected new block to be proposal block" )
2015-09-22 21:12:34 -04: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 15:57:57 -05: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 14:56:05 -05: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 14:56:05 -05:00
voteCh := subscribeToVoter ( cs1 , cs1 . privValidator . Address )
2015-09-22 21:12:34 -04: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 14:56:05 -05:00
startTestRound ( cs1 , cs1 . Height , 0 )
<- newRoundCh
re := <- proposalCh
2016-01-28 19:44:44 -08:00
rs := re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-12-13 14:56:05 -05:00
theBlockHash := rs . ProposalBlock . Hash ( )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- voteCh // prevote
2015-09-22 21:12:34 -04:00
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrevote , cs1 , rs . ProposalBlock . Hash ( ) , rs . ProposalBlockParts . Header ( ) , nil , cs2 , cs3 , cs4 )
2015-12-13 14:56:05 -05:00
<- voteCh //precommit
2015-09-22 21:12:34 -04: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 21:12:34 -04:00
2016-07-11 20:40:48 -04:00
rs = cs1 . GetRoundState ( )
2015-09-22 21:12:34 -04:00
// add precommits from the rest
2016-07-11 20:40:48 -04: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 21:12:34 -04:00
2015-12-13 14:56:05 -05: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 14:56:05 -05:00
propBlockParts := propBlock . MakePartSet ( )
2015-09-22 21:12:34 -04:00
incrementRound ( cs2 , cs3 , cs4 )
// timeout to new round
2015-12-13 14:56:05 -05:00
re = <- timeoutWaitCh
2016-01-28 19:44:44 -08:00
rs = re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-12-13 14:56:05 -05: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 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- newRoundCh
log . Notice ( "#### ONTO ROUND 1" )
2015-09-22 21:12:34 -04: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 14:56:05 -05:00
// but we should receive the proposal
select {
case <- proposalCh :
case <- timeoutProposeCh :
<- proposalCh
}
2015-09-22 21:12:34 -04:00
// go to prevote, prevote for locked block (not proposal)
2015-12-13 14:56:05 -05:00
<- voteCh
2015-12-01 20:12:01 -08:00
validatePrevote ( t , cs1 , 0 , vss [ 0 ] , lockedBlockHash )
2015-12-13 14:56:05 -05:00
// now lets add prevotes from everyone else for nil (a polka!)
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrevote , cs1 , nil , types . PartSetHeader { } , nil , cs2 , cs3 , cs4 )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05: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 21:12:34 -04:00
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrecommit , cs1 , nil , types . PartSetHeader { } , nil , cs2 , cs3 )
2015-12-13 14:56:05 -05:00
<- newRoundCh
2015-09-22 21:12:34 -04: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 15:57:57 -05: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 14:56:05 -05: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 14:56:05 -05:00
voteCh := subscribeToVoter ( cs1 , cs1 . privValidator . Address )
2015-09-22 21:12:34 -04:00
// start round and wait for propose and prevote
2015-12-13 14:56:05 -05:00
startTestRound ( cs1 , cs1 . Height , 0 )
<- newRoundCh
re := <- proposalCh
2016-01-28 19:44:44 -08:00
rs := re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-12-13 14:56:05 -05:00
propBlock := rs . ProposalBlock
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- voteCh // prevote
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
validatePrevote ( t , cs1 , 0 , vss [ 0 ] , propBlock . Hash ( ) )
2015-09-22 21:12:34 -04: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 14:56:05 -05:00
/ *
_ , v1 := cs1 . Validators . GetByAddress ( vss [ 0 ] . Address )
v1 . VotingPower = 1
if updated := cs1 . Validators . Update ( v1 ) ; ! updated {
2016-07-11 21:10:05 -04:00
panic ( "failed to update validator" )
2015-12-13 14:56:05 -05:00
} * /
2015-09-22 21:12:34 -04:00
log . Warn ( "old prop" , "hash" , fmt . Sprintf ( "%X" , propBlock . Hash ( ) ) )
// we do see them precommit nil
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrecommit , cs1 , nil , types . PartSetHeader { } , nil , cs2 , cs3 , cs4 )
2015-09-22 21:12:34 -04:00
2015-12-01 20:12:01 -08:00
prop , propBlock := decideProposal ( cs1 , cs2 , cs2 . Height , cs2 . Round + 1 )
2015-12-13 14:56:05 -05:00
propBlockHash := propBlock . Hash ( )
propBlockParts := propBlock . MakePartSet ( )
2015-09-22 21:12:34 -04:00
incrementRound ( cs2 , cs3 , cs4 )
2015-12-13 14:56:05 -05: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 21:12:34 -04: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 14:56:05 -05:00
// but we should receive the proposal
select {
case re = <- proposalCh :
case <- timeoutProposeCh :
re = <- proposalCh
}
2016-01-28 19:44:44 -08:00
rs = re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-12-13 14:56:05 -05:00
if rs . LockedBlock != nil {
2016-07-11 21:10:05 -04:00
panic ( "we should not be locked!" )
2015-09-22 21:12:34 -04:00
}
log . Warn ( "new prop" , "hash" , fmt . Sprintf ( "%X" , propBlockHash ) )
// go to prevote, prevote for proposal block
2015-12-13 14:56:05 -05:00
<- voteCh
2015-12-01 20:12:01 -08:00
validatePrevote ( t , cs1 , 1 , vss [ 0 ] , propBlockHash )
2015-09-22 21:12:34 -04:00
// now we see the others prevote for it, so we should lock on it
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrevote , cs1 , propBlockHash , propBlockParts . Header ( ) , nil , cs2 , cs3 , cs4 )
2015-12-13 14:56:05 -05:00
<- voteCh // precommit
2015-09-22 21:12:34 -04: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 21:12:34 -04:00
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrecommit , cs1 , nil , types . PartSetHeader { } , nil , cs2 , cs3 )
2015-12-13 14:56:05 -05:00
<- timeoutWaitCh
2015-09-22 21:12:34 -04:00
incrementRound ( cs2 , cs3 , cs4 )
2015-12-13 14:56:05 -05:00
<- newRoundCh
log . Notice ( "### ONTO ROUND 2" )
2015-09-22 21:12:34 -04:00
/ * Round3
we see the polka from round 1 but we shouldn ' t unlock !
* /
// timeout of propose
2015-12-13 14:56:05 -05:00
<- timeoutProposeCh
2015-09-22 21:12:34 -04:00
// finish prevote
2015-12-13 14:56:05 -05:00
<- voteCh
2015-09-22 21:12:34 -04: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 21:12:34 -04:00
2016-01-18 12:18:09 -08:00
newStepCh := subscribeToEvent ( cs1 . evsw , "tester" , types . EventStringNewRoundStep ( ) , 1 )
2015-12-13 19:30:15 -05:00
2015-09-22 21:12:34 -04:00
// add prevotes from the earlier round
addVoteToFromMany ( cs1 , prevotes , cs2 , cs3 , cs4 )
log . Warn ( "Done adding prevotes!" )
2015-12-13 19:30:15 -05:00
ensureNoNewStep ( newStepCh )
2015-09-22 21:12:34 -04:00
}
// 4 vals.
2015-12-13 14:56:05 -05: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 21:12:34 -04:00
func TestLockPOLSafety2 ( t * testing . T ) {
2016-01-18 15:57:57 -05: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 21:12:34 -04: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 14:56:05 -05:00
voteCh := subscribeToVoter ( cs1 , cs1 . privValidator . Address )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05: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 21:12:34 -04:00
2015-12-13 14:56:05 -05: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 21:12:34 -04:00
2015-12-13 14:56:05 -05: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 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
incrementRound ( cs2 , cs3 , cs4 )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
cs1 . updateRoundStep ( 0 , RoundStepPrecommitWait )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
log . Notice ( "### ONTO Round 1" )
// jump in at round 1
height := cs1 . Height
startTestRound ( cs1 , height , 1 )
<- newRoundCh
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
cs1 . SetProposalAndBlock ( prop1 , propBlock1 , propBlockParts1 , "some peer" )
<- proposalCh
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- voteCh // prevote
2015-09-22 21:12:34 -04:00
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrevote , cs1 , propBlockHash1 , propBlockParts1 . Header ( ) , nil , cs2 , cs3 , cs4 )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05: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 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
// add precommits from the rest
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrecommit , cs1 , nil , types . PartSetHeader { } , nil , cs2 , cs4 )
signAddVoteToFrom ( types . VoteTypePrecommit , cs1 , cs3 , propBlockHash1 , propBlockParts1 . Header ( ) , nil )
2015-09-22 21:12:34 -04:00
incrementRound ( cs2 , cs3 , cs4 )
2015-12-13 14:56:05 -05:00
// timeout of precommit wait to new round
<- timeoutWaitCh
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05: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 21:10:05 -04:00
panic ( err )
2015-12-13 14:56:05 -05:00
}
cs1 . SetProposalAndBlock ( newProp , propBlock0 , propBlockParts0 , "some peer" )
addVoteToFromMany ( cs1 , prevotes , cs2 , cs3 , cs4 ) // add the pol votes
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- newRoundCh
log . Notice ( "### ONTO Round 2" )
/ * Round2
// now we see the polka from round 1, but we shouldnt unlock
2015-09-22 21:12:34 -04:00
* /
2015-12-13 14:56:05 -05:00
select {
case <- timeoutProposeCh :
<- proposalCh
case <- proposalCh :
}
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
select {
case <- unlockCh :
2016-07-11 21:10:05 -04:00
panic ( "validator unlocked using an old polka" )
2015-12-13 14:56:05 -05:00
case <- voteCh :
// prevote our locked block
2015-09-22 21:12:34 -04:00
}
2015-12-13 14:56:05 -05:00
validatePrevote ( t , cs1 , 2 , vss [ 0 ] , propBlockHash1 )
2015-09-22 21:12:34 -04:00
}
//------------------------------------------------------------------------------------------
// SlashingSuite
2015-12-13 14:56:05 -05:00
// TODO: Slashing
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
/ *
2015-09-22 21:12:34 -04:00
func TestSlashingPrevotes ( t * testing . T ) {
2016-01-18 15:57:57 -05:00
cs1 , vss := randConsensusState ( 2 )
2015-12-01 20:12:01 -08:00
cs2 := vss [ 1 ]
2015-12-13 14:56:05 -05:00
2015-12-14 00:38:19 -05: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 14:56:05 -05:00
voteCh := subscribeToVoter ( cs1 , cs1 . privValidator . Address )
2015-09-22 21:12:34 -04:00
// start round and wait for propose and prevote
2015-12-13 14:56:05 -05:00
startTestRound ( cs1 , cs1 . Height , 0 )
<- newRoundCh
re := <- proposalCh
<- voteCh // prevote
2016-01-28 19:44:44 -08:00
rs := re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-09-22 21:12:34 -04: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 20:40:48 -04:00
hash := rs . ProposalBlock . Hash ( )
2015-09-22 21:12:34 -04:00
hash [ 0 ] = byte ( hash [ 0 ] + 1 ) % 255
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrevote , cs1 , cs2 , hash , rs . ProposalBlockParts . Header ( ) , nil )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- timeoutWaitCh
2015-09-22 21:12:34 -04: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 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrevote , cs1 , cs2 , rs . ProposalBlock . Hash ( ) , rs . ProposalBlockParts . Header ( ) , nil )
2015-09-22 21:12:34 -04:00
2015-11-01 11:34:08 -08:00
// XXX: Check for existence of Dupeout info
2015-09-22 21:12:34 -04:00
}
func TestSlashingPrecommits ( t * testing . T ) {
2016-01-18 15:57:57 -05:00
cs1 , vss := randConsensusState ( 2 )
2015-12-01 20:12:01 -08:00
cs2 := vss [ 1 ]
2015-12-13 14:56:05 -05:00
2015-12-14 00:38:19 -05: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 14:56:05 -05:00
voteCh := subscribeToVoter ( cs1 , cs1 . privValidator . Address )
2015-09-22 21:12:34 -04:00
// start round and wait for propose and prevote
2015-12-13 14:56:05 -05:00
startTestRound ( cs1 , cs1 . Height , 0 )
<- newRoundCh
re := <- proposalCh
<- voteCh // prevote
2015-09-22 21:12:34 -04:00
// add prevote from cs2
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrevote , cs1 , cs2 , rs . ProposalBlock . Hash ( ) , rs . ProposalBlockParts . Header ( ) , nil )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- voteCh // precommit
2015-09-22 21:12:34 -04: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 14:56:05 -05:00
hash := rs . ProposalBlock . Hash ( )
2015-09-22 21:12:34 -04:00
hash [ 0 ] = byte ( hash [ 0 ] + 1 ) % 255
2016-07-11 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrecommit , cs1 , cs2 , hash , rs . ProposalBlockParts . Header ( ) , nil )
2015-09-22 21:12:34 -04: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 20:40:48 -04:00
signAddVoteToFrom ( types . VoteTypePrecommit , cs1 , cs2 , rs . ProposalBlock . Hash ( ) , rs . ProposalBlockParts . Header ( ) , nil )
2015-09-22 21:12:34 -04:00
2015-11-01 11:34:08 -08:00
// XXX: Check for existence of Dupeout info
2015-09-22 21:12:34 -04:00
}
2015-12-13 14:56:05 -05:00
* /
2015-09-22 21:12:34 -04: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 15:57:57 -05: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 21:12:34 -04: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 14:56:05 -05:00
voteCh := subscribeToVoter ( cs1 , cs1 . privValidator . Address )
2015-09-22 21:12:34 -04:00
// start round and wait for propose and prevote
2015-12-13 14:56:05 -05:00
startTestRound ( cs1 , cs1 . Height , 0 )
<- newRoundCh
re := <- proposalCh
2016-01-28 19:44:44 -08:00
rs := re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-12-13 14:56:05 -05:00
propBlock := rs . ProposalBlock
propBlockParts := propBlock . MakePartSet ( )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
<- voteCh // prevote
2015-09-22 21:12:34 -04:00
2016-07-11 20:40:48 -04:00
signAddVoteToFromMany ( types . VoteTypePrevote , cs1 , propBlock . Hash ( ) , propBlockParts . Header ( ) , nil , cs3 , cs4 )
2015-12-13 14:56:05 -05:00
<- voteCh // precommit
2015-09-22 21:12:34 -04:00
// the proposed block should now be locked and our precommit added
2015-12-13 14:56:05 -05:00
validatePrecommit ( t , cs1 , 0 , 0 , vss [ 0 ] , propBlock . Hash ( ) , propBlock . Hash ( ) )
2015-09-22 21:12:34 -04:00
// add precommits from the rest
2016-07-11 20:40:48 -04: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 21:12:34 -04:00
// we receive this later, but cs3 might receive it earlier and with ours will go to commit!
2015-12-13 14:56:05 -05:00
precommit4 := signVote ( cs4 , types . VoteTypePrecommit , propBlock . Hash ( ) , propBlockParts . Header ( ) )
2015-09-22 21:12:34 -04:00
incrementRound ( cs2 , cs3 , cs4 )
// timeout to new round
2015-12-13 14:56:05 -05:00
<- timeoutWaitCh
re = <- newRoundCh
2016-01-28 19:44:44 -08:00
rs = re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
log . Notice ( "### ONTO ROUND 1" )
2015-09-22 21:12:34 -04: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 14:56:05 -05:00
<- voteCh // prevote
validatePrevote ( t , cs1 , 0 , vss [ 0 ] , rs . LockedBlock . Hash ( ) )
2015-09-22 21:12:34 -04: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 14:56:05 -05:00
<- newBlockCh
re = <- newRoundCh
2016-01-28 19:44:44 -08:00
rs = re . ( types . EventDataRoundState ) . RoundState . ( * RoundState )
2015-09-22 21:12:34 -04:00
2015-12-13 14:56:05 -05:00
if rs . Height != 2 {
2016-07-11 21:10:05 -04:00
panic ( "expected height to increment" )
2015-09-22 21:12:34 -04:00
}
}