mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-14 13:51:21 +00:00
Add validator index and address to Vote.
This commit is contained in:
@ -25,19 +25,23 @@ var config cfg.Config // NOTE: must be reset for each _test.go file
|
|||||||
var ensureTimeout = time.Duration(2)
|
var ensureTimeout = time.Duration(2)
|
||||||
|
|
||||||
type validatorStub struct {
|
type validatorStub struct {
|
||||||
|
Index int // Validator index. NOTE: we don't assume validator set changes.
|
||||||
Height int
|
Height int
|
||||||
Round int
|
Round int
|
||||||
*types.PrivValidator
|
*types.PrivValidator
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewValidatorStub(privValidator *types.PrivValidator) *validatorStub {
|
func NewValidatorStub(privValidator *types.PrivValidator, valIndex int) *validatorStub {
|
||||||
return &validatorStub{
|
return &validatorStub{
|
||||||
|
Index: valIndex,
|
||||||
PrivValidator: privValidator,
|
PrivValidator: privValidator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||||
vote := &types.Vote{
|
vote := &types.Vote{
|
||||||
|
ValidatorIndex: vs.Index,
|
||||||
|
ValidatorAddress: vs.PrivValidator.Address,
|
||||||
Height: vs.Height,
|
Height: vs.Height,
|
||||||
Round: vs.Round,
|
Round: vs.Round,
|
||||||
Type: voteType,
|
Type: voteType,
|
||||||
@ -48,7 +52,10 @@ func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartS
|
|||||||
return vote, err
|
return vote, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// convenienve function for testing
|
//-------------------------------------------------------------------------------
|
||||||
|
// Convenience functions
|
||||||
|
|
||||||
|
// Sign vote for type/hash/header
|
||||||
func signVote(vs *validatorStub, voteType byte, hash []byte, header types.PartSetHeader) *types.Vote {
|
func signVote(vs *validatorStub, voteType byte, hash []byte, header types.PartSetHeader) *types.Vote {
|
||||||
v, err := vs.signVote(voteType, hash, header)
|
v, err := vs.signVote(voteType, hash, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -57,8 +64,8 @@ func signVote(vs *validatorStub, voteType byte, hash []byte, header types.PartSe
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// create proposal block from cs1 but sign it with vs
|
// Create proposal block from cs1 but sign it with vs
|
||||||
func decideProposal(cs1 *ConsensusState, cs2 *validatorStub, height, round int) (proposal *types.Proposal, block *types.Block) {
|
func decideProposal(cs1 *ConsensusState, vs *validatorStub, height, round int) (proposal *types.Proposal, block *types.Block) {
|
||||||
block, blockParts := cs1.createProposalBlock()
|
block, blockParts := cs1.createProposalBlock()
|
||||||
if block == nil { // on error
|
if block == nil { // on error
|
||||||
panic("error creating proposal block")
|
panic("error creating proposal block")
|
||||||
@ -66,93 +73,19 @@ func decideProposal(cs1 *ConsensusState, cs2 *validatorStub, height, round int)
|
|||||||
|
|
||||||
// Make proposal
|
// Make proposal
|
||||||
proposal = types.NewProposal(height, round, blockParts.Header(), cs1.Votes.POLRound())
|
proposal = types.NewProposal(height, round, blockParts.Header(), cs1.Votes.POLRound())
|
||||||
if err := cs2.SignProposal(config.GetString("chain_id"), proposal); err != nil {
|
if err := vs.SignProposal(config.GetString("chain_id"), proposal); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------
|
func addVotes(to *ConsensusState, votes ...*types.Vote) {
|
||||||
// utils
|
for _, vote := range votes {
|
||||||
|
to.peerMsgQueue <- msgInfo{Msg: &VoteMessage{vote}}
|
||||||
/*
|
|
||||||
func nilRound(t *testing.T, cs1 *ConsensusState, vss ...*validatorStub) {
|
|
||||||
cs1.mtx.Lock()
|
|
||||||
height, round := cs1.Height, cs1.Round
|
|
||||||
cs1.mtx.Unlock()
|
|
||||||
|
|
||||||
waitFor(t, cs1, height, round, RoundStepPrevote)
|
|
||||||
|
|
||||||
signAddVoteToFromMany(types.VoteTypePrevote, cs1, nil, cs1.ProposalBlockParts.Header(), vss...)
|
|
||||||
|
|
||||||
waitFor(t, cs1, height, round, RoundStepPrecommit)
|
|
||||||
|
|
||||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, cs1.ProposalBlockParts.Header(), vss...)
|
|
||||||
|
|
||||||
waitFor(t, cs1, height, round+1, RoundStepNewRound)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// NOTE: this switches the propser as far as `perspectiveOf` is concerned,
|
|
||||||
// but for simplicity we return a block it generated.
|
|
||||||
func changeProposer(t *testing.T, perspectiveOf *ConsensusState, newProposer *validatorStub) *types.Block {
|
|
||||||
_, v1 := perspectiveOf.Validators.GetByAddress(perspectiveOf.privValidator.Address)
|
|
||||||
v1.Accum, v1.VotingPower = 0, 0
|
|
||||||
if updated := perspectiveOf.Validators.Update(v1); !updated {
|
|
||||||
panic("failed to update validator")
|
|
||||||
}
|
|
||||||
_, v2 := perspectiveOf.Validators.GetByAddress(newProposer.Address)
|
|
||||||
v2.Accum, v2.VotingPower = 100, 100
|
|
||||||
if updated := perspectiveOf.Validators.Update(v2); !updated {
|
|
||||||
panic("failed to update validator")
|
|
||||||
}
|
|
||||||
|
|
||||||
// make the proposal
|
|
||||||
propBlock, _ := perspectiveOf.createProposalBlock()
|
|
||||||
if propBlock == nil {
|
|
||||||
panic("Failed to create proposal block with cs2")
|
|
||||||
}
|
|
||||||
return propBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
func fixVotingPower(t *testing.T, cs1 *ConsensusState, addr2 []byte) {
|
|
||||||
_, v1 := cs1.Validators.GetByAddress(cs1.privValidator.Address)
|
|
||||||
_, v2 := cs1.Validators.GetByAddress(addr2)
|
|
||||||
v1.Accum, v1.VotingPower = v2.Accum, v2.VotingPower
|
|
||||||
if updated := cs1.Validators.Update(v1); !updated {
|
|
||||||
panic("failed to update validator")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addVoteToFromMany(to *ConsensusState, votes []*types.Vote, froms ...*validatorStub) {
|
func signVotes(voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) []*types.Vote {
|
||||||
if len(votes) != len(froms) {
|
|
||||||
panic("len(votes) and len(froms) must match")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, from := range froms {
|
|
||||||
addVoteToFrom(to, from, votes[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addVoteToFrom(to *ConsensusState, from *validatorStub, vote *types.Vote) {
|
|
||||||
to.mtx.Lock() // NOTE: wont need this when the vote comes with the index!
|
|
||||||
valIndex, _ := to.Validators.GetByAddress(from.PrivValidator.Address)
|
|
||||||
to.mtx.Unlock()
|
|
||||||
|
|
||||||
to.peerMsgQueue <- msgInfo{Msg: &VoteMessage{valIndex, vote}}
|
|
||||||
// added, err := to.TryAddVote(valIndex, vote, "")
|
|
||||||
/*
|
|
||||||
if _, ok := err.(*types.ErrVoteConflictingSignature); ok {
|
|
||||||
// let it fly
|
|
||||||
} else if !added {
|
|
||||||
fmt.Println("to, from, vote:", to.Height, from.Height, vote.Height)
|
|
||||||
panic(fmt.Sprintln("Failed to add vote. Err:", err))
|
|
||||||
} else if err != nil {
|
|
||||||
panic(fmt.Sprintln("Failed to add vote:", err))
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func signVoteMany(voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) []*types.Vote {
|
|
||||||
votes := make([]*types.Vote, len(vss))
|
votes := make([]*types.Vote, len(vss))
|
||||||
for i, vs := range vss {
|
for i, vs := range vss {
|
||||||
votes[i] = signVote(vs, voteType, hash, header)
|
votes[i] = signVote(vs, voteType, hash, header)
|
||||||
@ -160,34 +93,9 @@ func signVoteMany(voteType byte, hash []byte, header types.PartSetHeader, vss ..
|
|||||||
return votes
|
return votes
|
||||||
}
|
}
|
||||||
|
|
||||||
// add vote to one cs from another
|
func signAddVotes(to *ConsensusState, voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) {
|
||||||
// if voteCh is not nil, read all votes
|
votes := signVotes(voteType, hash, header, vss...)
|
||||||
func signAddVoteToFromMany(voteType byte, to *ConsensusState, hash []byte, header types.PartSetHeader, voteCh chan interface{}, froms ...*validatorStub) {
|
addVotes(to, votes...)
|
||||||
var wg chan struct{} // when done reading all votes
|
|
||||||
if voteCh != nil {
|
|
||||||
wg = readVotes(voteCh, len(froms))
|
|
||||||
}
|
|
||||||
for _, from := range froms {
|
|
||||||
vote := signVote(from, voteType, hash, header)
|
|
||||||
addVoteToFrom(to, from, vote)
|
|
||||||
}
|
|
||||||
|
|
||||||
if voteCh != nil {
|
|
||||||
<-wg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func signAddVoteToFrom(voteType byte, to *ConsensusState, from *validatorStub, hash []byte, header types.PartSetHeader, voteCh chan interface{}) *types.Vote {
|
|
||||||
var wg chan struct{} // when done reading all votes
|
|
||||||
if voteCh != nil {
|
|
||||||
wg = readVotes(voteCh, 1)
|
|
||||||
}
|
|
||||||
vote := signVote(from, voteType, hash, header)
|
|
||||||
addVoteToFrom(to, from, vote)
|
|
||||||
if voteCh != nil {
|
|
||||||
<-wg
|
|
||||||
}
|
|
||||||
return vote
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureNoNewStep(stepCh chan interface{}) {
|
func ensureNoNewStep(stepCh chan interface{}) {
|
||||||
@ -200,39 +108,6 @@ func ensureNoNewStep(stepCh chan interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func ensureNoNewStep(t *testing.T, cs *ConsensusState) {
|
|
||||||
timeout := time.NewTicker(ensureTimeout * time.Second)
|
|
||||||
select {
|
|
||||||
case <-timeout.C:
|
|
||||||
break
|
|
||||||
case <-cs.NewStepCh():
|
|
||||||
panic("We should be stuck waiting for more votes, not moving to the next step")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureNewStep(t *testing.T, cs *ConsensusState) *RoundState {
|
|
||||||
timeout := time.NewTicker(ensureTimeout * time.Second)
|
|
||||||
select {
|
|
||||||
case <-timeout.C:
|
|
||||||
panic("We should have gone to the next step, not be stuck waiting")
|
|
||||||
case rs := <-cs.NewStepCh():
|
|
||||||
return rs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitFor(t *testing.T, cs *ConsensusState, height int, round int, step RoundStepType) {
|
|
||||||
for {
|
|
||||||
rs := ensureNewStep(t, cs)
|
|
||||||
if CompareHRS(rs.Height, rs.Round, rs.Step, height, round, step) < 0 {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func incrementHeight(vss ...*validatorStub) {
|
func incrementHeight(vss ...*validatorStub) {
|
||||||
for _, vs := range vss {
|
for _, vs := range vss {
|
||||||
vs.Height += 1
|
vs.Height += 1
|
||||||
@ -363,7 +238,7 @@ func randConsensusState(nValidators int) (*ConsensusState, []*validatorStub) {
|
|||||||
cs := newConsensusState(state, privVals[0], counter.NewCounterApplication(true))
|
cs := newConsensusState(state, privVals[0], counter.NewCounterApplication(true))
|
||||||
|
|
||||||
for i := 0; i < nValidators; i++ {
|
for i := 0; i < nValidators; i++ {
|
||||||
vss[i] = NewValidatorStub(privVals[i])
|
vss[i] = NewValidatorStub(privVals[i], i)
|
||||||
}
|
}
|
||||||
// since cs1 starts at 1
|
// since cs1 starts at 1
|
||||||
incrementHeight(vss[1:]...)
|
incrementHeight(vss[1:]...)
|
||||||
@ -379,7 +254,7 @@ func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} {
|
|||||||
v := <-voteCh0
|
v := <-voteCh0
|
||||||
vote := v.(types.EventDataVote)
|
vote := v.(types.EventDataVote)
|
||||||
// we only fire for our own votes
|
// we only fire for our own votes
|
||||||
if bytes.Equal(addr, vote.Address) {
|
if bytes.Equal(addr, vote.Vote.ValidatorAddress) {
|
||||||
voteCh <- v
|
voteCh <- v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ func (hvs *HeightVoteSet) addRound(round int) {
|
|||||||
|
|
||||||
// Duplicate votes return added=false, err=nil.
|
// Duplicate votes return added=false, err=nil.
|
||||||
// By convention, peerKey is "" if origin is self.
|
// By convention, peerKey is "" if origin is self.
|
||||||
func (hvs *HeightVoteSet) AddByIndex(valIndex int, vote *types.Vote, peerKey string) (added bool, address []byte, err error) {
|
func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerKey string) (added bool, err error) {
|
||||||
hvs.mtx.Lock()
|
hvs.mtx.Lock()
|
||||||
defer hvs.mtx.Unlock()
|
defer hvs.mtx.Unlock()
|
||||||
voteSet := hvs.getVoteSet(vote.Round, vote.Type)
|
voteSet := hvs.getVoteSet(vote.Round, vote.Type)
|
||||||
@ -117,7 +117,7 @@ func (hvs *HeightVoteSet) AddByIndex(valIndex int, vote *types.Vote, peerKey str
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
added, address, err = voteSet.AddByIndex(valIndex, vote)
|
added, err = voteSet.AddVote(vote)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,31 +17,34 @@ func TestPeerCatchupRounds(t *testing.T) {
|
|||||||
|
|
||||||
hvs := NewHeightVoteSet(config.GetString("chain_id"), 1, valSet)
|
hvs := NewHeightVoteSet(config.GetString("chain_id"), 1, valSet)
|
||||||
|
|
||||||
vote999_0 := makeVoteHR(t, 1, 999, privVals[0])
|
vote999_0 := makeVoteHR(t, 1, 999, privVals, 0)
|
||||||
added, _, err := hvs.AddByIndex(0, vote999_0, "peer1")
|
added, err := hvs.AddVote(vote999_0, "peer1")
|
||||||
if !added || err != nil {
|
if !added || err != nil {
|
||||||
t.Error("Expected to successfully add vote from peer", added, err)
|
t.Error("Expected to successfully add vote from peer", added, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
vote1000_0 := makeVoteHR(t, 1, 1000, privVals[0])
|
vote1000_0 := makeVoteHR(t, 1, 1000, privVals, 0)
|
||||||
added, _, err = hvs.AddByIndex(0, vote1000_0, "peer1")
|
added, err = hvs.AddVote(vote1000_0, "peer1")
|
||||||
if added {
|
if added {
|
||||||
t.Error("Expected to *not* add vote from peer, too many catchup rounds.")
|
t.Error("Expected to *not* add vote from peer, too many catchup rounds.")
|
||||||
}
|
}
|
||||||
|
|
||||||
added, _, err = hvs.AddByIndex(0, vote1000_0, "peer2")
|
added, err = hvs.AddVote(vote1000_0, "peer2")
|
||||||
if !added || err != nil {
|
if !added || err != nil {
|
||||||
t.Error("Expected to successfully add vote from another peer")
|
t.Error("Expected to successfully add vote from another peer")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeVoteHR(t *testing.T, height, round int, privVal *types.PrivValidator) *types.Vote {
|
func makeVoteHR(t *testing.T, height, round int, privVals []*types.PrivValidator, valIndex int) *types.Vote {
|
||||||
|
privVal := privVals[valIndex]
|
||||||
vote := &types.Vote{
|
vote := &types.Vote{
|
||||||
Height: height,
|
ValidatorAddress: privVal.Address,
|
||||||
Round: round,
|
ValidatorIndex: valIndex,
|
||||||
Type: types.VoteTypePrecommit,
|
Height: height,
|
||||||
BlockHash: []byte("fakehash"),
|
Round: round,
|
||||||
|
Type: types.VoteTypePrecommit,
|
||||||
|
BlockHash: []byte("fakehash"),
|
||||||
}
|
}
|
||||||
chainID := config.GetString("chain_id")
|
chainID := config.GetString("chain_id")
|
||||||
err := privVal.SignVote(chainID, vote)
|
err := privVal.SignVote(chainID, vote)
|
||||||
|
@ -201,7 +201,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte)
|
|||||||
cs.mtx.Unlock()
|
cs.mtx.Unlock()
|
||||||
ps.EnsureVoteBitArrays(height, valSize)
|
ps.EnsureVoteBitArrays(height, valSize)
|
||||||
ps.EnsureVoteBitArrays(height-1, lastCommitSize)
|
ps.EnsureVoteBitArrays(height-1, lastCommitSize)
|
||||||
ps.SetHasVote(msg.Vote, msg.ValidatorIndex)
|
ps.SetHasVote(msg.Vote)
|
||||||
|
|
||||||
conR.conS.peerMsgQueue <- msgInfo{msg, src.Key}
|
conR.conS.peerMsgQueue <- msgInfo{msg, src.Key}
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ func (conR *ConsensusReactor) registerEventCallbacks() {
|
|||||||
|
|
||||||
types.AddListenerForEvent(conR.evsw, "conR", types.EventStringVote(), func(data types.TMEventData) {
|
types.AddListenerForEvent(conR.evsw, "conR", types.EventStringVote(), func(data types.TMEventData) {
|
||||||
edv := data.(types.EventDataVote)
|
edv := data.(types.EventDataVote)
|
||||||
conR.broadcastHasVoteMessage(edv.Vote, edv.Index)
|
conR.broadcastHasVoteMessage(edv.Vote)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,12 +258,12 @@ func (conR *ConsensusReactor) broadcastNewRoundStep(rs *RoundState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Broadcasts HasVoteMessage to peers that care.
|
// Broadcasts HasVoteMessage to peers that care.
|
||||||
func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote, index int) {
|
func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) {
|
||||||
msg := &HasVoteMessage{
|
msg := &HasVoteMessage{
|
||||||
Height: vote.Height,
|
Height: vote.Height,
|
||||||
Round: vote.Round,
|
Round: vote.Round,
|
||||||
Type: vote.Type,
|
Type: vote.Type,
|
||||||
Index: index,
|
Index: vote.ValidatorIndex,
|
||||||
}
|
}
|
||||||
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg})
|
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg})
|
||||||
/*
|
/*
|
||||||
@ -613,8 +613,8 @@ func (ps *PeerState) SetHasProposalBlockPart(height int, round int, index int) {
|
|||||||
// Convenience function to send vote to peer.
|
// Convenience function to send vote to peer.
|
||||||
// Returns true if vote was sent.
|
// Returns true if vote was sent.
|
||||||
func (ps *PeerState) PickSendVote(votes types.VoteSetReader) (ok bool) {
|
func (ps *PeerState) PickSendVote(votes types.VoteSetReader) (ok bool) {
|
||||||
if index, vote, ok := ps.PickVoteToSend(votes); ok {
|
if vote, ok := ps.PickVoteToSend(votes); ok {
|
||||||
msg := &VoteMessage{index, vote}
|
msg := &VoteMessage{vote}
|
||||||
ps.Peer.Send(VoteChannel, struct{ ConsensusMessage }{msg})
|
ps.Peer.Send(VoteChannel, struct{ ConsensusMessage }{msg})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -622,12 +622,12 @@ func (ps *PeerState) PickSendVote(votes types.VoteSetReader) (ok bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// votes: Must be the correct Size() for the Height().
|
// votes: Must be the correct Size() for the Height().
|
||||||
func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (index int, vote *types.Vote, ok bool) {
|
func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (vote *types.Vote, ok bool) {
|
||||||
ps.mtx.Lock()
|
ps.mtx.Lock()
|
||||||
defer ps.mtx.Unlock()
|
defer ps.mtx.Unlock()
|
||||||
|
|
||||||
if votes.Size() == 0 {
|
if votes.Size() == 0 {
|
||||||
return 0, nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
height, round, type_, size := votes.Height(), votes.Round(), votes.Type(), votes.Size()
|
height, round, type_, size := votes.Height(), votes.Round(), votes.Type(), votes.Size()
|
||||||
@ -640,13 +640,13 @@ func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (index int, vote
|
|||||||
|
|
||||||
psVotes := ps.getVoteBitArray(height, round, type_)
|
psVotes := ps.getVoteBitArray(height, round, type_)
|
||||||
if psVotes == nil {
|
if psVotes == nil {
|
||||||
return 0, nil, false // Not something worth sending
|
return nil, false // Not something worth sending
|
||||||
}
|
}
|
||||||
if index, ok := votes.BitArray().Sub(psVotes).PickRandom(); ok {
|
if index, ok := votes.BitArray().Sub(psVotes).PickRandom(); ok {
|
||||||
ps.setHasVote(height, round, type_, index)
|
ps.setHasVote(height, round, type_, index)
|
||||||
return index, votes.GetByIndex(index), true
|
return votes.GetByIndex(index), true
|
||||||
}
|
}
|
||||||
return 0, nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PeerState) getVoteBitArray(height, round int, type_ byte) *BitArray {
|
func (ps *PeerState) getVoteBitArray(height, round int, type_ byte) *BitArray {
|
||||||
@ -741,11 +741,11 @@ func (ps *PeerState) ensureVoteBitArrays(height int, numValidators int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PeerState) SetHasVote(vote *types.Vote, index int) {
|
func (ps *PeerState) SetHasVote(vote *types.Vote) {
|
||||||
ps.mtx.Lock()
|
ps.mtx.Lock()
|
||||||
defer ps.mtx.Unlock()
|
defer ps.mtx.Unlock()
|
||||||
|
|
||||||
ps.setHasVote(vote.Height, vote.Round, vote.Type, index)
|
ps.setHasVote(vote.Height, vote.Round, vote.Type, vote.ValidatorIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PeerState) setHasVote(height int, round int, type_ byte, index int) {
|
func (ps *PeerState) setHasVote(height int, round int, type_ byte, index int) {
|
||||||
@ -985,12 +985,11 @@ func (m *BlockPartMessage) String() string {
|
|||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
type VoteMessage struct {
|
type VoteMessage struct {
|
||||||
ValidatorIndex int
|
Vote *types.Vote
|
||||||
Vote *types.Vote
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *VoteMessage) String() string {
|
func (m *VoteMessage) String() string {
|
||||||
return fmt.Sprintf("[Vote VI:%v V:%v VI:%v]", m.ValidatorIndex, m.Vote, m.ValidatorIndex)
|
return fmt.Sprintf("[Vote %v]", m.Vote)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
@ -374,15 +374,15 @@ func (cs *ConsensusState) OpenWAL(walDir string) (err error) {
|
|||||||
// TODO: should these return anything or let callers just use events?
|
// TODO: should these return anything or let callers just use events?
|
||||||
|
|
||||||
// May block on send if queue is full.
|
// May block on send if queue is full.
|
||||||
func (cs *ConsensusState) AddVote(valIndex int, vote *types.Vote, peerKey string) (added bool, address []byte, err error) {
|
func (cs *ConsensusState) AddVote(vote *types.Vote, peerKey string) (added bool, err error) {
|
||||||
if peerKey == "" {
|
if peerKey == "" {
|
||||||
cs.internalMsgQueue <- msgInfo{&VoteMessage{valIndex, vote}, ""}
|
cs.internalMsgQueue <- msgInfo{&VoteMessage{vote}, ""}
|
||||||
} else {
|
} else {
|
||||||
cs.peerMsgQueue <- msgInfo{&VoteMessage{valIndex, vote}, peerKey}
|
cs.peerMsgQueue <- msgInfo{&VoteMessage{vote}, peerKey}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: wait for event?!
|
// TODO: wait for event?!
|
||||||
return false, nil, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// May block on send if queue is full.
|
// May block on send if queue is full.
|
||||||
@ -472,11 +472,13 @@ func (cs *ConsensusState) reconstructLastCommit(state *sm.State) {
|
|||||||
}
|
}
|
||||||
seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight)
|
seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight)
|
||||||
lastPrecommits := types.NewVoteSet(cs.config.GetString("chain_id"), state.LastBlockHeight, seenCommit.Round(), types.VoteTypePrecommit, state.LastValidators)
|
lastPrecommits := types.NewVoteSet(cs.config.GetString("chain_id"), state.LastBlockHeight, seenCommit.Round(), types.VoteTypePrecommit, state.LastValidators)
|
||||||
for idx, precommit := range seenCommit.Precommits {
|
for _, precommit := range seenCommit.Precommits {
|
||||||
if precommit == nil {
|
if precommit == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
added, _, err := lastPrecommits.AddByIndex(idx, precommit)
|
// XXXX reconstruct Vote from precommit after changing precommit to simpler
|
||||||
|
// structure.
|
||||||
|
added, err := lastPrecommits.AddVote(precommit)
|
||||||
if !added || err != nil {
|
if !added || err != nil {
|
||||||
PanicCrisis(Fmt("Failed to reconstruct LastCommit: %v", err))
|
PanicCrisis(Fmt("Failed to reconstruct LastCommit: %v", err))
|
||||||
}
|
}
|
||||||
@ -694,7 +696,7 @@ func (cs *ConsensusState) handleMsg(mi msgInfo, rs RoundState) {
|
|||||||
case *VoteMessage:
|
case *VoteMessage:
|
||||||
// attempt to add the vote and dupeout the validator if its a duplicate signature
|
// attempt to add the vote and dupeout the validator if its a duplicate signature
|
||||||
// if the vote gives us a 2/3-any or 2/3-one, we transition
|
// if the vote gives us a 2/3-any or 2/3-one, we transition
|
||||||
err := cs.tryAddVote(msg.ValidatorIndex, msg.Vote, peerKey)
|
err := cs.tryAddVote(msg.Vote, peerKey)
|
||||||
if err == ErrAddingVote {
|
if err == ErrAddingVote {
|
||||||
// TODO: punish peer
|
// TODO: punish peer
|
||||||
}
|
}
|
||||||
@ -1390,8 +1392,8 @@ func (cs *ConsensusState) addProposalBlockPart(height int, part *types.Part, ver
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to add the vote. if its a duplicate signature, dupeout the validator
|
// Attempt to add the vote. if its a duplicate signature, dupeout the validator
|
||||||
func (cs *ConsensusState) tryAddVote(valIndex int, vote *types.Vote, peerKey string) error {
|
func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerKey string) error {
|
||||||
_, _, err := cs.addVote(valIndex, vote, peerKey)
|
_, err := cs.addVote(vote, peerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the vote height is off, we'll just ignore it,
|
// If the vote height is off, we'll just ignore it,
|
||||||
// But if it's a conflicting sig, broadcast evidence tx for slashing.
|
// But if it's a conflicting sig, broadcast evidence tx for slashing.
|
||||||
@ -1424,7 +1426,7 @@ func (cs *ConsensusState) tryAddVote(valIndex int, vote *types.Vote, peerKey str
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
func (cs *ConsensusState) addVote(valIndex int, vote *types.Vote, peerKey string) (added bool, address []byte, err error) {
|
func (cs *ConsensusState) addVote(vote *types.Vote, peerKey string) (added bool, err error) {
|
||||||
log.Debug("addVote", "voteHeight", vote.Height, "voteType", vote.Type, "csHeight", cs.Height)
|
log.Debug("addVote", "voteHeight", vote.Height, "voteType", vote.Type, "csHeight", cs.Height)
|
||||||
|
|
||||||
// A precommit for the previous height?
|
// A precommit for the previous height?
|
||||||
@ -1432,13 +1434,12 @@ func (cs *ConsensusState) addVote(valIndex int, vote *types.Vote, peerKey string
|
|||||||
if !(cs.Step == RoundStepNewHeight && vote.Type == types.VoteTypePrecommit) {
|
if !(cs.Step == RoundStepNewHeight && vote.Type == types.VoteTypePrecommit) {
|
||||||
// TODO: give the reason ..
|
// TODO: give the reason ..
|
||||||
// fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.")
|
// fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.")
|
||||||
return added, nil, ErrVoteHeightMismatch
|
return added, ErrVoteHeightMismatch
|
||||||
}
|
}
|
||||||
added, address, err = cs.LastCommit.AddByIndex(valIndex, vote)
|
added, err = cs.LastCommit.AddVote(vote)
|
||||||
if added {
|
if added {
|
||||||
log.Info(Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
|
log.Info(Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
|
||||||
types.FireEventVote(cs.evsw, types.EventDataVote{valIndex, address, vote})
|
types.FireEventVote(cs.evsw, types.EventDataVote{vote})
|
||||||
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1446,9 +1447,9 @@ func (cs *ConsensusState) addVote(valIndex int, vote *types.Vote, peerKey string
|
|||||||
// A prevote/precommit for this height?
|
// A prevote/precommit for this height?
|
||||||
if vote.Height == cs.Height {
|
if vote.Height == cs.Height {
|
||||||
height := cs.Height
|
height := cs.Height
|
||||||
added, address, err = cs.Votes.AddByIndex(valIndex, vote, peerKey)
|
added, err = cs.Votes.AddVote(vote, peerKey)
|
||||||
if added {
|
if added {
|
||||||
types.FireEventVote(cs.evsw, types.EventDataVote{valIndex, address, vote})
|
types.FireEventVote(cs.evsw, types.EventDataVote{vote})
|
||||||
|
|
||||||
switch vote.Type {
|
switch vote.Type {
|
||||||
case types.VoteTypePrevote:
|
case types.VoteTypePrevote:
|
||||||
@ -1518,7 +1519,11 @@ func (cs *ConsensusState) addVote(valIndex int, vote *types.Vote, peerKey string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||||
|
// TODO: store our index in the cs so we don't have to do this every time
|
||||||
|
valIndex, _ := cs.Validators.GetByAddress(cs.privValidator.Address)
|
||||||
vote := &types.Vote{
|
vote := &types.Vote{
|
||||||
|
ValidatorAddress: cs.privValidator.Address,
|
||||||
|
ValidatorIndex: valIndex,
|
||||||
Height: cs.Height,
|
Height: cs.Height,
|
||||||
Round: cs.Round,
|
Round: cs.Round,
|
||||||
Type: type_,
|
Type: type_,
|
||||||
@ -1537,9 +1542,7 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.Part
|
|||||||
}
|
}
|
||||||
vote, err := cs.signVote(type_, hash, header)
|
vote, err := cs.signVote(type_, hash, header)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO: store our index in the cs so we don't have to do this every time
|
cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""})
|
||||||
valIndex, _ := cs.Validators.GetByAddress(cs.privValidator.Address)
|
|
||||||
cs.sendInternalMessage(msgInfo{&VoteMessage{valIndex, vote}, ""})
|
|
||||||
log.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "error", err)
|
log.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "error", err)
|
||||||
return vote
|
return vote
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,7 +74,7 @@ func TestProposerSelection0(t *testing.T) {
|
|||||||
<-proposalCh
|
<-proposalCh
|
||||||
|
|
||||||
rs := cs1.GetRoundState()
|
rs := cs1.GetRoundState()
|
||||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil, vss[1:]...)
|
signAddVotes(cs1, types.VoteTypePrecommit, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:]...)
|
||||||
|
|
||||||
// wait for new round so next validator is set
|
// wait for new round so next validator is set
|
||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
@ -106,7 +106,7 @@ func TestProposerSelection2(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs := cs1.GetRoundState()
|
rs := cs1.GetRoundState()
|
||||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, rs.ProposalBlockParts.Header(), nil, vss[1:]...)
|
signAddVotes(cs1, types.VoteTypePrecommit, nil, rs.ProposalBlockParts.Header(), vss[1:]...)
|
||||||
<-newRoundCh // wait for the new round event each round
|
<-newRoundCh // wait for the new round event each round
|
||||||
|
|
||||||
incrementRound(vss[1:]...)
|
incrementRound(vss[1:]...)
|
||||||
@ -179,12 +179,12 @@ func TestEnterProposeYesPrivValidator(t *testing.T) {
|
|||||||
func TestBadProposal(t *testing.T) {
|
func TestBadProposal(t *testing.T) {
|
||||||
cs1, vss := randConsensusState(2)
|
cs1, vss := randConsensusState(2)
|
||||||
height, round := cs1.Height, cs1.Round
|
height, round := cs1.Height, cs1.Round
|
||||||
cs2 := vss[1]
|
vs2 := vss[1]
|
||||||
|
|
||||||
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
|
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
|
||||||
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
|
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
|
||||||
|
|
||||||
propBlock, _ := cs1.createProposalBlock() //changeProposer(t, cs1, cs2)
|
propBlock, _ := cs1.createProposalBlock() //changeProposer(t, cs1, vs2)
|
||||||
|
|
||||||
// make the second validator the proposer by incrementing round
|
// make the second validator the proposer by incrementing round
|
||||||
round = round + 1
|
round = round + 1
|
||||||
@ -198,9 +198,9 @@ func TestBadProposal(t *testing.T) {
|
|||||||
stateHash[0] = byte((stateHash[0] + 1) % 255)
|
stateHash[0] = byte((stateHash[0] + 1) % 255)
|
||||||
propBlock.AppHash = stateHash
|
propBlock.AppHash = stateHash
|
||||||
propBlockParts := propBlock.MakePartSet()
|
propBlockParts := propBlock.MakePartSet()
|
||||||
proposal := types.NewProposal(cs2.Height, round, propBlockParts.Header(), -1)
|
proposal := types.NewProposal(vs2.Height, round, propBlockParts.Header(), -1)
|
||||||
if err := cs2.SignProposal(config.GetString("chain_id"), proposal); err != nil {
|
if err := vs2.SignProposal(config.GetString("chain_id"), proposal); err != nil {
|
||||||
panic("failed to sign bad proposal: " + err.Error())
|
t.Fatal("failed to sign bad proposal", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the proposal block
|
// set the proposal block
|
||||||
@ -217,14 +217,15 @@ func TestBadProposal(t *testing.T) {
|
|||||||
|
|
||||||
validatePrevote(t, cs1, round, vss[0], nil)
|
validatePrevote(t, cs1, round, vss[0], nil)
|
||||||
|
|
||||||
// add bad prevote from cs2 and wait for it
|
// add bad prevote from vs2 and wait for it
|
||||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh)
|
signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet().Header(), vs2)
|
||||||
|
<-voteCh
|
||||||
|
|
||||||
// wait for precommit
|
// wait for precommit
|
||||||
<-voteCh
|
<-voteCh
|
||||||
|
|
||||||
validatePrecommit(t, cs1, round, 0, vss[0], nil, nil)
|
validatePrecommit(t, cs1, round, 0, vss[0], nil, nil)
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh)
|
signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlock.MakePartSet().Header(), vs2)
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
@ -281,7 +282,7 @@ func TestFullRoundNil(t *testing.T) {
|
|||||||
// where the first validator has to wait for votes from the second
|
// where the first validator has to wait for votes from the second
|
||||||
func TestFullRound2(t *testing.T) {
|
func TestFullRound2(t *testing.T) {
|
||||||
cs1, vss := randConsensusState(2)
|
cs1, vss := randConsensusState(2)
|
||||||
cs2 := vss[1]
|
vs2 := vss[1]
|
||||||
height, round := cs1.Height, cs1.Round
|
height, round := cs1.Height, cs1.Round
|
||||||
|
|
||||||
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
|
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
|
||||||
@ -296,8 +297,9 @@ func TestFullRound2(t *testing.T) {
|
|||||||
rs := cs1.GetRoundState()
|
rs := cs1.GetRoundState()
|
||||||
propBlockHash, propPartsHeader := rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header()
|
propBlockHash, propPartsHeader := rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header()
|
||||||
|
|
||||||
// prevote arrives from cs2:
|
// prevote arrives from vs2:
|
||||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlockHash, propPartsHeader, voteCh)
|
signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propPartsHeader, vs2)
|
||||||
|
<-voteCh
|
||||||
|
|
||||||
<-voteCh //precommit
|
<-voteCh //precommit
|
||||||
|
|
||||||
@ -306,8 +308,9 @@ func TestFullRound2(t *testing.T) {
|
|||||||
|
|
||||||
// we should be stuck in limbo waiting for more precommits
|
// we should be stuck in limbo waiting for more precommits
|
||||||
|
|
||||||
// precommit arrives from cs2:
|
// precommit arrives from vs2:
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlockHash, propPartsHeader, voteCh)
|
signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash, propPartsHeader, vs2)
|
||||||
|
<-voteCh
|
||||||
|
|
||||||
// wait to finish commit, propose in next height
|
// wait to finish commit, propose in next height
|
||||||
<-newBlockCh
|
<-newBlockCh
|
||||||
@ -320,7 +323,7 @@ func TestFullRound2(t *testing.T) {
|
|||||||
// two vals take turns proposing. val1 locks on first one, precommits nil on everything else
|
// two vals take turns proposing. val1 locks on first one, precommits nil on everything else
|
||||||
func TestLockNoPOL(t *testing.T) {
|
func TestLockNoPOL(t *testing.T) {
|
||||||
cs1, vss := randConsensusState(2)
|
cs1, vss := randConsensusState(2)
|
||||||
cs2 := vss[1]
|
vs2 := vss[1]
|
||||||
height := cs1.Height
|
height := cs1.Height
|
||||||
|
|
||||||
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
||||||
@ -344,8 +347,9 @@ func TestLockNoPOL(t *testing.T) {
|
|||||||
<-voteCh // prevote
|
<-voteCh // prevote
|
||||||
|
|
||||||
// we should now be stuck in limbo forever, waiting for more prevotes
|
// we should now be stuck in limbo forever, waiting for more prevotes
|
||||||
// prevote arrives from cs2:
|
// prevote arrives from vs2:
|
||||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), voteCh)
|
signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2)
|
||||||
|
<-voteCh // prevote
|
||||||
|
|
||||||
<-voteCh // precommit
|
<-voteCh // precommit
|
||||||
|
|
||||||
@ -358,7 +362,8 @@ func TestLockNoPOL(t *testing.T) {
|
|||||||
hash := make([]byte, len(theBlockHash))
|
hash := make([]byte, len(theBlockHash))
|
||||||
copy(hash, theBlockHash)
|
copy(hash, theBlockHash)
|
||||||
hash[0] = byte((hash[0] + 1) % 255)
|
hash[0] = byte((hash[0] + 1) % 255)
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
|
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet().Header(), vs2)
|
||||||
|
<-voteCh // precommit
|
||||||
|
|
||||||
// (note we're entering precommit for a second time this round)
|
// (note we're entering precommit for a second time this round)
|
||||||
// but with invalid args. then we enterPrecommitWait, and the timeout to new round
|
// but with invalid args. then we enterPrecommitWait, and the timeout to new round
|
||||||
@ -372,7 +377,7 @@ func TestLockNoPOL(t *testing.T) {
|
|||||||
Round2 (cs1, B) // B B2
|
Round2 (cs1, B) // B B2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
incrementRound(cs2)
|
incrementRound(vs2)
|
||||||
|
|
||||||
// now we're on a new round and not the proposer, so wait for timeout
|
// now we're on a new round and not the proposer, so wait for timeout
|
||||||
re = <-timeoutProposeCh
|
re = <-timeoutProposeCh
|
||||||
@ -389,7 +394,8 @@ func TestLockNoPOL(t *testing.T) {
|
|||||||
validatePrevote(t, cs1, 1, vss[0], rs.LockedBlock.Hash())
|
validatePrevote(t, cs1, 1, vss[0], rs.LockedBlock.Hash())
|
||||||
|
|
||||||
// add a conflicting prevote from the other validator
|
// add a conflicting prevote from the other validator
|
||||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
|
signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlock.MakePartSet().Header(), vs2)
|
||||||
|
<-voteCh
|
||||||
|
|
||||||
// now we're going to enter prevote again, but with invalid args
|
// now we're going to enter prevote again, but with invalid args
|
||||||
// and then prevote wait, which should timeout. then wait for precommit
|
// and then prevote wait, which should timeout. then wait for precommit
|
||||||
@ -401,9 +407,10 @@ func TestLockNoPOL(t *testing.T) {
|
|||||||
// we should precommit nil and be locked on the proposal
|
// we should precommit nil and be locked on the proposal
|
||||||
validatePrecommit(t, cs1, 1, 0, vss[0], nil, theBlockHash)
|
validatePrecommit(t, cs1, 1, 0, vss[0], nil, theBlockHash)
|
||||||
|
|
||||||
// add conflicting precommit from cs2
|
// add conflicting precommit from vs2
|
||||||
// NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
|
// NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
|
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet().Header(), vs2)
|
||||||
|
<-voteCh
|
||||||
|
|
||||||
// (note we're entering precommit for a second time this round, but with invalid args
|
// (note we're entering precommit for a second time this round, but with invalid args
|
||||||
// then we enterPrecommitWait and timeout into NewRound
|
// then we enterPrecommitWait and timeout into NewRound
|
||||||
@ -412,10 +419,10 @@ func TestLockNoPOL(t *testing.T) {
|
|||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
log.Notice("#### ONTO ROUND 2")
|
log.Notice("#### ONTO ROUND 2")
|
||||||
/*
|
/*
|
||||||
Round3 (cs2, _) // B, B2
|
Round3 (vs2, _) // B, B2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
incrementRound(cs2)
|
incrementRound(vs2)
|
||||||
|
|
||||||
re = <-proposalCh
|
re = <-proposalCh
|
||||||
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
|
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
|
||||||
@ -429,28 +436,31 @@ func TestLockNoPOL(t *testing.T) {
|
|||||||
|
|
||||||
validatePrevote(t, cs1, 2, vss[0], rs.LockedBlock.Hash())
|
validatePrevote(t, cs1, 2, vss[0], rs.LockedBlock.Hash())
|
||||||
|
|
||||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
|
signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlock.MakePartSet().Header(), vs2)
|
||||||
|
<-voteCh
|
||||||
|
|
||||||
<-timeoutWaitCh // prevote wait
|
<-timeoutWaitCh // prevote wait
|
||||||
<-voteCh // precommit
|
<-voteCh // precommit
|
||||||
|
|
||||||
validatePrecommit(t, cs1, 2, 0, vss[0], nil, theBlockHash) // precommit nil but be locked on proposal
|
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
|
|
||||||
|
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet().Header(), vs2) // NOTE: conflicting precommits at same height
|
||||||
|
<-voteCh
|
||||||
|
|
||||||
<-timeoutWaitCh
|
<-timeoutWaitCh
|
||||||
|
|
||||||
// before we time out into new round, set next proposal block
|
// before we time out into new round, set next proposal block
|
||||||
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
|
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||||
if prop == nil || propBlock == nil {
|
if prop == nil || propBlock == nil {
|
||||||
panic("Failed to create proposal block with cs2")
|
t.Fatal("Failed to create proposal block with vs2")
|
||||||
}
|
}
|
||||||
|
|
||||||
incrementRound(cs2)
|
incrementRound(vs2)
|
||||||
|
|
||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
log.Notice("#### ONTO ROUND 3")
|
log.Notice("#### ONTO ROUND 3")
|
||||||
/*
|
/*
|
||||||
Round4 (cs2, C) // B C // B C
|
Round4 (vs2, C) // B C // B C
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// now we're on a new round and not the proposer
|
// now we're on a new round and not the proposer
|
||||||
@ -463,19 +473,22 @@ func TestLockNoPOL(t *testing.T) {
|
|||||||
// prevote for locked block (not proposal)
|
// prevote for locked block (not proposal)
|
||||||
validatePrevote(t, cs1, 0, vss[0], cs1.LockedBlock.Hash())
|
validatePrevote(t, cs1, 0, vss[0], cs1.LockedBlock.Hash())
|
||||||
|
|
||||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh)
|
signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet().Header(), vs2)
|
||||||
|
<-voteCh
|
||||||
|
|
||||||
<-timeoutWaitCh
|
<-timeoutWaitCh
|
||||||
<-voteCh
|
<-voteCh
|
||||||
|
|
||||||
validatePrecommit(t, cs1, 2, 0, vss[0], nil, theBlockHash) // precommit nil but locked on proposal
|
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
|
|
||||||
|
signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlock.MakePartSet().Header(), vs2) // NOTE: conflicting precommits at same height
|
||||||
|
<-voteCh
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
|
// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
|
||||||
func TestLockPOLRelock(t *testing.T) {
|
func TestLockPOLRelock(t *testing.T) {
|
||||||
cs1, vss := randConsensusState(4)
|
cs1, vss := randConsensusState(4)
|
||||||
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
|
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
|
||||||
|
|
||||||
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
||||||
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
|
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
|
||||||
@ -484,14 +497,14 @@ func TestLockPOLRelock(t *testing.T) {
|
|||||||
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
|
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
|
||||||
newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlockHeader(), 1)
|
newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlockHeader(), 1)
|
||||||
|
|
||||||
log.Debug("cs2 last round", "lr", cs2.PrivValidator.LastRound)
|
log.Debug("vs2 last round", "lr", vs2.PrivValidator.LastRound)
|
||||||
|
|
||||||
// everything done from perspective of cs1
|
// everything done from perspective of cs1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Round1 (cs1, B) // B B B B// B nil B nil
|
Round1 (cs1, B) // B B B B// B nil B nil
|
||||||
|
|
||||||
eg. cs2 and cs4 didn't see the 2/3 prevotes
|
eg. vs2 and vs4 didn't see the 2/3 prevotes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// start round and wait for propose and prevote
|
// start round and wait for propose and prevote
|
||||||
@ -501,26 +514,27 @@ func TestLockPOLRelock(t *testing.T) {
|
|||||||
re := <-proposalCh
|
re := <-proposalCh
|
||||||
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
|
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
|
||||||
theBlockHash := rs.ProposalBlock.Hash()
|
theBlockHash := rs.ProposalBlock.Hash()
|
||||||
theBlockPartsHeader := rs.ProposalBlockParts.Header()
|
|
||||||
|
|
||||||
<-voteCh // prevote
|
<-voteCh // prevote
|
||||||
|
|
||||||
signAddVoteToFromMany(types.VoteTypePrevote, cs1, theBlockHash, theBlockPartsHeader, voteCh, cs2, cs3, cs4)
|
signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2, vs3, vs4)
|
||||||
|
_, _, _ = <-voteCh, <-voteCh, <-voteCh // prevotes
|
||||||
|
|
||||||
<-voteCh // our precommit
|
<-voteCh // our precommit
|
||||||
// the proposed block should now be locked and our precommit added
|
// the proposed block should now be locked and our precommit added
|
||||||
validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
|
validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
|
||||||
|
|
||||||
// add precommits from the rest
|
// add precommits from the rest
|
||||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, voteCh, cs2, cs4)
|
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, theBlockHash, theBlockPartsHeader, voteCh)
|
signAddVotes(cs1, types.VoteTypePrecommit, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs3)
|
||||||
|
_, _, _ = <-voteCh, <-voteCh, <-voteCh // precommits
|
||||||
|
|
||||||
// before we timeout to the new round set the new proposal
|
// before we timeout to the new round set the new proposal
|
||||||
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
|
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||||
propBlockParts := propBlock.MakePartSet()
|
propBlockParts := propBlock.MakePartSet()
|
||||||
propBlockHash := propBlock.Hash()
|
propBlockHash := propBlock.Hash()
|
||||||
|
|
||||||
incrementRound(cs2, cs3, cs4)
|
incrementRound(vs2, vs3, vs4)
|
||||||
|
|
||||||
// timeout to new round
|
// timeout to new round
|
||||||
<-timeoutWaitCh
|
<-timeoutWaitCh
|
||||||
@ -532,7 +546,7 @@ func TestLockPOLRelock(t *testing.T) {
|
|||||||
log.Notice("### ONTO ROUND 1")
|
log.Notice("### ONTO ROUND 1")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Round2 (cs2, C) // B C C C // C C C _)
|
Round2 (vs2, C) // B C C C // C C C _)
|
||||||
|
|
||||||
cs1 changes lock!
|
cs1 changes lock!
|
||||||
*/
|
*/
|
||||||
@ -550,7 +564,8 @@ func TestLockPOLRelock(t *testing.T) {
|
|||||||
validatePrevote(t, cs1, 0, vss[0], theBlockHash)
|
validatePrevote(t, cs1, 0, vss[0], theBlockHash)
|
||||||
|
|
||||||
// now lets add prevotes from everyone else for the new block
|
// now lets add prevotes from everyone else for the new block
|
||||||
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash, propBlockParts.Header(), voteCh, cs2, cs3, cs4)
|
signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
|
||||||
|
_, _, _ = <-voteCh, <-voteCh, <-voteCh // prevotes
|
||||||
|
|
||||||
// now either we go to PrevoteWait or Precommit
|
// now either we go to PrevoteWait or Precommit
|
||||||
select {
|
select {
|
||||||
@ -564,7 +579,8 @@ func TestLockPOLRelock(t *testing.T) {
|
|||||||
// we should have unlocked and locked on the new block
|
// we should have unlocked and locked on the new block
|
||||||
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
|
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
|
||||||
|
|
||||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, propBlockHash, propBlockParts.Header(), voteCh, cs2, cs3)
|
signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash, propBlockParts.Header(), vs2, vs3)
|
||||||
|
_, _ = <-voteCh, <-voteCh
|
||||||
|
|
||||||
be := <-newBlockCh
|
be := <-newBlockCh
|
||||||
b := be.(types.EventDataNewBlockHeader)
|
b := be.(types.EventDataNewBlockHeader)
|
||||||
@ -582,7 +598,7 @@ func TestLockPOLRelock(t *testing.T) {
|
|||||||
// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
|
// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
|
||||||
func TestLockPOLUnlock(t *testing.T) {
|
func TestLockPOLUnlock(t *testing.T) {
|
||||||
cs1, vss := randConsensusState(4)
|
cs1, vss := randConsensusState(4)
|
||||||
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
|
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
|
||||||
|
|
||||||
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
|
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
|
||||||
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
||||||
@ -608,7 +624,7 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||||||
|
|
||||||
<-voteCh // prevote
|
<-voteCh // prevote
|
||||||
|
|
||||||
signAddVoteToFromMany(types.VoteTypePrevote, cs1, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil, cs2, cs3, cs4)
|
signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2, vs3, vs4)
|
||||||
|
|
||||||
<-voteCh //precommit
|
<-voteCh //precommit
|
||||||
|
|
||||||
@ -618,14 +634,14 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||||||
rs = cs1.GetRoundState()
|
rs = cs1.GetRoundState()
|
||||||
|
|
||||||
// add precommits from the rest
|
// add precommits from the rest
|
||||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs4)
|
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil)
|
signAddVotes(cs1, types.VoteTypePrecommit, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs3)
|
||||||
|
|
||||||
// before we time out into new round, set next proposal block
|
// before we time out into new round, set next proposal block
|
||||||
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
|
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||||
propBlockParts := propBlock.MakePartSet()
|
propBlockParts := propBlock.MakePartSet()
|
||||||
|
|
||||||
incrementRound(cs2, cs3, cs4)
|
incrementRound(vs2, vs3, vs4)
|
||||||
|
|
||||||
// timeout to new round
|
// timeout to new round
|
||||||
re = <-timeoutWaitCh
|
re = <-timeoutWaitCh
|
||||||
@ -638,7 +654,7 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
log.Notice("#### ONTO ROUND 1")
|
log.Notice("#### ONTO ROUND 1")
|
||||||
/*
|
/*
|
||||||
Round2 (cs2, C) // B nil nil nil // nil nil nil _
|
Round2 (vs2, C) // B nil nil nil // nil nil nil _
|
||||||
|
|
||||||
cs1 unlocks!
|
cs1 unlocks!
|
||||||
*/
|
*/
|
||||||
@ -655,7 +671,7 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||||||
<-voteCh
|
<-voteCh
|
||||||
validatePrevote(t, cs1, 0, vss[0], lockedBlockHash)
|
validatePrevote(t, cs1, 0, vss[0], lockedBlockHash)
|
||||||
// now lets add prevotes from everyone else for nil (a polka!)
|
// now lets add prevotes from everyone else for nil (a polka!)
|
||||||
signAddVoteToFromMany(types.VoteTypePrevote, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3, cs4)
|
signAddVotes(cs1, types.VoteTypePrevote, nil, types.PartSetHeader{}, vs2, vs3, vs4)
|
||||||
|
|
||||||
// the polka makes us unlock and precommit nil
|
// the polka makes us unlock and precommit nil
|
||||||
<-unlockCh
|
<-unlockCh
|
||||||
@ -665,7 +681,7 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||||||
// NOTE: since we don't relock on nil, the lock round is 0
|
// NOTE: since we don't relock on nil, the lock round is 0
|
||||||
validatePrecommit(t, cs1, 1, 0, vss[0], nil, nil)
|
validatePrecommit(t, cs1, 1, 0, vss[0], nil, nil)
|
||||||
|
|
||||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3)
|
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3)
|
||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,7 +691,7 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||||||
// then we see the polka from round 1 but shouldn't unlock
|
// then we see the polka from round 1 but shouldn't unlock
|
||||||
func TestLockPOLSafety1(t *testing.T) {
|
func TestLockPOLSafety1(t *testing.T) {
|
||||||
cs1, vss := randConsensusState(4)
|
cs1, vss := randConsensusState(4)
|
||||||
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
|
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
|
||||||
|
|
||||||
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
|
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
|
||||||
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
||||||
@ -695,7 +711,7 @@ func TestLockPOLSafety1(t *testing.T) {
|
|||||||
validatePrevote(t, cs1, 0, vss[0], propBlock.Hash())
|
validatePrevote(t, cs1, 0, vss[0], propBlock.Hash())
|
||||||
|
|
||||||
// the others sign a polka but we don't see it
|
// the others sign a polka but we don't see it
|
||||||
prevotes := signVoteMany(types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet().Header(), cs2, cs3, cs4)
|
prevotes := signVotes(types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet().Header(), vs2, vs3, vs4)
|
||||||
|
|
||||||
// before we time out into new round, set next proposer
|
// before we time out into new round, set next proposer
|
||||||
// and next proposal block
|
// and next proposal block
|
||||||
@ -709,13 +725,13 @@ func TestLockPOLSafety1(t *testing.T) {
|
|||||||
log.Warn("old prop", "hash", fmt.Sprintf("%X", propBlock.Hash()))
|
log.Warn("old prop", "hash", fmt.Sprintf("%X", propBlock.Hash()))
|
||||||
|
|
||||||
// we do see them precommit nil
|
// we do see them precommit nil
|
||||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3, cs4)
|
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3, vs4)
|
||||||
|
|
||||||
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
|
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||||
propBlockHash := propBlock.Hash()
|
propBlockHash := propBlock.Hash()
|
||||||
propBlockParts := propBlock.MakePartSet()
|
propBlockParts := propBlock.MakePartSet()
|
||||||
|
|
||||||
incrementRound(cs2, cs3, cs4)
|
incrementRound(vs2, vs3, vs4)
|
||||||
|
|
||||||
//XXX: this isnt gauranteed to get there before the timeoutPropose ...
|
//XXX: this isnt gauranteed to get there before the timeoutPropose ...
|
||||||
cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
|
cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
|
||||||
@ -746,18 +762,18 @@ func TestLockPOLSafety1(t *testing.T) {
|
|||||||
validatePrevote(t, cs1, 1, vss[0], propBlockHash)
|
validatePrevote(t, cs1, 1, vss[0], propBlockHash)
|
||||||
|
|
||||||
// now we see the others prevote for it, so we should lock on it
|
// now we see the others prevote for it, so we should lock on it
|
||||||
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash, propBlockParts.Header(), nil, cs2, cs3, cs4)
|
signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
|
||||||
|
|
||||||
<-voteCh // precommit
|
<-voteCh // precommit
|
||||||
|
|
||||||
// we should have precommitted
|
// we should have precommitted
|
||||||
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
|
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
|
||||||
|
|
||||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3)
|
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3)
|
||||||
|
|
||||||
<-timeoutWaitCh
|
<-timeoutWaitCh
|
||||||
|
|
||||||
incrementRound(cs2, cs3, cs4)
|
incrementRound(vs2, vs3, vs4)
|
||||||
|
|
||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
|
|
||||||
@ -778,7 +794,7 @@ func TestLockPOLSafety1(t *testing.T) {
|
|||||||
newStepCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRoundStep(), 1)
|
newStepCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRoundStep(), 1)
|
||||||
|
|
||||||
// add prevotes from the earlier round
|
// add prevotes from the earlier round
|
||||||
addVoteToFromMany(cs1, prevotes, cs2, cs3, cs4)
|
addVotes(cs1, prevotes...)
|
||||||
|
|
||||||
log.Warn("Done adding prevotes!")
|
log.Warn("Done adding prevotes!")
|
||||||
|
|
||||||
@ -794,7 +810,7 @@ func TestLockPOLSafety1(t *testing.T) {
|
|||||||
// dont see P0, lock on P1 at R1, dont unlock using P0 at R2
|
// dont see P0, lock on P1 at R1, dont unlock using P0 at R2
|
||||||
func TestLockPOLSafety2(t *testing.T) {
|
func TestLockPOLSafety2(t *testing.T) {
|
||||||
cs1, vss := randConsensusState(4)
|
cs1, vss := randConsensusState(4)
|
||||||
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
|
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
|
||||||
|
|
||||||
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
|
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
|
||||||
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
||||||
@ -810,14 +826,14 @@ func TestLockPOLSafety2(t *testing.T) {
|
|||||||
propBlockParts0 := propBlock0.MakePartSet()
|
propBlockParts0 := propBlock0.MakePartSet()
|
||||||
|
|
||||||
// the others sign a polka but we don't see it
|
// the others sign a polka but we don't see it
|
||||||
prevotes := signVoteMany(types.VoteTypePrevote, propBlockHash0, propBlockParts0.Header(), cs2, cs3, cs4)
|
prevotes := signVotes(types.VoteTypePrevote, propBlockHash0, propBlockParts0.Header(), vs2, vs3, vs4)
|
||||||
|
|
||||||
// the block for round 1
|
// the block for round 1
|
||||||
prop1, propBlock1 := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
|
prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||||
propBlockHash1 := propBlock1.Hash()
|
propBlockHash1 := propBlock1.Hash()
|
||||||
propBlockParts1 := propBlock1.MakePartSet()
|
propBlockParts1 := propBlock1.MakePartSet()
|
||||||
|
|
||||||
incrementRound(cs2, cs3, cs4)
|
incrementRound(vs2, vs3, vs4)
|
||||||
|
|
||||||
cs1.updateRoundStep(0, RoundStepPrecommitWait)
|
cs1.updateRoundStep(0, RoundStepPrecommitWait)
|
||||||
|
|
||||||
@ -832,28 +848,30 @@ func TestLockPOLSafety2(t *testing.T) {
|
|||||||
|
|
||||||
<-voteCh // prevote
|
<-voteCh // prevote
|
||||||
|
|
||||||
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash1, propBlockParts1.Header(), nil, cs2, cs3, cs4)
|
signAddVotes(cs1, types.VoteTypePrevote, propBlockHash1, propBlockParts1.Header(), vs2, vs3, vs4)
|
||||||
|
|
||||||
<-voteCh // precommit
|
<-voteCh // precommit
|
||||||
// the proposed block should now be locked and our precommit added
|
// the proposed block should now be locked and our precommit added
|
||||||
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash1, propBlockHash1)
|
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash1, propBlockHash1)
|
||||||
|
|
||||||
// add precommits from the rest
|
// add precommits from the rest
|
||||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs4)
|
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, propBlockHash1, propBlockParts1.Header(), nil)
|
signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash1, propBlockParts1.Header(), vs3)
|
||||||
|
|
||||||
incrementRound(cs2, cs3, cs4)
|
incrementRound(vs2, vs3, vs4)
|
||||||
|
|
||||||
// timeout of precommit wait to new round
|
// timeout of precommit wait to new round
|
||||||
<-timeoutWaitCh
|
<-timeoutWaitCh
|
||||||
|
|
||||||
// in round 2 we see the polkad block from round 0
|
// in round 2 we see the polkad block from round 0
|
||||||
newProp := types.NewProposal(height, 2, propBlockParts0.Header(), 0)
|
newProp := types.NewProposal(height, 2, propBlockParts0.Header(), 0)
|
||||||
if err := cs3.SignProposal(config.GetString("chain_id"), newProp); err != nil {
|
if err := vs3.SignProposal(config.GetString("chain_id"), newProp); err != nil {
|
||||||
panic(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
cs1.SetProposalAndBlock(newProp, propBlock0, propBlockParts0, "some peer")
|
cs1.SetProposalAndBlock(newProp, propBlock0, propBlockParts0, "some peer")
|
||||||
addVoteToFromMany(cs1, prevotes, cs2, cs3, cs4) // add the pol votes
|
|
||||||
|
// Add the pol votes
|
||||||
|
addVotes(cs1, prevotes...)
|
||||||
|
|
||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
log.Notice("### ONTO Round 2")
|
log.Notice("### ONTO Round 2")
|
||||||
@ -884,7 +902,7 @@ func TestLockPOLSafety2(t *testing.T) {
|
|||||||
/*
|
/*
|
||||||
func TestSlashingPrevotes(t *testing.T) {
|
func TestSlashingPrevotes(t *testing.T) {
|
||||||
cs1, vss := randConsensusState(2)
|
cs1, vss := randConsensusState(2)
|
||||||
cs2 := vss[1]
|
vs2 := vss[1]
|
||||||
|
|
||||||
|
|
||||||
proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
|
proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
|
||||||
@ -904,7 +922,7 @@ func TestSlashingPrevotes(t *testing.T) {
|
|||||||
// add one for a different block should cause us to go into prevote wait
|
// add one for a different block should cause us to go into prevote wait
|
||||||
hash := rs.ProposalBlock.Hash()
|
hash := rs.ProposalBlock.Hash()
|
||||||
hash[0] = byte(hash[0]+1) % 255
|
hash[0] = byte(hash[0]+1) % 255
|
||||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, rs.ProposalBlockParts.Header(), nil)
|
signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlockParts.Header(), vs2)
|
||||||
|
|
||||||
<-timeoutWaitCh
|
<-timeoutWaitCh
|
||||||
|
|
||||||
@ -912,14 +930,14 @@ func TestSlashingPrevotes(t *testing.T) {
|
|||||||
// away and ignore more prevotes (and thus fail to slash!)
|
// away and ignore more prevotes (and thus fail to slash!)
|
||||||
|
|
||||||
// add the conflicting vote
|
// add the conflicting vote
|
||||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(),nil)
|
signAddVotes(cs1, types.VoteTypePrevote, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
|
||||||
|
|
||||||
// XXX: Check for existence of Dupeout info
|
// XXX: Check for existence of Dupeout info
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSlashingPrecommits(t *testing.T) {
|
func TestSlashingPrecommits(t *testing.T) {
|
||||||
cs1, vss := randConsensusState(2)
|
cs1, vss := randConsensusState(2)
|
||||||
cs2 := vss[1]
|
vs2 := vss[1]
|
||||||
|
|
||||||
|
|
||||||
proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
|
proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
|
||||||
@ -933,8 +951,8 @@ func TestSlashingPrecommits(t *testing.T) {
|
|||||||
re := <-proposalCh
|
re := <-proposalCh
|
||||||
<-voteCh // prevote
|
<-voteCh // prevote
|
||||||
|
|
||||||
// add prevote from cs2
|
// add prevote from vs2
|
||||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil)
|
signAddVotes(cs1, types.VoteTypePrevote, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
|
||||||
|
|
||||||
<-voteCh // precommit
|
<-voteCh // precommit
|
||||||
|
|
||||||
@ -942,13 +960,13 @@ func TestSlashingPrecommits(t *testing.T) {
|
|||||||
// add one for a different block should cause us to go into prevote wait
|
// add one for a different block should cause us to go into prevote wait
|
||||||
hash := rs.ProposalBlock.Hash()
|
hash := rs.ProposalBlock.Hash()
|
||||||
hash[0] = byte(hash[0]+1) % 255
|
hash[0] = byte(hash[0]+1) % 255
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlockParts.Header(),nil)
|
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlockParts.Header(), vs2)
|
||||||
|
|
||||||
// NOTE: we have to send the vote for different block first so we don't just go into precommit round right
|
// 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!)
|
// away and ignore more prevotes (and thus fail to slash!)
|
||||||
|
|
||||||
// add precommit from cs2
|
// add precommit from vs2
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(),nil)
|
signAddVotes(cs1, types.VoteTypePrecommit, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
|
||||||
|
|
||||||
// XXX: Check for existence of Dupeout info
|
// XXX: Check for existence of Dupeout info
|
||||||
}
|
}
|
||||||
@ -964,7 +982,7 @@ func TestSlashingPrecommits(t *testing.T) {
|
|||||||
// we receive a final precommit after going into next round, but others might have gone to commit already!
|
// we receive a final precommit after going into next round, but others might have gone to commit already!
|
||||||
func TestHalt1(t *testing.T) {
|
func TestHalt1(t *testing.T) {
|
||||||
cs1, vss := randConsensusState(4)
|
cs1, vss := randConsensusState(4)
|
||||||
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
|
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
|
||||||
|
|
||||||
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
|
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
|
||||||
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
|
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
|
||||||
@ -982,19 +1000,19 @@ func TestHalt1(t *testing.T) {
|
|||||||
|
|
||||||
<-voteCh // prevote
|
<-voteCh // prevote
|
||||||
|
|
||||||
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlock.Hash(), propBlockParts.Header(), nil, cs3, cs4)
|
signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlockParts.Header(), vs3, vs4)
|
||||||
<-voteCh // precommit
|
<-voteCh // precommit
|
||||||
|
|
||||||
// the proposed block should now be locked and our precommit added
|
// the proposed block should now be locked and our precommit added
|
||||||
validatePrecommit(t, cs1, 0, 0, vss[0], propBlock.Hash(), propBlock.Hash())
|
validatePrecommit(t, cs1, 0, 0, vss[0], propBlock.Hash(), propBlock.Hash())
|
||||||
|
|
||||||
// add precommits from the rest
|
// add precommits from the rest
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, nil, types.PartSetHeader{}, nil) // didnt receive proposal
|
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2) // didnt receive proposal
|
||||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, propBlock.Hash(), propBlockParts.Header(), nil)
|
signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header(), vs3)
|
||||||
// we receive this later, but cs3 might receive it earlier and with ours will go to commit!
|
// we receive this later, but vs3 might receive it earlier and with ours will go to commit!
|
||||||
precommit4 := signVote(cs4, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header())
|
precommit4 := signVote(vs4, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header())
|
||||||
|
|
||||||
incrementRound(cs2, cs3, cs4)
|
incrementRound(vs2, vs3, vs4)
|
||||||
|
|
||||||
// timeout to new round
|
// timeout to new round
|
||||||
<-timeoutWaitCh
|
<-timeoutWaitCh
|
||||||
@ -1012,7 +1030,7 @@ func TestHalt1(t *testing.T) {
|
|||||||
validatePrevote(t, cs1, 0, vss[0], rs.LockedBlock.Hash())
|
validatePrevote(t, cs1, 0, vss[0], rs.LockedBlock.Hash())
|
||||||
|
|
||||||
// now we receive the precommit from the previous round
|
// now we receive the precommit from the previous round
|
||||||
addVoteToFrom(cs1, cs4, precommit4)
|
addVotes(cs1, precommit4)
|
||||||
|
|
||||||
// receiving that precommit should take us straight to commit
|
// receiving that precommit should take us straight to commit
|
||||||
<-newBlockCh
|
<-newBlockCh
|
||||||
|
@ -91,9 +91,7 @@ type EventDataRoundState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type EventDataVote struct {
|
type EventDataVote struct {
|
||||||
Index int
|
Vote *Vote
|
||||||
Address []byte
|
|
||||||
Vote *Vote
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ EventDataNewBlock) AssertIsTMEventData() {}
|
func (_ EventDataNewBlock) AssertIsTMEventData() {}
|
||||||
|
@ -313,6 +313,7 @@ func (ac accumComparable) Less(o interface{}) bool {
|
|||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
// For testing
|
// For testing
|
||||||
|
|
||||||
|
// NOTE: PrivValidator are in order.
|
||||||
func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []*PrivValidator) {
|
func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []*PrivValidator) {
|
||||||
vals := make([]*Validator, numValidators)
|
vals := make([]*Validator, numValidators)
|
||||||
privValidators := make([]*PrivValidator, numValidators)
|
privValidators := make([]*PrivValidator, numValidators)
|
||||||
|
@ -11,10 +11,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrVoteUnexpectedStep = errors.New("Unexpected step")
|
ErrVoteUnexpectedStep = errors.New("Unexpected step")
|
||||||
ErrVoteInvalidAccount = errors.New("Invalid round vote account")
|
ErrVoteInvalidValidatorIndex = errors.New("Invalid round vote validator index")
|
||||||
ErrVoteInvalidSignature = errors.New("Invalid round vote signature")
|
ErrVoteInvalidValidatorAddress = errors.New("Invalid round vote validator address")
|
||||||
ErrVoteInvalidBlockHash = errors.New("Invalid block hash")
|
ErrVoteInvalidSignature = errors.New("Invalid round vote signature")
|
||||||
|
ErrVoteInvalidBlockHash = errors.New("Invalid block hash")
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrVoteConflictingSignature struct {
|
type ErrVoteConflictingSignature struct {
|
||||||
@ -28,6 +29,8 @@ func (err *ErrVoteConflictingSignature) Error() string {
|
|||||||
|
|
||||||
// Represents a prevote, precommit, or commit vote from validators for consensus.
|
// Represents a prevote, precommit, or commit vote from validators for consensus.
|
||||||
type Vote struct {
|
type Vote struct {
|
||||||
|
ValidatorAddress []byte `json:"validator_address"`
|
||||||
|
ValidatorIndex int `json:"validator_index"`
|
||||||
Height int `json:"height"`
|
Height int `json:"height"`
|
||||||
Round int `json:"round"`
|
Round int `json:"round"`
|
||||||
Type byte `json:"type"`
|
Type byte `json:"type"`
|
||||||
@ -67,5 +70,8 @@ func (vote *Vote) String() string {
|
|||||||
PanicSanity("Unknown vote type")
|
PanicSanity("Unknown vote type")
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("Vote{%v/%02d/%v(%v) %X#%v %v}", vote.Height, vote.Round, vote.Type, typeString, Fingerprint(vote.BlockHash), vote.BlockPartsHeader, vote.Signature)
|
return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %v}",
|
||||||
|
vote.ValidatorIndex, Fingerprint(vote.ValidatorAddress),
|
||||||
|
vote.Height, vote.Round, vote.Type, typeString,
|
||||||
|
Fingerprint(vote.BlockHash), vote.Signature)
|
||||||
}
|
}
|
||||||
|
@ -86,65 +86,55 @@ func (voteSet *VoteSet) Size() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns added=true, index if vote was added
|
// Returns added=true
|
||||||
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
|
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidIndex|InvalidAddress|InvalidSignature|InvalidBlockHash|ConflictingSignature]
|
||||||
// Duplicate votes return added=false, err=nil.
|
// Duplicate votes return added=false, err=nil.
|
||||||
// NOTE: vote should not be mutated after adding.
|
// NOTE: vote should not be mutated after adding.
|
||||||
func (voteSet *VoteSet) AddByIndex(valIndex int, vote *Vote) (added bool, address []byte, err error) {
|
func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) {
|
||||||
voteSet.mtx.Lock()
|
voteSet.mtx.Lock()
|
||||||
defer voteSet.mtx.Unlock()
|
defer voteSet.mtx.Unlock()
|
||||||
|
|
||||||
return voteSet.addByIndex(valIndex, vote)
|
return voteSet.addVote(vote)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns added=true, index if vote was added
|
func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
|
||||||
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
|
valIndex := vote.ValidatorIndex
|
||||||
// Duplicate votes return added=false, err=nil.
|
valAddr := vote.ValidatorAddress
|
||||||
// NOTE: vote should not be mutated after adding.
|
|
||||||
func (voteSet *VoteSet) AddByAddress(address []byte, vote *Vote) (added bool, index int, err error) {
|
|
||||||
voteSet.mtx.Lock()
|
|
||||||
defer voteSet.mtx.Unlock()
|
|
||||||
|
|
||||||
// Ensure that signer is a validator.
|
// Ensure thta validator index was set
|
||||||
valIndex, val := voteSet.valSet.GetByAddress(address)
|
if valIndex < 0 || len(valAddr) == 0 {
|
||||||
if val == nil {
|
panic("Validator index or address was not set in vote.")
|
||||||
return false, 0, ErrVoteInvalidAccount
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return voteSet.addVote(val, valIndex, vote)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (voteSet *VoteSet) addByIndex(valIndex int, vote *Vote) (added bool, address []byte, err error) {
|
|
||||||
// Ensure that signer is a validator.
|
|
||||||
address, val := voteSet.valSet.GetByIndex(valIndex)
|
|
||||||
if val == nil {
|
|
||||||
return false, nil, ErrVoteInvalidAccount
|
|
||||||
}
|
|
||||||
|
|
||||||
added, _, err = voteSet.addVote(val, valIndex, vote)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool, int, error) {
|
|
||||||
|
|
||||||
// Make sure the step matches. (or that vote is commit && round < voteSet.round)
|
// Make sure the step matches. (or that vote is commit && round < voteSet.round)
|
||||||
if (vote.Height != voteSet.height) ||
|
if (vote.Height != voteSet.height) ||
|
||||||
(vote.Round != voteSet.round) ||
|
(vote.Round != voteSet.round) ||
|
||||||
(vote.Type != voteSet.type_) {
|
(vote.Type != voteSet.type_) {
|
||||||
return false, 0, ErrVoteUnexpectedStep
|
return false, ErrVoteUnexpectedStep
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that signer is a validator.
|
||||||
|
lookupAddr, val := voteSet.valSet.GetByIndex(valIndex)
|
||||||
|
if val == nil {
|
||||||
|
return false, ErrVoteInvalidValidatorIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the signer has the right address
|
||||||
|
if !bytes.Equal(valAddr, lookupAddr) {
|
||||||
|
return false, ErrVoteInvalidValidatorAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
// If vote already exists, return false.
|
// If vote already exists, return false.
|
||||||
if existingVote := voteSet.votes[valIndex]; existingVote != nil {
|
if existingVote := voteSet.votes[valIndex]; existingVote != nil {
|
||||||
if bytes.Equal(existingVote.BlockHash, vote.BlockHash) {
|
if bytes.Equal(existingVote.BlockHash, vote.BlockHash) {
|
||||||
return false, valIndex, nil
|
return false, nil
|
||||||
} else {
|
} else {
|
||||||
// Check signature.
|
// Check signature.
|
||||||
if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
|
if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
|
||||||
// Bad signature.
|
// Bad signature.
|
||||||
return false, 0, ErrVoteInvalidSignature
|
return false, ErrVoteInvalidSignature
|
||||||
}
|
}
|
||||||
return false, valIndex, &ErrVoteConflictingSignature{
|
return false, &ErrVoteConflictingSignature{
|
||||||
VoteA: existingVote,
|
VoteA: existingVote,
|
||||||
VoteB: vote,
|
VoteB: vote,
|
||||||
}
|
}
|
||||||
@ -154,7 +144,7 @@ func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool,
|
|||||||
// Check signature.
|
// Check signature.
|
||||||
if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
|
if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
|
||||||
// Bad signature.
|
// Bad signature.
|
||||||
return false, 0, ErrVoteInvalidSignature
|
return false, ErrVoteInvalidSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add vote.
|
// Add vote.
|
||||||
@ -173,7 +163,7 @@ func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool,
|
|||||||
voteSet.maj23Exists = true
|
voteSet.maj23Exists = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, valIndex, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (voteSet *VoteSet) BitArray() *BitArray {
|
func (voteSet *VoteSet) BitArray() *BitArray {
|
||||||
|
@ -10,12 +10,21 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Move it out?
|
// NOTE: privValidators are in order
|
||||||
|
// TODO: Move it out?
|
||||||
func randVoteSet(height int, round int, type_ byte, numValidators int, votingPower int64) (*VoteSet, *ValidatorSet, []*PrivValidator) {
|
func randVoteSet(height int, round int, type_ byte, numValidators int, votingPower int64) (*VoteSet, *ValidatorSet, []*PrivValidator) {
|
||||||
valSet, privValidators := RandValidatorSet(numValidators, votingPower)
|
valSet, privValidators := RandValidatorSet(numValidators, votingPower)
|
||||||
return NewVoteSet("test_chain_id", height, round, type_, valSet), valSet, privValidators
|
return NewVoteSet("test_chain_id", height, round, type_, valSet), valSet, privValidators
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convenience: Return new vote with different validator address/index
|
||||||
|
func withValidator(vote *Vote, addr []byte, idx int) *Vote {
|
||||||
|
vote = vote.Copy()
|
||||||
|
vote.ValidatorAddress = addr
|
||||||
|
vote.ValidatorIndex = idx
|
||||||
|
return vote
|
||||||
|
}
|
||||||
|
|
||||||
// Convenience: Return new vote with different height
|
// Convenience: Return new vote with different height
|
||||||
func withHeight(vote *Vote, height int) *Vote {
|
func withHeight(vote *Vote, height int) *Vote {
|
||||||
vote = vote.Copy()
|
vote = vote.Copy()
|
||||||
@ -53,7 +62,7 @@ func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote {
|
|||||||
|
|
||||||
func signAddVote(privVal *PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) {
|
func signAddVote(privVal *PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) {
|
||||||
vote.Signature = privVal.Sign(SignBytes(voteSet.ChainID(), vote)).(crypto.SignatureEd25519)
|
vote.Signature = privVal.Sign(SignBytes(voteSet.ChainID(), vote)).(crypto.SignatureEd25519)
|
||||||
added, _, err := voteSet.AddByAddress(privVal.Address, vote)
|
added, err := voteSet.AddVote(vote)
|
||||||
return added, err
|
return added, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +84,14 @@ func TestAddVote(t *testing.T) {
|
|||||||
t.Errorf("There should be no 2/3 majority")
|
t.Errorf("There should be no 2/3 majority")
|
||||||
}
|
}
|
||||||
|
|
||||||
vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil}
|
vote := &Vote{
|
||||||
|
ValidatorAddress: val0.Address,
|
||||||
|
ValidatorIndex: 0, // since privValidators are in order
|
||||||
|
Height: height,
|
||||||
|
Round: round,
|
||||||
|
Type: VoteTypePrevote,
|
||||||
|
BlockHash: nil,
|
||||||
|
}
|
||||||
signAddVote(val0, vote, voteSet)
|
signAddVote(val0, vote, voteSet)
|
||||||
|
|
||||||
if voteSet.GetByAddress(val0.Address) == nil {
|
if voteSet.GetByAddress(val0.Address) == nil {
|
||||||
@ -94,10 +110,17 @@ func Test2_3Majority(t *testing.T) {
|
|||||||
height, round := 1, 0
|
height, round := 1, 0
|
||||||
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
|
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
|
||||||
|
|
||||||
vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil}
|
voteProto := &Vote{
|
||||||
|
ValidatorAddress: nil, // NOTE: must fill in
|
||||||
|
ValidatorIndex: -1, // NOTE: must fill in
|
||||||
|
Height: height,
|
||||||
|
Round: round,
|
||||||
|
Type: VoteTypePrevote,
|
||||||
|
BlockHash: nil,
|
||||||
|
}
|
||||||
// 6 out of 10 voted for nil.
|
// 6 out of 10 voted for nil.
|
||||||
for i := 0; i < 6; i++ {
|
for i := 0; i < 6; i++ {
|
||||||
|
vote := withValidator(voteProto, privValidators[i].Address, i)
|
||||||
signAddVote(privValidators[i], vote, voteSet)
|
signAddVote(privValidators[i], vote, voteSet)
|
||||||
}
|
}
|
||||||
hash, header, ok := voteSet.TwoThirdsMajority()
|
hash, header, ok := voteSet.TwoThirdsMajority()
|
||||||
@ -107,6 +130,7 @@ func Test2_3Majority(t *testing.T) {
|
|||||||
|
|
||||||
// 7th validator voted for some blockhash
|
// 7th validator voted for some blockhash
|
||||||
{
|
{
|
||||||
|
vote := withValidator(voteProto, privValidators[6].Address, 6)
|
||||||
signAddVote(privValidators[6], withBlockHash(vote, RandBytes(32)), voteSet)
|
signAddVote(privValidators[6], withBlockHash(vote, RandBytes(32)), voteSet)
|
||||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||||
if hash != nil || !header.IsZero() || ok {
|
if hash != nil || !header.IsZero() || ok {
|
||||||
@ -116,6 +140,7 @@ func Test2_3Majority(t *testing.T) {
|
|||||||
|
|
||||||
// 8th validator voted for nil.
|
// 8th validator voted for nil.
|
||||||
{
|
{
|
||||||
|
vote := withValidator(voteProto, privValidators[7].Address, 7)
|
||||||
signAddVote(privValidators[7], vote, voteSet)
|
signAddVote(privValidators[7], vote, voteSet)
|
||||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||||
if hash != nil || !header.IsZero() || !ok {
|
if hash != nil || !header.IsZero() || !ok {
|
||||||
@ -132,10 +157,19 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||||||
blockPartsTotal := 123
|
blockPartsTotal := 123
|
||||||
blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
|
blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
|
||||||
|
|
||||||
vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: blockHash, BlockPartsHeader: blockPartsHeader}
|
voteProto := &Vote{
|
||||||
|
ValidatorAddress: nil, // NOTE: must fill in
|
||||||
|
ValidatorIndex: -1, // NOTE: must fill in
|
||||||
|
Height: height,
|
||||||
|
Round: round,
|
||||||
|
Type: VoteTypePrevote,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockPartsHeader: blockPartsHeader,
|
||||||
|
}
|
||||||
|
|
||||||
// 66 out of 100 voted for nil.
|
// 66 out of 100 voted for nil.
|
||||||
for i := 0; i < 66; i++ {
|
for i := 0; i < 66; i++ {
|
||||||
|
vote := withValidator(voteProto, privValidators[i].Address, i)
|
||||||
signAddVote(privValidators[i], vote, voteSet)
|
signAddVote(privValidators[i], vote, voteSet)
|
||||||
}
|
}
|
||||||
hash, header, ok := voteSet.TwoThirdsMajority()
|
hash, header, ok := voteSet.TwoThirdsMajority()
|
||||||
@ -145,6 +179,7 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||||||
|
|
||||||
// 67th validator voted for nil
|
// 67th validator voted for nil
|
||||||
{
|
{
|
||||||
|
vote := withValidator(voteProto, privValidators[66].Address, 66)
|
||||||
signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet)
|
signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet)
|
||||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||||
if hash != nil || !header.IsZero() || ok {
|
if hash != nil || !header.IsZero() || ok {
|
||||||
@ -154,6 +189,7 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||||||
|
|
||||||
// 68th validator voted for a different BlockParts PartSetHeader
|
// 68th validator voted for a different BlockParts PartSetHeader
|
||||||
{
|
{
|
||||||
|
vote := withValidator(voteProto, privValidators[67].Address, 67)
|
||||||
blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
|
blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
|
||||||
signAddVote(privValidators[67], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
|
signAddVote(privValidators[67], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
|
||||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||||
@ -164,6 +200,7 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||||||
|
|
||||||
// 69th validator voted for different BlockParts Total
|
// 69th validator voted for different BlockParts Total
|
||||||
{
|
{
|
||||||
|
vote := withValidator(voteProto, privValidators[68].Address, 68)
|
||||||
blockPartsHeader := PartSetHeader{blockPartsTotal + 1, blockPartsHeader.Hash}
|
blockPartsHeader := PartSetHeader{blockPartsTotal + 1, blockPartsHeader.Hash}
|
||||||
signAddVote(privValidators[68], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
|
signAddVote(privValidators[68], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
|
||||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||||
@ -174,6 +211,7 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||||||
|
|
||||||
// 70th validator voted for different BlockHash
|
// 70th validator voted for different BlockHash
|
||||||
{
|
{
|
||||||
|
vote := withValidator(voteProto, privValidators[69].Address, 69)
|
||||||
signAddVote(privValidators[69], withBlockHash(vote, RandBytes(32)), voteSet)
|
signAddVote(privValidators[69], withBlockHash(vote, RandBytes(32)), voteSet)
|
||||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||||
if hash != nil || !header.IsZero() || ok {
|
if hash != nil || !header.IsZero() || ok {
|
||||||
@ -183,6 +221,7 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||||||
|
|
||||||
// 71st validator voted for the right BlockHash & BlockPartsHeader
|
// 71st validator voted for the right BlockHash & BlockPartsHeader
|
||||||
{
|
{
|
||||||
|
vote := withValidator(voteProto, privValidators[70].Address, 70)
|
||||||
signAddVote(privValidators[70], vote, voteSet)
|
signAddVote(privValidators[70], vote, voteSet)
|
||||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||||
if !bytes.Equal(hash, blockHash) || !header.Equals(blockPartsHeader) || !ok {
|
if !bytes.Equal(hash, blockHash) || !header.Equals(blockPartsHeader) || !ok {
|
||||||
@ -195,35 +234,58 @@ func TestBadVotes(t *testing.T) {
|
|||||||
height, round := 1, 0
|
height, round := 1, 0
|
||||||
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
|
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
|
||||||
|
|
||||||
|
voteProto := &Vote{
|
||||||
|
ValidatorAddress: nil,
|
||||||
|
ValidatorIndex: -1,
|
||||||
|
Height: height,
|
||||||
|
Round: round,
|
||||||
|
Type: VoteTypePrevote,
|
||||||
|
BlockHash: nil,
|
||||||
|
}
|
||||||
|
|
||||||
// val0 votes for nil.
|
// val0 votes for nil.
|
||||||
vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil}
|
{
|
||||||
added, err := signAddVote(privValidators[0], vote, voteSet)
|
vote := withValidator(voteProto, privValidators[0].Address, 0)
|
||||||
if !added || err != nil {
|
added, err := signAddVote(privValidators[0], vote, voteSet)
|
||||||
t.Errorf("Expected VoteSet.Add to succeed")
|
if !added || err != nil {
|
||||||
|
t.Errorf("Expected VoteSet.Add to succeed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// val0 votes again for some block.
|
// val0 votes again for some block.
|
||||||
added, err = signAddVote(privValidators[0], withBlockHash(vote, RandBytes(32)), voteSet)
|
{
|
||||||
if added || err == nil {
|
vote := withValidator(voteProto, privValidators[0].Address, 0)
|
||||||
t.Errorf("Expected VoteSet.Add to fail, dupeout.")
|
added, err := signAddVote(privValidators[0], withBlockHash(vote, RandBytes(32)), voteSet)
|
||||||
|
if added || err == nil {
|
||||||
|
t.Errorf("Expected VoteSet.Add to fail, dupeout.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// val1 votes on another height
|
// val1 votes on another height
|
||||||
added, err = signAddVote(privValidators[1], withHeight(vote, height+1), voteSet)
|
{
|
||||||
if added {
|
vote := withValidator(voteProto, privValidators[1].Address, 1)
|
||||||
t.Errorf("Expected VoteSet.Add to fail, wrong height")
|
added, err := signAddVote(privValidators[1], withHeight(vote, height+1), voteSet)
|
||||||
|
if added || err == nil {
|
||||||
|
t.Errorf("Expected VoteSet.Add to fail, wrong height")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// val2 votes on another round
|
// val2 votes on another round
|
||||||
added, err = signAddVote(privValidators[2], withRound(vote, round+1), voteSet)
|
{
|
||||||
if added {
|
vote := withValidator(voteProto, privValidators[2].Address, 2)
|
||||||
t.Errorf("Expected VoteSet.Add to fail, wrong round")
|
added, err := signAddVote(privValidators[2], withRound(vote, round+1), voteSet)
|
||||||
|
if added || err == nil {
|
||||||
|
t.Errorf("Expected VoteSet.Add to fail, wrong round")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// val3 votes of another type.
|
// val3 votes of another type.
|
||||||
added, err = signAddVote(privValidators[3], withType(vote, VoteTypePrecommit), voteSet)
|
{
|
||||||
if added {
|
vote := withValidator(voteProto, privValidators[3].Address, 3)
|
||||||
t.Errorf("Expected VoteSet.Add to fail, wrong type")
|
added, err := signAddVote(privValidators[3], withType(vote, VoteTypePrecommit), voteSet)
|
||||||
|
if added || err == nil {
|
||||||
|
t.Errorf("Expected VoteSet.Add to fail, wrong type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,11 +294,19 @@ func TestMakeCommit(t *testing.T) {
|
|||||||
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrecommit, 10, 1)
|
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrecommit, 10, 1)
|
||||||
blockHash, blockPartsHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)}
|
blockHash, blockPartsHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)}
|
||||||
|
|
||||||
vote := &Vote{Height: height, Round: round, Type: VoteTypePrecommit,
|
voteProto := &Vote{
|
||||||
BlockHash: blockHash, BlockPartsHeader: blockPartsHeader}
|
ValidatorAddress: nil,
|
||||||
|
ValidatorIndex: -1,
|
||||||
|
Height: height,
|
||||||
|
Round: round,
|
||||||
|
Type: VoteTypePrecommit,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockPartsHeader: blockPartsHeader,
|
||||||
|
}
|
||||||
|
|
||||||
// 6 out of 10 voted for some block.
|
// 6 out of 10 voted for some block.
|
||||||
for i := 0; i < 6; i++ {
|
for i := 0; i < 6; i++ {
|
||||||
|
vote := withValidator(voteProto, privValidators[i].Address, i)
|
||||||
signAddVote(privValidators[i], vote, voteSet)
|
signAddVote(privValidators[i], vote, voteSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,13 +315,15 @@ func TestMakeCommit(t *testing.T) {
|
|||||||
|
|
||||||
// 7th voted for some other block.
|
// 7th voted for some other block.
|
||||||
{
|
{
|
||||||
vote := withBlockHash(vote, RandBytes(32))
|
vote := withValidator(voteProto, privValidators[6].Address, 6)
|
||||||
|
vote = withBlockHash(vote, RandBytes(32))
|
||||||
vote = withBlockPartsHeader(vote, PartSetHeader{123, RandBytes(32)})
|
vote = withBlockPartsHeader(vote, PartSetHeader{123, RandBytes(32)})
|
||||||
signAddVote(privValidators[6], vote, voteSet)
|
signAddVote(privValidators[6], vote, voteSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The 8th voted like everyone else.
|
// The 8th voted like everyone else.
|
||||||
{
|
{
|
||||||
|
vote := withValidator(voteProto, privValidators[7].Address, 7)
|
||||||
signAddVote(privValidators[7], vote, voteSet)
|
signAddVote(privValidators[7], vote, voteSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user