|
|
@ -197,7 +197,9 @@ func TestStateBadProposal(t *testing.T) { |
|
|
|
stateHash[0] = byte((stateHash[0] + 1) % 255) |
|
|
|
propBlock.AppHash = stateHash |
|
|
|
propBlockParts := propBlock.MakePartSet(partSize) |
|
|
|
proposal := types.NewProposal(vs2.Height, round, propBlockParts.Header(), -1, types.BlockID{}) |
|
|
|
proposal := types.NewProposal( |
|
|
|
vs2.Height, round, -1, |
|
|
|
types.BlockID{propBlock.Hash(), propBlockParts.Header()}) |
|
|
|
if err := vs2.SignProposal(config.ChainID(), proposal); err != nil { |
|
|
|
t.Fatal("failed to sign bad proposal", err) |
|
|
|
} |
|
|
@ -811,6 +813,7 @@ func TestStateLockPOLSafety2(t *testing.T) { |
|
|
|
_, propBlock0 := decideProposal(cs1, vss[0], height, round) |
|
|
|
propBlockHash0 := propBlock0.Hash() |
|
|
|
propBlockParts0 := propBlock0.MakePartSet(partSize) |
|
|
|
propBlockID0 := types.BlockID{propBlockHash0, propBlockParts0.Header()} |
|
|
|
|
|
|
|
// the others sign a polka but we don't see it
|
|
|
|
prevotes := signVotes(types.PrevoteType, propBlockHash0, propBlockParts0.Header(), vs2, vs3, vs4) |
|
|
@ -819,7 +822,6 @@ func TestStateLockPOLSafety2(t *testing.T) { |
|
|
|
prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1) |
|
|
|
propBlockHash1 := propBlock1.Hash() |
|
|
|
propBlockParts1 := propBlock1.MakePartSet(partSize) |
|
|
|
propBlockID1 := types.BlockID{propBlockHash1, propBlockParts1.Header()} |
|
|
|
|
|
|
|
incrementRound(vs2, vs3, vs4) |
|
|
|
|
|
|
@ -854,7 +856,7 @@ func TestStateLockPOLSafety2(t *testing.T) { |
|
|
|
|
|
|
|
round = round + 1 // moving to the next round
|
|
|
|
// in round 2 we see the polkad block from round 0
|
|
|
|
newProp := types.NewProposal(height, round, propBlockParts0.Header(), 0, propBlockID1) |
|
|
|
newProp := types.NewProposal(height, round, 0, propBlockID0) |
|
|
|
if err := vs3.SignProposal(config.ChainID(), newProp); err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
@ -909,7 +911,7 @@ func TestProposeValidBlock(t *testing.T) { |
|
|
|
ensurePrevote(voteCh, height, round) |
|
|
|
validatePrevote(t, cs1, round, vss[0], propBlockHash) |
|
|
|
|
|
|
|
// the others sign a polka but we don't see it
|
|
|
|
// the others sign a polka
|
|
|
|
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4) |
|
|
|
|
|
|
|
ensurePrecommit(voteCh, height, round) |
|
|
@ -964,6 +966,119 @@ func TestProposeValidBlock(t *testing.T) { |
|
|
|
rs = cs1.GetRoundState() |
|
|
|
assert.True(t, bytes.Equal(rs.ProposalBlock.Hash(), propBlockHash)) |
|
|
|
assert.True(t, bytes.Equal(rs.ProposalBlock.Hash(), rs.ValidBlock.Hash())) |
|
|
|
assert.True(t, rs.Proposal.POLRound == rs.ValidRound) |
|
|
|
assert.True(t, bytes.Equal(rs.Proposal.BlockID.Hash, rs.ValidBlock.Hash())) |
|
|
|
} |
|
|
|
|
|
|
|
// What we want:
|
|
|
|
// P0 miss to lock B but set valid block to B after receiving delayed prevote.
|
|
|
|
func TestSetValidBlockOnDelayedPrevote(t *testing.T) { |
|
|
|
cs1, vss := randConsensusState(4) |
|
|
|
vs2, vs3, vs4 := vss[1], vss[2], vss[3] |
|
|
|
height, round := cs1.Height, cs1.Round |
|
|
|
|
|
|
|
partSize := types.BlockPartSizeBytes |
|
|
|
|
|
|
|
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) |
|
|
|
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) |
|
|
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) |
|
|
|
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) |
|
|
|
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) |
|
|
|
|
|
|
|
// start round and wait for propose and prevote
|
|
|
|
startTestRound(cs1, cs1.Height, round) |
|
|
|
ensureNewRound(newRoundCh, height, round) |
|
|
|
|
|
|
|
ensureNewProposal(proposalCh, height, round) |
|
|
|
rs := cs1.GetRoundState() |
|
|
|
propBlock := rs.ProposalBlock |
|
|
|
propBlockHash := propBlock.Hash() |
|
|
|
propBlockParts := propBlock.MakePartSet(partSize) |
|
|
|
|
|
|
|
ensurePrevote(voteCh, height, round) |
|
|
|
validatePrevote(t, cs1, round, vss[0], propBlockHash) |
|
|
|
|
|
|
|
// vs2 send prevote for propBlock
|
|
|
|
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2) |
|
|
|
|
|
|
|
// vs3 send prevote nil
|
|
|
|
signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs3) |
|
|
|
|
|
|
|
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds()) |
|
|
|
|
|
|
|
ensurePrecommit(voteCh, height, round) |
|
|
|
// we should have precommitted
|
|
|
|
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil) |
|
|
|
|
|
|
|
rs = cs1.GetRoundState() |
|
|
|
|
|
|
|
assert.True(t, rs.ValidBlock == nil) |
|
|
|
assert.True(t, rs.ValidBlockParts == nil) |
|
|
|
assert.True(t, rs.ValidRound == -1) |
|
|
|
|
|
|
|
// vs2 send (delayed) prevote for propBlock
|
|
|
|
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs4) |
|
|
|
|
|
|
|
ensureNewValidBlock(validBlockCh, height, round) |
|
|
|
|
|
|
|
rs = cs1.GetRoundState() |
|
|
|
|
|
|
|
assert.True(t, bytes.Equal(rs.ValidBlock.Hash(), propBlockHash)) |
|
|
|
assert.True(t, rs.ValidBlockParts.Header().Equals(propBlockParts.Header())) |
|
|
|
assert.True(t, rs.ValidRound == round) |
|
|
|
} |
|
|
|
|
|
|
|
// What we want:
|
|
|
|
// P0 miss to lock B as Proposal Block is missing, but set valid block to B after
|
|
|
|
// receiving delayed Block Proposal.
|
|
|
|
func TestSetValidBlockOnDelayedProposal(t *testing.T) { |
|
|
|
cs1, vss := randConsensusState(4) |
|
|
|
vs2, vs3, vs4 := vss[1], vss[2], vss[3] |
|
|
|
height, round := cs1.Height, cs1.Round |
|
|
|
|
|
|
|
partSize := types.BlockPartSizeBytes |
|
|
|
|
|
|
|
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) |
|
|
|
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) |
|
|
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) |
|
|
|
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) |
|
|
|
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) |
|
|
|
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) |
|
|
|
|
|
|
|
round = round + 1 // move to round in which P0 is not proposer
|
|
|
|
incrementRound(vs2, vs3, vs4) |
|
|
|
|
|
|
|
startTestRound(cs1, cs1.Height, round) |
|
|
|
ensureNewRound(newRoundCh, height, round) |
|
|
|
|
|
|
|
ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.TimeoutPropose.Nanoseconds()) |
|
|
|
|
|
|
|
ensurePrevote(voteCh, height, round) |
|
|
|
validatePrevote(t, cs1, round, vss[0], nil) |
|
|
|
|
|
|
|
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1) |
|
|
|
propBlockHash := propBlock.Hash() |
|
|
|
propBlockParts := propBlock.MakePartSet(partSize) |
|
|
|
|
|
|
|
// vs2, vs3 and vs4 send prevote for propBlock
|
|
|
|
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4) |
|
|
|
ensureNewValidBlock(validBlockCh, height, round) |
|
|
|
|
|
|
|
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds()) |
|
|
|
|
|
|
|
ensurePrecommit(voteCh, height, round) |
|
|
|
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil) |
|
|
|
|
|
|
|
if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
|
|
|
|
ensureNewProposal(proposalCh, height, round) |
|
|
|
rs := cs1.GetRoundState() |
|
|
|
|
|
|
|
assert.True(t, bytes.Equal(rs.ValidBlock.Hash(), propBlockHash)) |
|
|
|
assert.True(t, rs.ValidBlockParts.Header().Equals(propBlockParts.Header())) |
|
|
|
assert.True(t, rs.ValidRound == round) |
|
|
|
} |
|
|
|
|
|
|
|
// 4 vals, 3 Nil Precommits at P0
|
|
|
@ -1078,6 +1193,80 @@ func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) { |
|
|
|
validatePrevote(t, cs1, round, vss[0], nil) |
|
|
|
} |
|
|
|
|
|
|
|
// What we want:
|
|
|
|
// P0 emit NewValidBlock event upon receiving 2/3+ Precommit for B but hasn't received block B yet
|
|
|
|
func TestEmitNewValidBlockEventOnCommitWithoutBlock(t *testing.T) { |
|
|
|
cs1, vss := randConsensusState(4) |
|
|
|
vs2, vs3, vs4 := vss[1], vss[2], vss[3] |
|
|
|
height, round := cs1.Height, 1 |
|
|
|
|
|
|
|
incrementRound(vs2, vs3, vs4) |
|
|
|
|
|
|
|
partSize := types.BlockPartSizeBytes |
|
|
|
|
|
|
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) |
|
|
|
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) |
|
|
|
|
|
|
|
_, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round) |
|
|
|
propBlockHash := propBlock.Hash() |
|
|
|
propBlockParts := propBlock.MakePartSet(partSize) |
|
|
|
|
|
|
|
// start round in which PO is not proposer
|
|
|
|
startTestRound(cs1, height, round) |
|
|
|
ensureNewRound(newRoundCh, height, round) |
|
|
|
|
|
|
|
// vs2, vs3 and vs4 send precommit for propBlock
|
|
|
|
signAddVotes(cs1, types.PrecommitType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4) |
|
|
|
ensureNewValidBlock(validBlockCh, height, round) |
|
|
|
|
|
|
|
rs := cs1.GetRoundState() |
|
|
|
assert.True(t, rs.Step == cstypes.RoundStepCommit) |
|
|
|
assert.True(t, rs.ProposalBlock == nil) |
|
|
|
assert.True(t, rs.ProposalBlockParts.Header().Equals(propBlockParts.Header())) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// What we want:
|
|
|
|
// P0 receives 2/3+ Precommit for B for round 0, while being in round 1. It emits NewValidBlock event.
|
|
|
|
// After receiving block, it executes block and moves to the next height.
|
|
|
|
func TestCommitFromPreviousRound(t *testing.T) { |
|
|
|
cs1, vss := randConsensusState(4) |
|
|
|
vs2, vs3, vs4 := vss[1], vss[2], vss[3] |
|
|
|
height, round := cs1.Height, 1 |
|
|
|
|
|
|
|
partSize := types.BlockPartSizeBytes |
|
|
|
|
|
|
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) |
|
|
|
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) |
|
|
|
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) |
|
|
|
|
|
|
|
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round) |
|
|
|
propBlockHash := propBlock.Hash() |
|
|
|
propBlockParts := propBlock.MakePartSet(partSize) |
|
|
|
|
|
|
|
// start round in which PO is not proposer
|
|
|
|
startTestRound(cs1, height, round) |
|
|
|
ensureNewRound(newRoundCh, height, round) |
|
|
|
|
|
|
|
// vs2, vs3 and vs4 send precommit for propBlock for the previous round
|
|
|
|
signAddVotes(cs1, types.PrecommitType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4) |
|
|
|
|
|
|
|
ensureNewValidBlock(validBlockCh, height, round) |
|
|
|
|
|
|
|
rs := cs1.GetRoundState() |
|
|
|
assert.True(t, rs.Step == cstypes.RoundStepCommit) |
|
|
|
assert.True(t, rs.CommitRound == vs2.Round) |
|
|
|
assert.True(t, rs.ProposalBlock == nil) |
|
|
|
assert.True(t, rs.ProposalBlockParts.Header().Equals(propBlockParts.Header())) |
|
|
|
|
|
|
|
if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
|
|
|
|
ensureNewProposal(proposalCh, height, round) |
|
|
|
ensureNewRound(newRoundCh, height+1, 0) |
|
|
|
} |
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------------
|
|
|
|
// SlashingSuite
|
|
|
|
// TODO: Slashing
|
|
|
|