github.com/fiagdao/tendermint@v0.32.11-0.20220824195748-2087fcc480c1/consensus/types/height_vote_set.go (about) 1 package types 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 "sync" 8 9 "github.com/tendermint/tendermint/p2p" 10 "github.com/tendermint/tendermint/types" 11 ) 12 13 type RoundVoteSet struct { 14 Prevotes *types.VoteSet 15 Precommits *types.VoteSet 16 } 17 18 var ( 19 ErrGotVoteFromUnwantedRound = errors.New( 20 "peer has sent a vote that does not match our round for more than one round", 21 ) 22 ) 23 24 /* 25 Keeps track of all VoteSets from round 0 to round 'round'. 26 27 Also keeps track of up to one RoundVoteSet greater than 28 'round' from each peer, to facilitate catchup syncing of commits. 29 30 A commit is +2/3 precommits for a block at a round, 31 but which round is not known in advance, so when a peer 32 provides a precommit for a round greater than mtx.round, 33 we create a new entry in roundVoteSets but also remember the 34 peer to prevent abuse. 35 We let each peer provide us with up to 2 unexpected "catchup" rounds. 36 One for their LastCommit round, and another for the official commit round. 37 */ 38 type HeightVoteSet struct { 39 chainID string 40 height int64 41 valSet *types.ValidatorSet 42 43 mtx sync.Mutex 44 round int // max tracked round 45 roundVoteSets map[int]RoundVoteSet // keys: [0...round] 46 peerCatchupRounds map[p2p.ID][]int // keys: peer.ID; values: at most 2 rounds 47 } 48 49 func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet { 50 hvs := &HeightVoteSet{ 51 chainID: chainID, 52 } 53 hvs.Reset(height, valSet) 54 return hvs 55 } 56 57 func (hvs *HeightVoteSet) Reset(height int64, valSet *types.ValidatorSet) { 58 hvs.mtx.Lock() 59 defer hvs.mtx.Unlock() 60 61 hvs.height = height 62 hvs.valSet = valSet 63 hvs.roundVoteSets = make(map[int]RoundVoteSet) 64 hvs.peerCatchupRounds = make(map[p2p.ID][]int) 65 66 hvs.addRound(0) 67 hvs.round = 0 68 } 69 70 func (hvs *HeightVoteSet) Height() int64 { 71 hvs.mtx.Lock() 72 defer hvs.mtx.Unlock() 73 return hvs.height 74 } 75 76 func (hvs *HeightVoteSet) Round() int { 77 hvs.mtx.Lock() 78 defer hvs.mtx.Unlock() 79 return hvs.round 80 } 81 82 // Create more RoundVoteSets up to round. 83 func (hvs *HeightVoteSet) SetRound(round int) { 84 hvs.mtx.Lock() 85 defer hvs.mtx.Unlock() 86 if hvs.round != 0 && (round < hvs.round+1) { 87 panic("SetRound() must increment hvs.round") 88 } 89 for r := hvs.round + 1; r <= round; r++ { 90 if _, ok := hvs.roundVoteSets[r]; ok { 91 continue // Already exists because peerCatchupRounds. 92 } 93 hvs.addRound(r) 94 } 95 hvs.round = round 96 } 97 98 func (hvs *HeightVoteSet) addRound(round int) { 99 if _, ok := hvs.roundVoteSets[round]; ok { 100 panic("addRound() for an existing round") 101 } 102 // log.Debug("addRound(round)", "round", round) 103 prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.PrevoteType, hvs.valSet) 104 precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, types.PrecommitType, hvs.valSet) 105 hvs.roundVoteSets[round] = RoundVoteSet{ 106 Prevotes: prevotes, 107 Precommits: precommits, 108 } 109 } 110 111 // Duplicate votes return added=false, err=nil. 112 // By convention, peerID is "" if origin is self. 113 func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerID p2p.ID) (added bool, err error) { 114 hvs.mtx.Lock() 115 defer hvs.mtx.Unlock() 116 if !types.IsVoteTypeValid(vote.Type) { 117 return 118 } 119 voteSet := hvs.getVoteSet(vote.Round, vote.Type) 120 if voteSet == nil { 121 if rndz := hvs.peerCatchupRounds[peerID]; len(rndz) < 2 { 122 hvs.addRound(vote.Round) 123 voteSet = hvs.getVoteSet(vote.Round, vote.Type) 124 hvs.peerCatchupRounds[peerID] = append(rndz, vote.Round) 125 } else { 126 // punish peer 127 err = ErrGotVoteFromUnwantedRound 128 return 129 } 130 } 131 added, err = voteSet.AddVote(vote) 132 return 133 } 134 135 func (hvs *HeightVoteSet) Prevotes(round int) *types.VoteSet { 136 hvs.mtx.Lock() 137 defer hvs.mtx.Unlock() 138 return hvs.getVoteSet(round, types.PrevoteType) 139 } 140 141 func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet { 142 hvs.mtx.Lock() 143 defer hvs.mtx.Unlock() 144 return hvs.getVoteSet(round, types.PrecommitType) 145 } 146 147 // Last round and blockID that has +2/3 prevotes for a particular block or nil. 148 // Returns -1 if no such round exists. 149 func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID) { 150 hvs.mtx.Lock() 151 defer hvs.mtx.Unlock() 152 for r := hvs.round; r >= 0; r-- { 153 rvs := hvs.getVoteSet(r, types.PrevoteType) 154 polBlockID, ok := rvs.TwoThirdsMajority() 155 if ok { 156 return r, polBlockID 157 } 158 } 159 return -1, types.BlockID{} 160 } 161 162 func (hvs *HeightVoteSet) getVoteSet(round int, voteType types.SignedMsgType) *types.VoteSet { 163 rvs, ok := hvs.roundVoteSets[round] 164 if !ok { 165 return nil 166 } 167 switch voteType { 168 case types.PrevoteType: 169 return rvs.Prevotes 170 case types.PrecommitType: 171 return rvs.Precommits 172 default: 173 panic(fmt.Sprintf("Unexpected vote type %X", voteType)) 174 } 175 } 176 177 // If a peer claims that it has 2/3 majority for given blockKey, call this. 178 // NOTE: if there are too many peers, or too much peer churn, 179 // this can cause memory issues. 180 // TODO: implement ability to remove peers too 181 func (hvs *HeightVoteSet) SetPeerMaj23( 182 round int, 183 voteType types.SignedMsgType, 184 peerID p2p.ID, 185 blockID types.BlockID) error { 186 hvs.mtx.Lock() 187 defer hvs.mtx.Unlock() 188 if !types.IsVoteTypeValid(voteType) { 189 return fmt.Errorf("setPeerMaj23: Invalid vote type %X", voteType) 190 } 191 voteSet := hvs.getVoteSet(round, voteType) 192 if voteSet == nil { 193 return nil // something we don't know about yet 194 } 195 return voteSet.SetPeerMaj23(types.P2PID(peerID), blockID) 196 } 197 198 //--------------------------------------------------------- 199 // string and json 200 201 func (hvs *HeightVoteSet) String() string { 202 return hvs.StringIndented("") 203 } 204 205 func (hvs *HeightVoteSet) StringIndented(indent string) string { 206 hvs.mtx.Lock() 207 defer hvs.mtx.Unlock() 208 vsStrings := make([]string, 0, (len(hvs.roundVoteSets)+1)*2) 209 // rounds 0 ~ hvs.round inclusive 210 for round := 0; round <= hvs.round; round++ { 211 voteSetString := hvs.roundVoteSets[round].Prevotes.StringShort() 212 vsStrings = append(vsStrings, voteSetString) 213 voteSetString = hvs.roundVoteSets[round].Precommits.StringShort() 214 vsStrings = append(vsStrings, voteSetString) 215 } 216 // all other peer catchup rounds 217 for round, roundVoteSet := range hvs.roundVoteSets { 218 if round <= hvs.round { 219 continue 220 } 221 voteSetString := roundVoteSet.Prevotes.StringShort() 222 vsStrings = append(vsStrings, voteSetString) 223 voteSetString = roundVoteSet.Precommits.StringShort() 224 vsStrings = append(vsStrings, voteSetString) 225 } 226 return fmt.Sprintf(`HeightVoteSet{H:%v R:0~%v 227 %s %v 228 %s}`, 229 hvs.height, hvs.round, 230 indent, strings.Join(vsStrings, "\n"+indent+" "), 231 indent) 232 } 233 234 func (hvs *HeightVoteSet) MarshalJSON() ([]byte, error) { 235 hvs.mtx.Lock() 236 defer hvs.mtx.Unlock() 237 238 allVotes := hvs.toAllRoundVotes() 239 return cdc.MarshalJSON(allVotes) 240 } 241 242 func (hvs *HeightVoteSet) toAllRoundVotes() []roundVotes { 243 totalRounds := hvs.round + 1 244 allVotes := make([]roundVotes, totalRounds) 245 // rounds 0 ~ hvs.round inclusive 246 for round := 0; round < totalRounds; round++ { 247 allVotes[round] = roundVotes{ 248 Round: round, 249 Prevotes: hvs.roundVoteSets[round].Prevotes.VoteStrings(), 250 PrevotesBitArray: hvs.roundVoteSets[round].Prevotes.BitArrayString(), 251 Precommits: hvs.roundVoteSets[round].Precommits.VoteStrings(), 252 PrecommitsBitArray: hvs.roundVoteSets[round].Precommits.BitArrayString(), 253 } 254 } 255 // TODO: all other peer catchup rounds 256 return allVotes 257 } 258 259 type roundVotes struct { 260 Round int `json:"round"` 261 Prevotes []string `json:"prevotes"` 262 PrevotesBitArray string `json:"prevotes_bit_array"` 263 Precommits []string `json:"precommits"` 264 PrecommitsBitArray string `json:"precommits_bit_array"` 265 }