From 2b8157ce2ae18411cbe59e164c56bac521493239 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 26 Aug 2015 18:56:34 -0400 Subject: [PATCH] addVote takes index --- consensus/height_vote_set.go | 4 +- consensus/reactor.go | 21 +++--- consensus/state.go | 136 ++++++++++++++--------------------- consensus/test.go | 76 +++++++++++++++++++- rpc/core_client/ws_client.go | 1 - types/vote_set.go | 11 +-- 6 files changed, 149 insertions(+), 100 deletions(-) diff --git a/consensus/height_vote_set.go b/consensus/height_vote_set.go index 6613bdba3..eb680cd6e 100644 --- a/consensus/height_vote_set.go +++ b/consensus/height_vote_set.go @@ -88,7 +88,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) AddByAddress(address []byte, vote *types.Vote, peerKey string) (added bool, index int, err error) { +func (hvs *HeightVoteSet) AddByIndex(valIndex int, vote *types.Vote, peerKey string) (added bool, address []byte, err error) { hvs.mtx.Lock() defer hvs.mtx.Unlock() voteSet := hvs.getVoteSet(vote.Round, vote.Type) @@ -104,7 +104,7 @@ func (hvs *HeightVoteSet) AddByAddress(address []byte, vote *types.Vote, peerKey } return } - added, index, err = voteSet.AddByAddress(address, vote) + added, address, err = voteSet.AddByIndex(valIndex, vote) return } diff --git a/consensus/reactor.go b/consensus/reactor.go index 0869cf8af..f04c3f6a3 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -137,8 +137,7 @@ func (conR *ConsensusReactor) Receive(chID byte, peer *p2p.Peer, msgBytes []byte return } - // Get round state - rs := conR.conS.GetRoundState() + // Get peer states ps := peer.Data.Get(PeerStateKey).(*PeerState) _, msg, err := DecodeMessage(msgBytes) if err != nil { @@ -146,13 +145,13 @@ func (conR *ConsensusReactor) Receive(chID byte, peer *p2p.Peer, msgBytes []byte // TODO punish peer? return } - log.Debug("Receive", "channel", chID, "peer", peer, "msg", msg, "rsHeight", rs.Height) + log.Debug("Receive", "channel", chId, "peer", peer, "msg", msg) switch chID { case StateChannel: switch msg := msg.(type) { case *NewRoundStepMessage: - ps.ApplyNewRoundStepMessage(msg, rs) + ps.ApplyNewRoundStepMessage(msg) case *CommitStepMessage: ps.ApplyCommitStepMessage(msg) case *HasVoteMessage: @@ -189,15 +188,19 @@ func (conR *ConsensusReactor) Receive(chID byte, peer *p2p.Peer, msgBytes []byte vote, valIndex := msg.Vote, msg.ValidatorIndex // attempt to add the vote and dupeout the validator if its a duplicate signature - added, err := conR.conS.TryAddVote(rs, vote, valIndex, peer.Key) + added, err := conR.conS.TryAddVote(valIndex, vote, peer.Key) if err == ErrAddingVote { // TODO: punish peer } else if err != nil { return } - ps.EnsureVoteBitArrays(rs.Height, rs.Validators.Size()) - ps.EnsureVoteBitArrays(rs.Height-1, rs.LastCommit.Size()) + cs := conR.conS + cs.mtx.Lock() + height, valSize, lastCommitSize := cs.Height, cs.Validators.Size(), cs.LastCommit.Size() + cs.mtx.Unlock() + ps.EnsureVoteBitArrays(height, valSize) + ps.EnsureVoteBitArrays(height-1, lastCommitSize) ps.SetHasVote(vote, valIndex) if added { @@ -208,7 +211,7 @@ func (conR *ConsensusReactor) Receive(chID byte, peer *p2p.Peer, msgBytes []byte } default: - // TODO: should these be punishable? + // don't punish (leave room for soft upgrades) log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg))) } default: @@ -793,7 +796,7 @@ func (ps *PeerState) setHasVote(height int, round int, type_ byte, index int) { } } -func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage, rs *RoundState) { +func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) { ps.mtx.Lock() defer ps.mtx.Unlock() diff --git a/consensus/state.go b/consensus/state.go index 2ec7b8768..3c8fb0124 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -180,6 +180,7 @@ var ( ErrInvalidProposalSignature = errors.New("Error invalid proposal signature") ErrInvalidProposalPOLRound = errors.New("Error invalid proposal POL round") ErrAddingVote = errors.New("Error adding vote") + ErrVoteHeightMismatch = errors.New("Error vote height mismatch") ) //----------------------------------------------------------------------------- @@ -300,7 +301,7 @@ type ConsensusState struct { evsw events.Fireable evc *events.EventCache // set in stageBlock and passed into state - timeoutChan chan TimeoutEvent // RoundState instead? + timeoutChan chan TimeoutEvent // so we can track timeouts timeoutQuitChan chan struct{} } @@ -499,7 +500,7 @@ func (cs *ConsensusState) EnterNewRound(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != RoundStepNewHeight) { - log.Info(Fmt("EnterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + log.Debug(Fmt("EnterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) return } if now := time.Now(); cs.StartTime.After(now) { @@ -538,39 +539,29 @@ func (cs *ConsensusState) EnterPropose(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPropose <= cs.Step) { - log.Info(Fmt("EnterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + log.Debug(Fmt("EnterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) return } log.Info(Fmt("EnterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) - var enterPrevote = make(chan struct{}, 1) - defer func() { // Done EnterPropose: cs.Round = round cs.Step = RoundStepPropose - enterPrevote <- struct{}{} + cs.newStepCh <- cs.getRoundState() + + // If we have the whole proposal + POL, then goto Prevote now. + // else, we'll EnterPrevote when the rest of the proposal is received (in AddProposalBlockPart), + // or else after timeoutPropose + if cs.isProposalComplete() { + go cs.EnterPrevote(height, cs.Round) + } }() - // EnterPrevote after timeoutPropose or if the proposal is complete + // This step times out after `timeoutPropose` go func() { - ticker := time.NewTicker(timeoutPropose) - LOOP: - for { - select { - case <-ticker.C: - enterPrevote <- struct{}{} - cs.timeoutChan <- TimeoutEvent{RoundStepPropose, height, round} - break LOOP - case <-enterPrevote: - // If we already have the proposal + POL, then goto Prevote - if cs.isProposalComplete() { - break LOOP - } - } - } - cs.newStepCh <- cs.getRoundState() - go cs.EnterPrevote(height, round) + time.Sleep(timeoutPropose) + cs.EnterPrevote(height, round) }() // Nothing more to do if we're not a validator @@ -582,7 +573,7 @@ func (cs *ConsensusState) EnterPropose(height int, round int) { log.Info("EnterPropose: Not our turn to propose", "proposer", cs.Validators.Proposer().Address, "privValidator", cs.privValidator) } else { log.Info("EnterPropose: Our turn to propose", "proposer", cs.Validators.Proposer().Address, "privValidator", cs.privValidator) - cs.decideProposal(height, round) // if this takes longer than timeoutPropose we'll catch it in a later EnterPropose + cs.decideProposal(height, round) } } @@ -633,8 +624,7 @@ func (cs *ConsensusState) isProposalComplete() bool { } // Create the next block to propose and return it. -// NOTE: make it side-effect free for clarity. -// XXX: where are the side-effects? +// NOTE: keep it side-effect free for clarity. func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts *types.PartSet) { var validation *types.Validation if cs.Height == 1 { @@ -688,7 +678,7 @@ func (cs *ConsensusState) EnterPrevote(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrevote <= cs.Step) { - log.Info(Fmt("EnterPrevote(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + log.Debug(Fmt("EnterPrevote(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) return } log.Info(Fmt("EnterPrevote(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) @@ -742,7 +732,7 @@ func (cs *ConsensusState) EnterPrevoteWait(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrevoteWait <= cs.Step) { - log.Info(Fmt("EnterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + log.Debug(Fmt("EnterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) return } if !cs.Votes.Prevotes(round).HasTwoThirdsAny() { @@ -766,15 +756,14 @@ func (cs *ConsensusState) EnterPrevoteWait(height int, round int) { // Enter: +2/3 precomits for block or nil. // Enter: `timeoutPrevote` after any +2/3 prevotes. // Enter: any +2/3 precommits for next round. -// Lock & precommit the ProposalBlock if we have enough prevotes for it, +// Lock & precommit the ProposalBlock if we have enough prevotes for it (a POL in this round) // else, unlock an existing lock and precommit nil if +2/3 of prevotes were nil, // else, precommit nil otherwise. -// NOTE: we don't precommit our locked block (unless theres another POL for it) because it complicates unlocking and accountability func (cs *ConsensusState) EnterPrecommit(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrecommit <= cs.Step) { - log.Info(Fmt("EnterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + log.Debug(Fmt("EnterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) return } log.Info(Fmt("EnterPrecommit(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) @@ -792,9 +781,7 @@ func (cs *ConsensusState) EnterPrecommit(height int, round int) { hash, partsHeader, ok := cs.Votes.Prevotes(round).TwoThirdsMajority() - // If we don't have two thirds of prevotes, just precommit nil - // NOTE: alternatively, if we have seen a POL since our last precommit, - // we could precommit that + // If we don't have two thirds of prevotes, we must precommit nil if !ok { if cs.LockedBlock != nil { log.Info("EnterPrecommit: No +2/3 prevotes during EnterPrecommit while we're locked. Precommitting nil") @@ -814,7 +801,7 @@ func (cs *ConsensusState) EnterPrecommit(height int, round int) { log.Info("EnterPrecommit: +2/3 prevoted for nil.") } else { log.Info("EnterPrecommit: +2/3 prevoted for nil. Unlocking") - cs.LockedRound = 0 //XXX: should be this round + cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil } @@ -834,7 +821,7 @@ func (cs *ConsensusState) EnterPrecommit(height int, round int) { // If +2/3 prevoted for proposal block, stage and precommit it if cs.ProposalBlock.HashesTo(hash) { - log.Info("EnterPrecommit: +2/3 prevoted proposal block.", "hash", fmt.Sprintf("%X", hash)) + log.Info("EnterPrecommit: +2/3 prevoted proposal block.", "hash", hash) // Validate the block. if err := cs.stageBlock(cs.ProposalBlock, cs.ProposalBlockParts); err != nil { PanicConsensus(Fmt("EnterPrecommit: +2/3 prevoted for an invalid block: %v", err)) @@ -872,7 +859,7 @@ func (cs *ConsensusState) EnterPrecommitWait(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrecommitWait <= cs.Step) { - log.Info(Fmt("EnterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + log.Debug(Fmt("EnterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) return } if !cs.Votes.Precommits(round).HasTwoThirdsAny() { @@ -902,7 +889,7 @@ func (cs *ConsensusState) EnterCommit(height int, commitRound int) { cs.mtx.Lock() defer cs.mtx.Unlock() if cs.Height != height || RoundStepCommit <= cs.Step { - log.Info(Fmt("EnterCommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step)) + log.Debug(Fmt("EnterCommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step)) return } log.Info(Fmt("EnterCommit(%v/%v). Current: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step)) @@ -924,20 +911,10 @@ func (cs *ConsensusState) EnterCommit(height int, commitRound int) { // The Locked* fields no longer matter. // Move them over to ProposalBlock if they match the commit hash, - // otherwise they can now be cleared. - // XXX: can't we just wait to clear them in updateToState ? - // XXX: it's causing a race condition in tests where they get cleared - // before we can check the lock! + // otherwise they'll be cleared in updateToState. if cs.LockedBlock.HashesTo(hash) { cs.ProposalBlock = cs.LockedBlock cs.ProposalBlockParts = cs.LockedBlockParts - /*cs.LockedRound = 0 - cs.LockedBlock = nil - cs.LockedBlockParts = nil*/ - } else { - /*cs.LockedRound = 0 - cs.LockedBlock = nil - cs.LockedBlockParts = nil*/ } // If we don't have the block being committed, set up to get it. @@ -977,7 +954,7 @@ func (cs *ConsensusState) FinalizeCommit(height, round int) { defer cs.mtx.Unlock() if cs.Height != height || cs.Step != RoundStepCommit { - log.Info(Fmt("FinalizeCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step)) + log.Debug(Fmt("FinalizeCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step)) return } @@ -1078,7 +1055,6 @@ func (cs *ConsensusState) AddProposalBlockPart(height int, part *types.Part) (ad log.Info("Received complete proposal", "hash", cs.ProposalBlock.Hash()) if cs.Step == RoundStepPropose && cs.isProposalComplete() { // Move onto the next step - // XXX: isn't this unecessary since propose will either do this or timeout into it go cs.EnterPrevote(height, cs.Round) } else if cs.Step == RoundStepCommit { // If we're waiting on the proposal block... @@ -1089,35 +1065,23 @@ func (cs *ConsensusState) AddProposalBlockPart(height int, part *types.Part) (ad return added, nil } -func (cs *ConsensusState) AddVote(address []byte, vote *types.Vote, peerKey string) (added bool, index int, err error) { +func (cs *ConsensusState) AddVote(valIndex int, vote *types.Vote, peerKey string) (added bool, address []byte, err error) { cs.mtx.Lock() defer cs.mtx.Unlock() - return cs.addVote(address, vote, peerKey) + return cs.addVote(valIndex, vote, peerKey) } // Attempt to add the vote. if its a duplicate signature, dupeout the validator -func (cs *ConsensusState) TryAddVote(rs *RoundState, vote *types.Vote, valIndex int, peerKey string) (bool, error) { - var validators *types.ValidatorSet - if rs.Height == vote.Height { - validators = rs.Validators - } else if rs.Height == vote.Height+1 { - if !(rs.Step == RoundStepNewHeight && vote.Type == types.VoteTypePrecommit) { - return false, fmt.Errorf("TryAddVote: Wrong height, not a LastCommit straggler commit.") - } - validators = rs.LastValidators - } else { - return false, fmt.Errorf("TryAddVote: Wrong height. Not necessarily a bad peer.") - } - - // We have vote/validators. Height may not be rs.Height - - address, _ := validators.GetByIndex(valIndex) - added, index, err := cs.AddVote(address, vote, peerKey) - _ = index // should be same as valIndex +func (cs *ConsensusState) TryAddVote(valIndex int, vote *types.Vote, peerKey string) (bool, error) { + added, address, err := cs.AddVote(valIndex, vote, peerKey) if err != nil { - // If conflicting sig, broadcast evidence tx for slashing. Else punish peer. - if errDupe, ok := err.(*types.ErrVoteConflictingSignature); ok { + // If the vote height is off, we'll just ignore it, + // But if it's a conflicting sig, broadcast evidence tx for slashing + // and otherwise punish peer. + if err == ErrVoteHeightMismatch { + return added, err + } else if errDupe, ok := err.(*types.ErrVoteConflictingSignature); ok { log.Warn("Found conflicting vote. Publish evidence") evidenceTx := &types.DupeoutTx{ Address: address, @@ -1137,12 +1101,17 @@ func (cs *ConsensusState) TryAddVote(rs *RoundState, vote *types.Vote, valIndex //----------------------------------------------------------------------------- -func (cs *ConsensusState) addVote(address []byte, vote *types.Vote, peerKey string) (added bool, index int, err error) { +func (cs *ConsensusState) addVote(valIndex int, vote *types.Vote, peerKey string) (added bool, address []byte, err error) { log.Debug("addVote", "voteHeight", vote.Height, "voteType", vote.Type, "csHeight", cs.Height) // A precommit for the previous height? - if vote.Height+1 == cs.Height && vote.Type == types.VoteTypePrecommit { - added, index, err = cs.LastCommit.AddByAddress(address, vote) + if vote.Height+1 == cs.Height { + 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 + } + added, address, err = cs.LastCommit.AddByIndex(valIndex, vote) if added { log.Info(Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort())) } @@ -1152,7 +1121,7 @@ func (cs *ConsensusState) addVote(address []byte, vote *types.Vote, peerKey stri // A prevote/precommit for this height? if vote.Height == cs.Height { height := cs.Height - added, index, err = cs.Votes.AddByAddress(address, vote, peerKey) + added, address, err = cs.Votes.AddByIndex(valIndex, vote, peerKey) if added { switch vote.Type { case types.VoteTypePrevote: @@ -1167,7 +1136,7 @@ func (cs *ConsensusState) addVote(address []byte, vote *types.Vote, peerKey stri hash, _, ok := prevotes.TwoThirdsMajority() if ok && !cs.LockedBlock.HashesTo(hash) { log.Notice("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round) - cs.LockedRound = 0 // XXX: shouldn't we set this to the current round? + cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil } @@ -1214,8 +1183,10 @@ func (cs *ConsensusState) addVote(address []byte, vote *types.Vote, peerKey stri PanicSanity(Fmt("Unexpected vote type %X", vote.Type)) // Should not happen. } } - // Either duplicate, or error upon cs.Votes.AddByAddress() + // Either duplicate, or error upon cs.Votes.AddByIndex() return + } else { + err = ErrVoteHeightMismatch } // Height mismatch, bad peer? @@ -1270,7 +1241,9 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.Part } vote, err := cs.signVote(type_, hash, header) if err == nil { - _, _, err := cs.addVote(cs.privValidator.Address, vote, "") + // NOTE: store our index in the cs so we don't have to do this every time + valIndex, _ := cs.Validators.GetByAddress(cs.privValidator.Address) + _, _, err := cs.addVote(valIndex, vote, "") log.Notice("Signed and added vote", "height", cs.Height, "round", cs.Round, "vote", vote, "error", err) return vote } else { @@ -1333,7 +1306,6 @@ func (cs *ConsensusState) logTimeouts(timeoutChan chan TimeoutEvent, quitChan <- select { case timeout := <-timeoutChan: log.Info("Timeout in consensus state", "height", timeout.Height, "round", timeout.Round, "step", timeout.Type.String()) - timeoutChan <- timeout case <-quitChan: return diff --git a/consensus/test.go b/consensus/test.go index 1633b31a9..be0066e55 100644 --- a/consensus/test.go +++ b/consensus/test.go @@ -17,9 +17,47 @@ import ( //------------------------------------------------------------------------------- // utils +func changeProposer(t *testing.T, perspectiveOf, newProposer *ConsensusState) *types.Block { + _, v1 := perspectiveOf.Validators.GetByAddress(perspectiveOf.privValidator.Address) + v1.Accum, v1.VotingPower = 0, 0 + if updated := perspectiveOf.Validators.Update(v1); !updated { + t.Fatal("failed to update validator") + } + _, v2 := perspectiveOf.Validators.GetByAddress(newProposer.privValidator.Address) + v2.Accum, v2.VotingPower = 100, 100 + if updated := perspectiveOf.Validators.Update(v2); !updated { + t.Fatal("failed to update validator") + } + + // make the proposal + propBlock, _ := newProposer.createProposalBlock() + if propBlock == nil { + t.Fatal("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 { + t.Fatal("failed to update validator") + } +} + +func addVoteToFromMany(to *ConsensusState, votes []*types.Vote, froms ...*ConsensusState) { + 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, from *ConsensusState, vote *types.Vote) { valIndex, _ := to.Validators.GetByAddress(from.privValidator.Address) - added, err := to.TryAddVote(to.GetRoundState(), vote, valIndex, "") + added, err := to.TryAddVote(valIndex, vote, "") if _, ok := err.(*types.ErrVoteConflictingSignature); ok { // let it fly } else if !added { @@ -37,7 +75,22 @@ func signVote(from *ConsensusState, voteType byte, hash []byte, header types.Par return vote } +func signVoteMany(voteType byte, hash []byte, header types.PartSetHeader, css ...*ConsensusState) []*types.Vote { + votes := make([]*types.Vote, len(css)) + for i, cs := range css { + votes[i] = signVote(cs, voteType, hash, header) + } + return votes +} + // add vote to one cs from another +func signAddVoteToFromMany(voteType byte, to *ConsensusState, hash []byte, header types.PartSetHeader, froms ...*ConsensusState) { + for _, from := range froms { + vote := signVote(from, voteType, hash, header) + addVoteToFrom(to, from, vote) + } +} + func signAddVoteToFrom(voteType byte, to, from *ConsensusState, hash []byte, header types.PartSetHeader) *types.Vote { vote := signVote(from, voteType, hash, header) addVoteToFrom(to, from, vote) @@ -81,6 +134,12 @@ func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *types } } +func incrementRound(css ...*ConsensusState) { + for _, cs := range css { + cs.Round += 1 + } +} + func validatePrecommit(t *testing.T, cs *ConsensusState, thisRound, lockRound int, privVal *types.PrivValidator, votedBlockHash, lockedBlockHash []byte) { precommits := cs.Votes.Precommits(thisRound) var vote *types.Vote @@ -110,6 +169,20 @@ func validatePrecommit(t *testing.T, cs *ConsensusState, thisRound, lockRound in } +func validatePrevoteAndPrecommit(t *testing.T, cs *ConsensusState, thisRound, lockRound int, privVal *types.PrivValidator, votedBlockHash, lockedBlockHash []byte, f func()) { + // verify the prevote + validatePrevote(t, cs, thisRound, privVal, votedBlockHash) + if f != nil { + f() + } + // wait to finish precommit + <-cs.NewStepCh() + // verify precommit + cs.mtx.Lock() + validatePrecommit(t, cs, thisRound, lockRound, privVal, votedBlockHash, lockedBlockHash) + cs.mtx.Unlock() +} + func simpleConsensusState(nValidators int) ([]*ConsensusState, []*types.PrivValidator) { // Get State state, privAccs, privVals := sm.RandGenesisState(10, true, 1000, nValidators, false, 10) @@ -131,6 +204,7 @@ func simpleConsensusState(nValidators int) ([]*ConsensusState, []*types.PrivVali // Make ConsensusReactor cs := NewConsensusState(state, blockStore, mempoolReactor) + cs.SetPrivValidator(privVals[i]) // read off the NewHeightStep <-cs.NewStepCh() diff --git a/rpc/core_client/ws_client.go b/rpc/core_client/ws_client.go index 5a719525e..904fbd943 100644 --- a/rpc/core_client/ws_client.go +++ b/rpc/core_client/ws_client.go @@ -7,7 +7,6 @@ import ( "github.com/tendermint/tendermint/Godeps/_workspace/src/github.com/gorilla/websocket" . "github.com/tendermint/tendermint/common" - _ "github.com/tendermint/tendermint/config/tendermint_test" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/rpc/types" "github.com/tendermint/tendermint/wire" diff --git a/types/vote_set.go b/types/vote_set.go index 23a69892d..621e30763 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -85,7 +85,7 @@ func (voteSet *VoteSet) Size() int { // 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) AddByIndex(valIndex int, vote *Vote) (added bool, index int, err error) { +func (voteSet *VoteSet) AddByIndex(valIndex int, vote *Vote) (added bool, address []byte, err error) { voteSet.mtx.Lock() defer voteSet.mtx.Unlock() @@ -109,14 +109,15 @@ func (voteSet *VoteSet) AddByAddress(address []byte, vote *Vote) (added bool, in return voteSet.addVote(val, valIndex, vote) } -func (voteSet *VoteSet) addByIndex(valIndex int, vote *Vote) (bool, int, error) { +func (voteSet *VoteSet) addByIndex(valIndex int, vote *Vote) (added bool, address []byte, err error) { // Ensure that signer is a validator. - _, val := voteSet.valSet.GetByIndex(valIndex) + address, val := voteSet.valSet.GetByIndex(valIndex) if val == nil { - return false, 0, ErrVoteInvalidAccount + return false, nil, ErrVoteInvalidAccount } - return voteSet.addVote(val, valIndex, vote) + added, _, err = voteSet.addVote(val, valIndex, vote) + return } func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool, int, error) {