From 3c5a2f55c262bfc0d39cbfd0519a5288aa728704 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Fri, 1 Jul 2016 17:47:31 -0400 Subject: [PATCH] Add validator index and address to Vote. --- consensus/common_test.go | 167 +++--------------------- consensus/height_vote_set.go | 4 +- consensus/height_vote_set_test.go | 23 ++-- consensus/reactor.go | 31 +++-- consensus/state.go | 41 +++--- consensus/state_test.go | 204 ++++++++++++++++-------------- types/events.go | 4 +- types/validator_set.go | 1 + types/vote.go | 16 ++- types/vote_set.go | 64 ++++------ types/vote_set_test.go | 122 ++++++++++++++---- 11 files changed, 321 insertions(+), 356 deletions(-) diff --git a/consensus/common_test.go b/consensus/common_test.go index 7cb3418c..44711299 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -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 } } diff --git a/consensus/height_vote_set.go b/consensus/height_vote_set.go index 5cc181b1..36d8911f 100644 --- a/consensus/height_vote_set.go +++ b/consensus/height_vote_set.go @@ -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 } diff --git a/consensus/height_vote_set_test.go b/consensus/height_vote_set_test.go index b5153259..ab0fed30 100644 --- a/consensus/height_vote_set_test.go +++ b/consensus/height_vote_set_test.go @@ -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) diff --git a/consensus/reactor.go b/consensus/reactor.go index 4d5e5618..8d872fc4 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -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) } //------------------------------------- diff --git a/consensus/state.go b/consensus/state.go index e9dd2f97..3bee75f6 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -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 { diff --git a/consensus/state_test.go b/consensus/state_test.go index a09ab16f..44eaf080 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -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 diff --git a/types/events.go b/types/events.go index 4fe4d407..fc7e0ac6 100644 --- a/types/events.go +++ b/types/events.go @@ -91,9 +91,7 @@ type EventDataRoundState struct { } type EventDataVote struct { - Index int - Address []byte - Vote *Vote + Vote *Vote } func (_ EventDataNewBlock) AssertIsTMEventData() {} diff --git a/types/validator_set.go b/types/validator_set.go index 5a51c79f..ed3ff156 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -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) diff --git a/types/vote.go b/types/vote.go index 67936927..bed249ab 100644 --- a/types/vote.go +++ b/types/vote.go @@ -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) } diff --git a/types/vote_set.go b/types/vote_set.go index e701d461..3f260154 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -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 { diff --git a/types/vote_set_test.go b/types/vote_set_test.go index ffc3a9ef..320449da 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -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) }