func (hvs *HeightVoteSet) POLInfo() (polRound def.INT, polBlockID pbtypes.BlockID) {
	hvs.mtx.Lock()
	defer hvs.mtx.Unlock()
	for r := hvs.round; r >= 0; r-- {
		rvs := hvs.getVoteSet(r, pbtypes.VoteType_Prevote)
		polBlockID, ok := rvs.TwoThirdsMajority()
		if ok {
			return r, polBlockID
		}
	}
	return -1, pbtypes.BlockID{}
}
Last round and blockID that has +2/3 prevotes for a particular block or nil.
Returns -1 if no such round exists.
package consensus
import (
	"strings"
	"sync"
	pbtypes "github.com/annchain/annchain/angine/protos/types"
	agtypes "github.com/annchain/annchain/angine/types"
	"github.com/pkg/errors"
	. "github.com/annchain/annchain/module/lib/go-common"
	"github.com/annchain/annchain/module/xlib/def"
)
type RoundVoteSet struct {
	Prevotes   *agtypes.VoteSet
	Precommits *agtypes.VoteSet
}
type HeightVoteSet struct {
	chainID string
	height  def.INT
	valSet  *agtypes.ValidatorSet
	mtx               sync.Mutex
	round             def.INT                   
	roundVoteSets     map[def.INT]RoundVoteSet  
	peerCatchupRounds map[string][]def.INT      
}
func NewHeightVoteSet(chainID string, height def.INT, valSet *agtypes.ValidatorSet) *HeightVoteSet {
	hvs := &HeightVoteSet{
		chainID: chainID,
	}
	hvs.Reset(height, valSet)
	return hvs
}
func (hvs *HeightVoteSet) Reset(height def.INT, valSet *agtypes.ValidatorSet) {
	hvs.mtx.Lock()
	defer hvs.mtx.Unlock()
	hvs.height = height
	hvs.valSet = valSet
	hvs.roundVoteSets = make(map[def.INT]RoundVoteSet)
	hvs.peerCatchupRounds = make(map[string][]def.INT)
	hvs.addRound(0)
	hvs.round = 0
}
func (hvs *HeightVoteSet) Height() def.INT {
	hvs.mtx.Lock()
	defer hvs.mtx.Unlock()
	return hvs.height
}
func (hvs *HeightVoteSet) Round() def.INT {
	hvs.mtx.Lock()
	defer hvs.mtx.Unlock()
	return hvs.round
}
func (hvs *HeightVoteSet) SetRound(round def.INT) {
	hvs.mtx.Lock()
	defer hvs.mtx.Unlock()
	if hvs.round != 0 && (round < hvs.round+1) {
		PanicSanity("SetRound() must increment hvs.round")
	}
	for r := hvs.round + 1; r <= round; r++ {
		if _, ok := hvs.roundVoteSets[r]; ok {
			continue  
		}
		hvs.addRound(r)
	}
	hvs.round = round
}
func (hvs *HeightVoteSet) addRound(round def.INT) {
	if _, ok := hvs.roundVoteSets[round]; ok {
		PanicSanity("addRound() for an existing round")
	}
	prevotes := agtypes.NewVoteSet(hvs.chainID, hvs.height, round, pbtypes.VoteType_Prevote, hvs.valSet)
	precommits := agtypes.NewVoteSet(hvs.chainID, hvs.height, round, pbtypes.VoteType_Precommit, hvs.valSet)
	hvs.roundVoteSets[round] = RoundVoteSet{
		Prevotes:   prevotes,
		Precommits: precommits,
	}
}
func (hvs *HeightVoteSet) AddVote(vote *pbtypes.Vote, peerKey string) (added bool, err error) {
	hvs.mtx.Lock()
	defer hvs.mtx.Unlock()
	vdata := vote.GetData()
	if !pbtypes.IsVoteTypeValid(vdata.Type) {
		return
	}
	voteSet := hvs.getVoteSet(vdata.Round, vdata.Type)
	if voteSet == nil {
		if rndz := hvs.peerCatchupRounds[peerKey]; len(rndz) < 2 {
			hvs.addRound(vdata.Round)
			voteSet = hvs.getVoteSet(vdata.Round, vdata.Type)
			hvs.peerCatchupRounds[peerKey] = append(rndz, vdata.Round)
		} else {
			return false, errors.Errorf("peer giving votes from unwanted rounds")
		}
	}
	added, err = voteSet.AddVote(vote)
	return
}
func (hvs *HeightVoteSet) Prevotes(round def.INT) *agtypes.VoteSet {
	hvs.mtx.Lock()
	defer hvs.mtx.Unlock()
	return hvs.getVoteSet(round, pbtypes.VoteType_Prevote)
}
func (hvs *HeightVoteSet) Precommits(round def.INT) *agtypes.VoteSet {
	hvs.mtx.Lock()
	defer hvs.mtx.Unlock()
	return hvs.getVoteSet(round, pbtypes.VoteType_Precommit)
}