Add validator index and address to Vote.
This commit is contained in:
parent
0b098a2eee
commit
3c5a2f55c2
|
@ -25,19 +25,23 @@ var config cfg.Config // NOTE: must be reset for each _test.go file
|
|||
var ensureTimeout = time.Duration(2)
|
||||
|
||||
type validatorStub struct {
|
||||
Index int // Validator index. NOTE: we don't assume validator set changes.
|
||||
Height int
|
||||
Round int
|
||||
*types.PrivValidator
|
||||
}
|
||||
|
||||
func NewValidatorStub(privValidator *types.PrivValidator) *validatorStub {
|
||||
func NewValidatorStub(privValidator *types.PrivValidator, valIndex int) *validatorStub {
|
||||
return &validatorStub{
|
||||
Index: valIndex,
|
||||
PrivValidator: privValidator,
|
||||
}
|
||||
}
|
||||
|
||||
func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||
vote := &types.Vote{
|
||||
ValidatorIndex: vs.Index,
|
||||
ValidatorAddress: vs.PrivValidator.Address,
|
||||
Height: vs.Height,
|
||||
Round: vs.Round,
|
||||
Type: voteType,
|
||||
|
@ -48,7 +52,10 @@ func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartS
|
|||
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 {
|
||||
v, err := vs.signVote(voteType, hash, header)
|
||||
if err != nil {
|
||||
|
@ -57,8 +64,8 @@ func signVote(vs *validatorStub, voteType byte, hash []byte, header types.PartSe
|
|||
return v
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Create proposal block from cs1 but sign it with vs
|
||||
func decideProposal(cs1 *ConsensusState, vs *validatorStub, height, round int) (proposal *types.Proposal, block *types.Block) {
|
||||
block, blockParts := cs1.createProposalBlock()
|
||||
if block == nil { // on error
|
||||
panic("error creating proposal block")
|
||||
|
@ -66,93 +73,19 @@ func decideProposal(cs1 *ConsensusState, cs2 *validatorStub, height, round int)
|
|||
|
||||
// Make proposal
|
||||
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)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// utils
|
||||
|
||||
/*
|
||||
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 addVotes(to *ConsensusState, votes ...*types.Vote) {
|
||||
for _, vote := range votes {
|
||||
to.peerMsgQueue <- msgInfo{Msg: &VoteMessage{vote}}
|
||||
}
|
||||
}
|
||||
|
||||
func addVoteToFromMany(to *ConsensusState, votes []*types.Vote, froms ...*validatorStub) {
|
||||
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 {
|
||||
func signVotes(voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) []*types.Vote {
|
||||
votes := make([]*types.Vote, len(vss))
|
||||
for i, vs := range vss {
|
||||
votes[i] = signVote(vs, voteType, hash, header)
|
||||
|
@ -160,34 +93,9 @@ func signVoteMany(voteType byte, hash []byte, header types.PartSetHeader, vss ..
|
|||
return votes
|
||||
}
|
||||
|
||||
// add vote to one cs from another
|
||||
// if voteCh is not nil, read all votes
|
||||
func signAddVoteToFromMany(voteType byte, to *ConsensusState, hash []byte, header types.PartSetHeader, voteCh chan interface{}, froms ...*validatorStub) {
|
||||
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 signAddVotes(to *ConsensusState, voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) {
|
||||
votes := signVotes(voteType, hash, header, vss...)
|
||||
addVotes(to, votes...)
|
||||
}
|
||||
|
||||
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) {
|
||||
for _, vs := range vss {
|
||||
vs.Height += 1
|
||||
|
@ -363,7 +238,7 @@ func randConsensusState(nValidators int) (*ConsensusState, []*validatorStub) {
|
|||
cs := newConsensusState(state, privVals[0], counter.NewCounterApplication(true))
|
||||
|
||||
for i := 0; i < nValidators; i++ {
|
||||
vss[i] = NewValidatorStub(privVals[i])
|
||||
vss[i] = NewValidatorStub(privVals[i], i)
|
||||
}
|
||||
// since cs1 starts at 1
|
||||
incrementHeight(vss[1:]...)
|
||||
|
@ -379,7 +254,7 @@ func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} {
|
|||
v := <-voteCh0
|
||||
vote := v.(types.EventDataVote)
|
||||
// we only fire for our own votes
|
||||
if bytes.Equal(addr, vote.Address) {
|
||||
if bytes.Equal(addr, vote.Vote.ValidatorAddress) {
|
||||
voteCh <- v
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ func (hvs *HeightVoteSet) addRound(round int) {
|
|||
|
||||
// Duplicate votes return added=false, err=nil.
|
||||
// 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()
|
||||
defer hvs.mtx.Unlock()
|
||||
voteSet := hvs.getVoteSet(vote.Round, vote.Type)
|
||||
|
@ -117,7 +117,7 @@ func (hvs *HeightVoteSet) AddByIndex(valIndex int, vote *types.Vote, peerKey str
|
|||
return
|
||||
}
|
||||
}
|
||||
added, address, err = voteSet.AddByIndex(valIndex, vote)
|
||||
added, err = voteSet.AddVote(vote)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -17,31 +17,34 @@ func TestPeerCatchupRounds(t *testing.T) {
|
|||
|
||||
hvs := NewHeightVoteSet(config.GetString("chain_id"), 1, valSet)
|
||||
|
||||
vote999_0 := makeVoteHR(t, 1, 999, privVals[0])
|
||||
added, _, err := hvs.AddByIndex(0, vote999_0, "peer1")
|
||||
vote999_0 := makeVoteHR(t, 1, 999, privVals, 0)
|
||||
added, err := hvs.AddVote(vote999_0, "peer1")
|
||||
if !added || err != nil {
|
||||
t.Error("Expected to successfully add vote from peer", added, err)
|
||||
}
|
||||
|
||||
vote1000_0 := makeVoteHR(t, 1, 1000, privVals[0])
|
||||
added, _, err = hvs.AddByIndex(0, vote1000_0, "peer1")
|
||||
vote1000_0 := makeVoteHR(t, 1, 1000, privVals, 0)
|
||||
added, err = hvs.AddVote(vote1000_0, "peer1")
|
||||
if added {
|
||||
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 {
|
||||
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{
|
||||
Height: height,
|
||||
Round: round,
|
||||
Type: types.VoteTypePrecommit,
|
||||
BlockHash: []byte("fakehash"),
|
||||
ValidatorAddress: privVal.Address,
|
||||
ValidatorIndex: valIndex,
|
||||
Height: height,
|
||||
Round: round,
|
||||
Type: types.VoteTypePrecommit,
|
||||
BlockHash: []byte("fakehash"),
|
||||
}
|
||||
chainID := config.GetString("chain_id")
|
||||
err := privVal.SignVote(chainID, vote)
|
||||
|
|
|
@ -201,7 +201,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte)
|
|||
cs.mtx.Unlock()
|
||||
ps.EnsureVoteBitArrays(height, valSize)
|
||||
ps.EnsureVoteBitArrays(height-1, lastCommitSize)
|
||||
ps.SetHasVote(msg.Vote, msg.ValidatorIndex)
|
||||
ps.SetHasVote(msg.Vote)
|
||||
|
||||
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) {
|
||||
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.
|
||||
func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote, index int) {
|
||||
func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) {
|
||||
msg := &HasVoteMessage{
|
||||
Height: vote.Height,
|
||||
Round: vote.Round,
|
||||
Type: vote.Type,
|
||||
Index: index,
|
||||
Index: vote.ValidatorIndex,
|
||||
}
|
||||
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.
|
||||
// Returns true if vote was sent.
|
||||
func (ps *PeerState) PickSendVote(votes types.VoteSetReader) (ok bool) {
|
||||
if index, vote, ok := ps.PickVoteToSend(votes); ok {
|
||||
msg := &VoteMessage{index, vote}
|
||||
if vote, ok := ps.PickVoteToSend(votes); ok {
|
||||
msg := &VoteMessage{vote}
|
||||
ps.Peer.Send(VoteChannel, struct{ ConsensusMessage }{msg})
|
||||
return true
|
||||
}
|
||||
|
@ -622,12 +622,12 @@ func (ps *PeerState) PickSendVote(votes types.VoteSetReader) (ok bool) {
|
|||
}
|
||||
|
||||
// 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()
|
||||
defer ps.mtx.Unlock()
|
||||
|
||||
if votes.Size() == 0 {
|
||||
return 0, nil, false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
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_)
|
||||
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 {
|
||||
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 {
|
||||
|
@ -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()
|
||||
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) {
|
||||
|
@ -985,12 +985,11 @@ func (m *BlockPartMessage) String() string {
|
|||
//-------------------------------------
|
||||
|
||||
type VoteMessage struct {
|
||||
ValidatorIndex int
|
||||
Vote *types.Vote
|
||||
Vote *types.Vote
|
||||
}
|
||||
|
||||
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?
|
||||
|
||||
// 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 == "" {
|
||||
cs.internalMsgQueue <- msgInfo{&VoteMessage{valIndex, vote}, ""}
|
||||
cs.internalMsgQueue <- msgInfo{&VoteMessage{vote}, ""}
|
||||
} else {
|
||||
cs.peerMsgQueue <- msgInfo{&VoteMessage{valIndex, vote}, peerKey}
|
||||
cs.peerMsgQueue <- msgInfo{&VoteMessage{vote}, peerKey}
|
||||
}
|
||||
|
||||
// TODO: wait for event?!
|
||||
return false, nil, nil
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
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 {
|
||||
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 {
|
||||
PanicCrisis(Fmt("Failed to reconstruct LastCommit: %v", err))
|
||||
}
|
||||
|
@ -694,7 +696,7 @@ func (cs *ConsensusState) handleMsg(mi msgInfo, rs RoundState) {
|
|||
case *VoteMessage:
|
||||
// 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
|
||||
err := cs.tryAddVote(msg.ValidatorIndex, msg.Vote, peerKey)
|
||||
err := cs.tryAddVote(msg.Vote, peerKey)
|
||||
if err == ErrAddingVote {
|
||||
// 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
|
||||
func (cs *ConsensusState) tryAddVote(valIndex int, vote *types.Vote, peerKey string) error {
|
||||
_, _, err := cs.addVote(valIndex, vote, peerKey)
|
||||
func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerKey string) error {
|
||||
_, err := cs.addVote(vote, peerKey)
|
||||
if err != nil {
|
||||
// If the vote height is off, we'll just ignore it,
|
||||
// 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)
|
||||
|
||||
// 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) {
|
||||
// TODO: give the reason ..
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
|
@ -1446,9 +1447,9 @@ func (cs *ConsensusState) addVote(valIndex int, vote *types.Vote, peerKey string
|
|||
// A prevote/precommit for this height?
|
||||
if vote.Height == cs.Height {
|
||||
height := cs.Height
|
||||
added, address, err = cs.Votes.AddByIndex(valIndex, vote, peerKey)
|
||||
added, err = cs.Votes.AddVote(vote, peerKey)
|
||||
if added {
|
||||
types.FireEventVote(cs.evsw, types.EventDataVote{valIndex, address, vote})
|
||||
types.FireEventVote(cs.evsw, types.EventDataVote{vote})
|
||||
|
||||
switch vote.Type {
|
||||
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) {
|
||||
// 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{
|
||||
ValidatorAddress: cs.privValidator.Address,
|
||||
ValidatorIndex: valIndex,
|
||||
Height: cs.Height,
|
||||
Round: cs.Round,
|
||||
Type: type_,
|
||||
|
@ -1537,9 +1542,7 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.Part
|
|||
}
|
||||
vote, err := cs.signVote(type_, hash, header)
|
||||
if err == nil {
|
||||
// TODO: store our index in the cs so we don't have to do this every time
|
||||
valIndex, _ := cs.Validators.GetByAddress(cs.privValidator.Address)
|
||||
cs.sendInternalMessage(msgInfo{&VoteMessage{valIndex, vote}, ""})
|
||||
cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""})
|
||||
log.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "error", err)
|
||||
return vote
|
||||
} else {
|
||||
|
|
|
@ -74,7 +74,7 @@ func TestProposerSelection0(t *testing.T) {
|
|||
<-proposalCh
|
||||
|
||||
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
|
||||
<-newRoundCh
|
||||
|
@ -106,7 +106,7 @@ func TestProposerSelection2(t *testing.T) {
|
|||
}
|
||||
|
||||
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
|
||||
|
||||
incrementRound(vss[1:]...)
|
||||
|
@ -179,12 +179,12 @@ func TestEnterProposeYesPrivValidator(t *testing.T) {
|
|||
func TestBadProposal(t *testing.T) {
|
||||
cs1, vss := randConsensusState(2)
|
||||
height, round := cs1.Height, cs1.Round
|
||||
cs2 := vss[1]
|
||||
vs2 := vss[1]
|
||||
|
||||
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 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
|
||||
round = round + 1
|
||||
|
@ -198,9 +198,9 @@ func TestBadProposal(t *testing.T) {
|
|||
stateHash[0] = byte((stateHash[0] + 1) % 255)
|
||||
propBlock.AppHash = stateHash
|
||||
propBlockParts := propBlock.MakePartSet()
|
||||
proposal := types.NewProposal(cs2.Height, round, propBlockParts.Header(), -1)
|
||||
if err := cs2.SignProposal(config.GetString("chain_id"), proposal); err != nil {
|
||||
panic("failed to sign bad proposal: " + err.Error())
|
||||
proposal := types.NewProposal(vs2.Height, round, propBlockParts.Header(), -1)
|
||||
if err := vs2.SignProposal(config.GetString("chain_id"), proposal); err != nil {
|
||||
t.Fatal("failed to sign bad proposal", err)
|
||||
}
|
||||
|
||||
// set the proposal block
|
||||
|
@ -217,14 +217,15 @@ func TestBadProposal(t *testing.T) {
|
|||
|
||||
validatePrevote(t, cs1, round, vss[0], nil)
|
||||
|
||||
// add bad prevote from cs2 and wait for it
|
||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh)
|
||||
// add bad prevote from vs2 and wait for it
|
||||
signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet().Header(), vs2)
|
||||
<-voteCh
|
||||
|
||||
// wait for precommit
|
||||
<-voteCh
|
||||
|
||||
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
|
||||
func TestFullRound2(t *testing.T) {
|
||||
cs1, vss := randConsensusState(2)
|
||||
cs2 := vss[1]
|
||||
vs2 := vss[1]
|
||||
height, round := cs1.Height, cs1.Round
|
||||
|
||||
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
|
||||
|
@ -296,8 +297,9 @@ func TestFullRound2(t *testing.T) {
|
|||
rs := cs1.GetRoundState()
|
||||
propBlockHash, propPartsHeader := rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header()
|
||||
|
||||
// prevote arrives from cs2:
|
||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlockHash, propPartsHeader, voteCh)
|
||||
// prevote arrives from vs2:
|
||||
signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propPartsHeader, vs2)
|
||||
<-voteCh
|
||||
|
||||
<-voteCh //precommit
|
||||
|
||||
|
@ -306,8 +308,9 @@ func TestFullRound2(t *testing.T) {
|
|||
|
||||
// we should be stuck in limbo waiting for more precommits
|
||||
|
||||
// precommit arrives from cs2:
|
||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlockHash, propPartsHeader, voteCh)
|
||||
// precommit arrives from vs2:
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash, propPartsHeader, vs2)
|
||||
<-voteCh
|
||||
|
||||
// wait to finish commit, propose in next height
|
||||
<-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
|
||||
func TestLockNoPOL(t *testing.T) {
|
||||
cs1, vss := randConsensusState(2)
|
||||
cs2 := vss[1]
|
||||
vs2 := vss[1]
|
||||
height := cs1.Height
|
||||
|
||||
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
||||
|
@ -344,8 +347,9 @@ func TestLockNoPOL(t *testing.T) {
|
|||
<-voteCh // prevote
|
||||
|
||||
// we should now be stuck in limbo forever, waiting for more prevotes
|
||||
// prevote arrives from cs2:
|
||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), voteCh)
|
||||
// prevote arrives from vs2:
|
||||
signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2)
|
||||
<-voteCh // prevote
|
||||
|
||||
<-voteCh // precommit
|
||||
|
||||
|
@ -358,7 +362,8 @@ func TestLockNoPOL(t *testing.T) {
|
|||
hash := make([]byte, len(theBlockHash))
|
||||
copy(hash, theBlockHash)
|
||||
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)
|
||||
// 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
|
||||
*/
|
||||
|
||||
incrementRound(cs2)
|
||||
incrementRound(vs2)
|
||||
|
||||
// now we're on a new round and not the proposer, so wait for timeout
|
||||
re = <-timeoutProposeCh
|
||||
|
@ -389,7 +394,8 @@ func TestLockNoPOL(t *testing.T) {
|
|||
validatePrevote(t, cs1, 1, vss[0], rs.LockedBlock.Hash())
|
||||
|
||||
// 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
|
||||
// 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
|
||||
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
|
||||
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
|
||||
// then we enterPrecommitWait and timeout into NewRound
|
||||
|
@ -412,10 +419,10 @@ func TestLockNoPOL(t *testing.T) {
|
|||
<-newRoundCh
|
||||
log.Notice("#### ONTO ROUND 2")
|
||||
/*
|
||||
Round3 (cs2, _) // B, B2
|
||||
Round3 (vs2, _) // B, B2
|
||||
*/
|
||||
|
||||
incrementRound(cs2)
|
||||
incrementRound(vs2)
|
||||
|
||||
re = <-proposalCh
|
||||
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
|
||||
|
@ -429,28 +436,31 @@ func TestLockNoPOL(t *testing.T) {
|
|||
|
||||
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
|
||||
<-voteCh // precommit
|
||||
|
||||
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
|
||||
validatePrecommit(t, cs1, 2, 0, vss[0], nil, theBlockHash) // precommit nil but be locked on proposal
|
||||
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet().Header(), vs2) // NOTE: conflicting precommits at same height
|
||||
<-voteCh
|
||||
|
||||
<-timeoutWaitCh
|
||||
|
||||
// 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 {
|
||||
panic("Failed to create proposal block with cs2")
|
||||
t.Fatal("Failed to create proposal block with vs2")
|
||||
}
|
||||
|
||||
incrementRound(cs2)
|
||||
incrementRound(vs2)
|
||||
|
||||
<-newRoundCh
|
||||
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
|
||||
|
@ -463,19 +473,22 @@ func TestLockNoPOL(t *testing.T) {
|
|||
// prevote for locked block (not proposal)
|
||||
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
|
||||
<-voteCh
|
||||
|
||||
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
|
||||
validatePrecommit(t, cs1, 2, 0, vss[0], nil, theBlockHash) // precommit nil but locked on proposal
|
||||
|
||||
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
|
||||
func TestLockPOLRelock(t *testing.T) {
|
||||
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)
|
||||
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)
|
||||
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
|
||||
|
||||
/*
|
||||
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
|
||||
|
@ -501,26 +514,27 @@ func TestLockPOLRelock(t *testing.T) {
|
|||
re := <-proposalCh
|
||||
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
|
||||
theBlockHash := rs.ProposalBlock.Hash()
|
||||
theBlockPartsHeader := rs.ProposalBlockParts.Header()
|
||||
|
||||
<-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
|
||||
// the proposed block should now be locked and our precommit added
|
||||
validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
|
||||
|
||||
// add precommits from the rest
|
||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, voteCh, cs2, cs4)
|
||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, theBlockHash, theBlockPartsHeader, voteCh)
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
|
||||
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
|
||||
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
|
||||
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||
propBlockParts := propBlock.MakePartSet()
|
||||
propBlockHash := propBlock.Hash()
|
||||
|
||||
incrementRound(cs2, cs3, cs4)
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
|
||||
// timeout to new round
|
||||
<-timeoutWaitCh
|
||||
|
@ -532,7 +546,7 @@ func TestLockPOLRelock(t *testing.T) {
|
|||
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!
|
||||
*/
|
||||
|
@ -550,7 +564,8 @@ func TestLockPOLRelock(t *testing.T) {
|
|||
validatePrevote(t, cs1, 0, vss[0], theBlockHash)
|
||||
|
||||
// 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
|
||||
select {
|
||||
|
@ -564,7 +579,8 @@ func TestLockPOLRelock(t *testing.T) {
|
|||
// we should have unlocked and locked on the new block
|
||||
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
|
||||
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
|
||||
func TestLockPOLUnlock(t *testing.T) {
|
||||
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)
|
||||
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
||||
|
@ -608,7 +624,7 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||
|
||||
<-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
|
||||
|
||||
|
@ -618,14 +634,14 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||
rs = cs1.GetRoundState()
|
||||
|
||||
// add precommits from the rest
|
||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs4)
|
||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil)
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs3)
|
||||
|
||||
// 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()
|
||||
|
||||
incrementRound(cs2, cs3, cs4)
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
|
||||
// timeout to new round
|
||||
re = <-timeoutWaitCh
|
||||
|
@ -638,7 +654,7 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||
<-newRoundCh
|
||||
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!
|
||||
*/
|
||||
|
@ -655,7 +671,7 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||
<-voteCh
|
||||
validatePrevote(t, cs1, 0, vss[0], lockedBlockHash)
|
||||
// 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
|
||||
<-unlockCh
|
||||
|
@ -665,7 +681,7 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||
// NOTE: since we don't relock on nil, the lock round is 0
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -675,7 +691,7 @@ func TestLockPOLUnlock(t *testing.T) {
|
|||
// then we see the polka from round 1 but shouldn't unlock
|
||||
func TestLockPOLSafety1(t *testing.T) {
|
||||
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)
|
||||
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())
|
||||
|
||||
// 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
|
||||
// and next proposal block
|
||||
|
@ -709,13 +725,13 @@ func TestLockPOLSafety1(t *testing.T) {
|
|||
log.Warn("old prop", "hash", fmt.Sprintf("%X", propBlock.Hash()))
|
||||
|
||||
// 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()
|
||||
propBlockParts := propBlock.MakePartSet()
|
||||
|
||||
incrementRound(cs2, cs3, cs4)
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
|
||||
//XXX: this isnt gauranteed to get there before the timeoutPropose ...
|
||||
cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
|
||||
|
@ -746,18 +762,18 @@ func TestLockPOLSafety1(t *testing.T) {
|
|||
validatePrevote(t, cs1, 1, vss[0], propBlockHash)
|
||||
|
||||
// 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
|
||||
|
||||
// we should have precommitted
|
||||
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
|
||||
|
||||
incrementRound(cs2, cs3, cs4)
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
|
||||
<-newRoundCh
|
||||
|
||||
|
@ -778,7 +794,7 @@ func TestLockPOLSafety1(t *testing.T) {
|
|||
newStepCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRoundStep(), 1)
|
||||
|
||||
// add prevotes from the earlier round
|
||||
addVoteToFromMany(cs1, prevotes, cs2, cs3, cs4)
|
||||
addVotes(cs1, 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
|
||||
func TestLockPOLSafety2(t *testing.T) {
|
||||
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)
|
||||
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
|
||||
|
@ -810,14 +826,14 @@ func TestLockPOLSafety2(t *testing.T) {
|
|||
propBlockParts0 := propBlock0.MakePartSet()
|
||||
|
||||
// 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
|
||||
prop1, propBlock1 := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
|
||||
prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
|
||||
propBlockHash1 := propBlock1.Hash()
|
||||
propBlockParts1 := propBlock1.MakePartSet()
|
||||
|
||||
incrementRound(cs2, cs3, cs4)
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
|
||||
cs1.updateRoundStep(0, RoundStepPrecommitWait)
|
||||
|
||||
|
@ -832,28 +848,30 @@ func TestLockPOLSafety2(t *testing.T) {
|
|||
|
||||
<-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
|
||||
// the proposed block should now be locked and our precommit added
|
||||
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash1, propBlockHash1)
|
||||
|
||||
// add precommits from the rest
|
||||
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs4)
|
||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, propBlockHash1, propBlockParts1.Header(), nil)
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash1, propBlockParts1.Header(), vs3)
|
||||
|
||||
incrementRound(cs2, cs3, cs4)
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
|
||||
// timeout of precommit wait to new round
|
||||
<-timeoutWaitCh
|
||||
|
||||
// in round 2 we see the polkad block from round 0
|
||||
newProp := types.NewProposal(height, 2, propBlockParts0.Header(), 0)
|
||||
if err := cs3.SignProposal(config.GetString("chain_id"), newProp); err != nil {
|
||||
panic(err)
|
||||
if err := vs3.SignProposal(config.GetString("chain_id"), newProp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
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
|
||||
log.Notice("### ONTO Round 2")
|
||||
|
@ -884,7 +902,7 @@ func TestLockPOLSafety2(t *testing.T) {
|
|||
/*
|
||||
func TestSlashingPrevotes(t *testing.T) {
|
||||
cs1, vss := randConsensusState(2)
|
||||
cs2 := vss[1]
|
||||
vs2 := vss[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
|
||||
hash := rs.ProposalBlock.Hash()
|
||||
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
|
||||
|
||||
|
@ -912,14 +930,14 @@ func TestSlashingPrevotes(t *testing.T) {
|
|||
// away and ignore more prevotes (and thus fail to slash!)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func TestSlashingPrecommits(t *testing.T) {
|
||||
cs1, vss := randConsensusState(2)
|
||||
cs2 := vss[1]
|
||||
vs2 := vss[1]
|
||||
|
||||
|
||||
proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
|
||||
|
@ -933,8 +951,8 @@ func TestSlashingPrecommits(t *testing.T) {
|
|||
re := <-proposalCh
|
||||
<-voteCh // prevote
|
||||
|
||||
// add prevote from cs2
|
||||
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil)
|
||||
// add prevote from vs2
|
||||
signAddVotes(cs1, types.VoteTypePrevote, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
|
||||
|
||||
<-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
|
||||
hash := rs.ProposalBlock.Hash()
|
||||
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
|
||||
// away and ignore more prevotes (and thus fail to slash!)
|
||||
|
||||
// add precommit from cs2
|
||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(),nil)
|
||||
// add precommit from vs2
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
|
||||
|
||||
// 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!
|
||||
func TestHalt1(t *testing.T) {
|
||||
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)
|
||||
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
|
||||
|
@ -982,19 +1000,19 @@ func TestHalt1(t *testing.T) {
|
|||
|
||||
<-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
|
||||
|
||||
// the proposed block should now be locked and our precommit added
|
||||
validatePrecommit(t, cs1, 0, 0, vss[0], propBlock.Hash(), propBlock.Hash())
|
||||
|
||||
// add precommits from the rest
|
||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, nil, types.PartSetHeader{}, nil) // didnt receive proposal
|
||||
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, propBlock.Hash(), propBlockParts.Header(), nil)
|
||||
// we receive this later, but cs3 might receive it earlier and with ours will go to commit!
|
||||
precommit4 := signVote(cs4, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header())
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2) // didnt receive proposal
|
||||
signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header(), vs3)
|
||||
// we receive this later, but vs3 might receive it earlier and with ours will go to commit!
|
||||
precommit4 := signVote(vs4, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header())
|
||||
|
||||
incrementRound(cs2, cs3, cs4)
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
|
||||
// timeout to new round
|
||||
<-timeoutWaitCh
|
||||
|
@ -1012,7 +1030,7 @@ func TestHalt1(t *testing.T) {
|
|||
validatePrevote(t, cs1, 0, vss[0], rs.LockedBlock.Hash())
|
||||
|
||||
// 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
|
||||
<-newBlockCh
|
||||
|
|
|
@ -91,9 +91,7 @@ type EventDataRoundState struct {
|
|||
}
|
||||
|
||||
type EventDataVote struct {
|
||||
Index int
|
||||
Address []byte
|
||||
Vote *Vote
|
||||
Vote *Vote
|
||||
}
|
||||
|
||||
func (_ EventDataNewBlock) AssertIsTMEventData() {}
|
||||
|
|
|
@ -313,6 +313,7 @@ func (ac accumComparable) Less(o interface{}) bool {
|
|||
//----------------------------------------
|
||||
// For testing
|
||||
|
||||
// NOTE: PrivValidator are in order.
|
||||
func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []*PrivValidator) {
|
||||
vals := make([]*Validator, numValidators)
|
||||
privValidators := make([]*PrivValidator, numValidators)
|
||||
|
|
|
@ -11,10 +11,11 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrVoteUnexpectedStep = errors.New("Unexpected step")
|
||||
ErrVoteInvalidAccount = errors.New("Invalid round vote account")
|
||||
ErrVoteInvalidSignature = errors.New("Invalid round vote signature")
|
||||
ErrVoteInvalidBlockHash = errors.New("Invalid block hash")
|
||||
ErrVoteUnexpectedStep = errors.New("Unexpected step")
|
||||
ErrVoteInvalidValidatorIndex = errors.New("Invalid round vote validator index")
|
||||
ErrVoteInvalidValidatorAddress = errors.New("Invalid round vote validator address")
|
||||
ErrVoteInvalidSignature = errors.New("Invalid round vote signature")
|
||||
ErrVoteInvalidBlockHash = errors.New("Invalid block hash")
|
||||
)
|
||||
|
||||
type ErrVoteConflictingSignature struct {
|
||||
|
@ -28,6 +29,8 @@ func (err *ErrVoteConflictingSignature) Error() string {
|
|||
|
||||
// Represents a prevote, precommit, or commit vote from validators for consensus.
|
||||
type Vote struct {
|
||||
ValidatorAddress []byte `json:"validator_address"`
|
||||
ValidatorIndex int `json:"validator_index"`
|
||||
Height int `json:"height"`
|
||||
Round int `json:"round"`
|
||||
Type byte `json:"type"`
|
||||
|
@ -67,5 +70,8 @@ func (vote *Vote) String() string {
|
|||
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
|
||||
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
|
||||
// Returns added=true
|
||||
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidIndex|InvalidAddress|InvalidSignature|InvalidBlockHash|ConflictingSignature]
|
||||
// Duplicate votes return added=false, err=nil.
|
||||
// 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()
|
||||
defer voteSet.mtx.Unlock()
|
||||
|
||||
return voteSet.addByIndex(valIndex, vote)
|
||||
return voteSet.addVote(vote)
|
||||
}
|
||||
|
||||
// Returns added=true, index if vote was added
|
||||
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
|
||||
// Duplicate votes return added=false, err=nil.
|
||||
// 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()
|
||||
func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
|
||||
valIndex := vote.ValidatorIndex
|
||||
valAddr := vote.ValidatorAddress
|
||||
|
||||
// Ensure that signer is a validator.
|
||||
valIndex, val := voteSet.valSet.GetByAddress(address)
|
||||
if val == nil {
|
||||
return false, 0, ErrVoteInvalidAccount
|
||||
// Ensure thta validator index was set
|
||||
if valIndex < 0 || len(valAddr) == 0 {
|
||||
panic("Validator index or address was not set in vote.")
|
||||
}
|
||||
|
||||
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)
|
||||
if (vote.Height != voteSet.height) ||
|
||||
(vote.Round != voteSet.round) ||
|
||||
(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 existingVote := voteSet.votes[valIndex]; existingVote != nil {
|
||||
if bytes.Equal(existingVote.BlockHash, vote.BlockHash) {
|
||||
return false, valIndex, nil
|
||||
return false, nil
|
||||
} else {
|
||||
// Check signature.
|
||||
if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
|
||||
// Bad signature.
|
||||
return false, 0, ErrVoteInvalidSignature
|
||||
return false, ErrVoteInvalidSignature
|
||||
}
|
||||
return false, valIndex, &ErrVoteConflictingSignature{
|
||||
return false, &ErrVoteConflictingSignature{
|
||||
VoteA: existingVote,
|
||||
VoteB: vote,
|
||||
}
|
||||
|
@ -154,7 +144,7 @@ func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool,
|
|||
// Check signature.
|
||||
if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
|
||||
// Bad signature.
|
||||
return false, 0, ErrVoteInvalidSignature
|
||||
return false, ErrVoteInvalidSignature
|
||||
}
|
||||
|
||||
// Add vote.
|
||||
|
@ -173,7 +163,7 @@ func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool,
|
|||
voteSet.maj23Exists = true
|
||||
}
|
||||
|
||||
return true, valIndex, nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (voteSet *VoteSet) BitArray() *BitArray {
|
||||
|
|
|
@ -10,12 +10,21 @@ import (
|
|||
"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) {
|
||||
valSet, privValidators := RandValidatorSet(numValidators, votingPower)
|
||||
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
|
||||
func withHeight(vote *Vote, height int) *Vote {
|
||||
vote = vote.Copy()
|
||||
|
@ -53,7 +62,7 @@ func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote {
|
|||
|
||||
func signAddVote(privVal *PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -75,7 +84,14 @@ func TestAddVote(t *testing.T) {
|
|||
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)
|
||||
|
||||
if voteSet.GetByAddress(val0.Address) == nil {
|
||||
|
@ -94,10 +110,17 @@ func Test2_3Majority(t *testing.T) {
|
|||
height, round := 1, 0
|
||||
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.
|
||||
for i := 0; i < 6; i++ {
|
||||
vote := withValidator(voteProto, privValidators[i].Address, i)
|
||||
signAddVote(privValidators[i], vote, voteSet)
|
||||
}
|
||||
hash, header, ok := voteSet.TwoThirdsMajority()
|
||||
|
@ -107,6 +130,7 @@ func Test2_3Majority(t *testing.T) {
|
|||
|
||||
// 7th validator voted for some blockhash
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[6].Address, 6)
|
||||
signAddVote(privValidators[6], withBlockHash(vote, RandBytes(32)), voteSet)
|
||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||
if hash != nil || !header.IsZero() || ok {
|
||||
|
@ -116,6 +140,7 @@ func Test2_3Majority(t *testing.T) {
|
|||
|
||||
// 8th validator voted for nil.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[7].Address, 7)
|
||||
signAddVote(privValidators[7], vote, voteSet)
|
||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||
if hash != nil || !header.IsZero() || !ok {
|
||||
|
@ -132,10 +157,19 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||
blockPartsTotal := 123
|
||||
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.
|
||||
for i := 0; i < 66; i++ {
|
||||
vote := withValidator(voteProto, privValidators[i].Address, i)
|
||||
signAddVote(privValidators[i], vote, voteSet)
|
||||
}
|
||||
hash, header, ok := voteSet.TwoThirdsMajority()
|
||||
|
@ -145,6 +179,7 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||
|
||||
// 67th validator voted for nil
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[66].Address, 66)
|
||||
signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet)
|
||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||
if hash != nil || !header.IsZero() || ok {
|
||||
|
@ -154,6 +189,7 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||
|
||||
// 68th validator voted for a different BlockParts PartSetHeader
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[67].Address, 67)
|
||||
blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
|
||||
signAddVote(privValidators[67], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
|
||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||
|
@ -164,6 +200,7 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||
|
||||
// 69th validator voted for different BlockParts Total
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[68].Address, 68)
|
||||
blockPartsHeader := PartSetHeader{blockPartsTotal + 1, blockPartsHeader.Hash}
|
||||
signAddVote(privValidators[68], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
|
||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||
|
@ -174,6 +211,7 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||
|
||||
// 70th validator voted for different BlockHash
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[69].Address, 69)
|
||||
signAddVote(privValidators[69], withBlockHash(vote, RandBytes(32)), voteSet)
|
||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||
if hash != nil || !header.IsZero() || ok {
|
||||
|
@ -183,6 +221,7 @@ func Test2_3MajorityRedux(t *testing.T) {
|
|||
|
||||
// 71st validator voted for the right BlockHash & BlockPartsHeader
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[70].Address, 70)
|
||||
signAddVote(privValidators[70], vote, voteSet)
|
||||
hash, header, ok = voteSet.TwoThirdsMajority()
|
||||
if !bytes.Equal(hash, blockHash) || !header.Equals(blockPartsHeader) || !ok {
|
||||
|
@ -195,35 +234,58 @@ func TestBadVotes(t *testing.T) {
|
|||
height, round := 1, 0
|
||||
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.
|
||||
vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil}
|
||||
added, err := signAddVote(privValidators[0], vote, voteSet)
|
||||
if !added || err != nil {
|
||||
t.Errorf("Expected VoteSet.Add to succeed")
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[0].Address, 0)
|
||||
added, err := signAddVote(privValidators[0], vote, voteSet)
|
||||
if !added || err != nil {
|
||||
t.Errorf("Expected VoteSet.Add to succeed")
|
||||
}
|
||||
}
|
||||
|
||||
// val0 votes again for some block.
|
||||
added, err = signAddVote(privValidators[0], withBlockHash(vote, RandBytes(32)), voteSet)
|
||||
if added || err == nil {
|
||||
t.Errorf("Expected VoteSet.Add to fail, dupeout.")
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[0].Address, 0)
|
||||
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
|
||||
added, err = signAddVote(privValidators[1], withHeight(vote, height+1), voteSet)
|
||||
if added {
|
||||
t.Errorf("Expected VoteSet.Add to fail, wrong height")
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[1].Address, 1)
|
||||
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
|
||||
added, err = signAddVote(privValidators[2], withRound(vote, round+1), voteSet)
|
||||
if added {
|
||||
t.Errorf("Expected VoteSet.Add to fail, wrong round")
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[2].Address, 2)
|
||||
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.
|
||||
added, err = signAddVote(privValidators[3], withType(vote, VoteTypePrecommit), voteSet)
|
||||
if added {
|
||||
t.Errorf("Expected VoteSet.Add to fail, wrong type")
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[3].Address, 3)
|
||||
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)
|
||||
blockHash, blockPartsHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)}
|
||||
|
||||
vote := &Vote{Height: height, Round: round, Type: VoteTypePrecommit,
|
||||
BlockHash: blockHash, BlockPartsHeader: blockPartsHeader}
|
||||
voteProto := &Vote{
|
||||
ValidatorAddress: nil,
|
||||
ValidatorIndex: -1,
|
||||
Height: height,
|
||||
Round: round,
|
||||
Type: VoteTypePrecommit,
|
||||
BlockHash: blockHash,
|
||||
BlockPartsHeader: blockPartsHeader,
|
||||
}
|
||||
|
||||
// 6 out of 10 voted for some block.
|
||||
for i := 0; i < 6; i++ {
|
||||
vote := withValidator(voteProto, privValidators[i].Address, i)
|
||||
signAddVote(privValidators[i], vote, voteSet)
|
||||
}
|
||||
|
||||
|
@ -245,13 +315,15 @@ func TestMakeCommit(t *testing.T) {
|
|||
|
||||
// 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)})
|
||||
signAddVote(privValidators[6], vote, voteSet)
|
||||
}
|
||||
|
||||
// The 8th voted like everyone else.
|
||||
{
|
||||
vote := withValidator(voteProto, privValidators[7].Address, 7)
|
||||
signAddVote(privValidators[7], vote, voteSet)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue