You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

220 lines
6.0 KiB

9 years ago
9 years ago
  1. package consensus
  2. import (
  3. "strings"
  4. "sync"
  5. . "github.com/tendermint/go-common"
  6. "github.com/tendermint/tendermint/types"
  7. )
  8. type RoundVoteSet struct {
  9. Prevotes *types.VoteSet
  10. Precommits *types.VoteSet
  11. }
  12. /*
  13. Keeps track of all VoteSets from round 0 to round 'round'.
  14. Also keeps track of up to one RoundVoteSet greater than
  15. 'round' from each peer, to facilitate catchup syncing of commits.
  16. A commit is +2/3 precommits for a block at a round,
  17. but which round is not known in advance, so when a peer
  18. provides a precommit for a round greater than mtx.round,
  19. we create a new entry in roundVoteSets but also remember the
  20. peer to prevent abuse.
  21. We let each peer provide us with up to 2 unexpected "catchup" rounds.
  22. One for their LastCommit round, and another for the official commit round.
  23. */
  24. type HeightVoteSet struct {
  25. chainID string
  26. height int
  27. valSet *types.ValidatorSet
  28. mtx sync.Mutex
  29. round int // max tracked round
  30. roundVoteSets map[int]RoundVoteSet // keys: [0...round]
  31. peerCatchupRounds map[string][]int // keys: peer.Key; values: at most 2 rounds
  32. }
  33. func NewHeightVoteSet(chainID string, height int, valSet *types.ValidatorSet) *HeightVoteSet {
  34. hvs := &HeightVoteSet{
  35. chainID: chainID,
  36. }
  37. hvs.Reset(height, valSet)
  38. return hvs
  39. }
  40. func (hvs *HeightVoteSet) Reset(height int, valSet *types.ValidatorSet) {
  41. hvs.mtx.Lock()
  42. defer hvs.mtx.Unlock()
  43. hvs.height = height
  44. hvs.valSet = valSet
  45. hvs.roundVoteSets = make(map[int]RoundVoteSet)
  46. hvs.peerCatchupRounds = make(map[string][]int)
  47. hvs.addRound(0)
  48. hvs.round = 0
  49. }
  50. func (hvs *HeightVoteSet) Height() int {
  51. hvs.mtx.Lock()
  52. defer hvs.mtx.Unlock()
  53. return hvs.height
  54. }
  55. func (hvs *HeightVoteSet) Round() int {
  56. hvs.mtx.Lock()
  57. defer hvs.mtx.Unlock()
  58. return hvs.round
  59. }
  60. // Create more RoundVoteSets up to round.
  61. func (hvs *HeightVoteSet) SetRound(round int) {
  62. hvs.mtx.Lock()
  63. defer hvs.mtx.Unlock()
  64. if hvs.round != 0 && (round < hvs.round+1) {
  65. PanicSanity("SetRound() must increment hvs.round")
  66. }
  67. for r := hvs.round + 1; r <= round; r++ {
  68. if _, ok := hvs.roundVoteSets[r]; ok {
  69. continue // Already exists because peerCatchupRounds.
  70. }
  71. hvs.addRound(r)
  72. }
  73. hvs.round = round
  74. }
  75. func (hvs *HeightVoteSet) addRound(round int) {
  76. if _, ok := hvs.roundVoteSets[round]; ok {
  77. PanicSanity("addRound() for an existing round")
  78. }
  79. log.Debug("addRound(round)", "round", round)
  80. prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrevote, hvs.valSet)
  81. precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrecommit, hvs.valSet)
  82. hvs.roundVoteSets[round] = RoundVoteSet{
  83. Prevotes: prevotes,
  84. Precommits: precommits,
  85. }
  86. }
  87. // Duplicate votes return added=false, err=nil.
  88. // By convention, peerKey is "" if origin is self.
  89. func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerKey string) (added bool, err error) {
  90. hvs.mtx.Lock()
  91. defer hvs.mtx.Unlock()
  92. if !types.IsVoteTypeValid(vote.Type) {
  93. return
  94. }
  95. voteSet := hvs.getVoteSet(vote.Round, vote.Type)
  96. if voteSet == nil {
  97. if rndz := hvs.peerCatchupRounds[peerKey]; len(rndz) < 2 {
  98. hvs.addRound(vote.Round)
  99. voteSet = hvs.getVoteSet(vote.Round, vote.Type)
  100. hvs.peerCatchupRounds[peerKey] = append(rndz, vote.Round)
  101. } else {
  102. // Peer has sent a vote that does not match our round,
  103. // for more than one round. Bad peer!
  104. // TODO punish peer.
  105. log.Warn("Deal with peer giving votes from unwanted rounds")
  106. return
  107. }
  108. }
  109. added, err = voteSet.AddVote(vote)
  110. return
  111. }
  112. func (hvs *HeightVoteSet) Prevotes(round int) *types.VoteSet {
  113. hvs.mtx.Lock()
  114. defer hvs.mtx.Unlock()
  115. return hvs.getVoteSet(round, types.VoteTypePrevote)
  116. }
  117. func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet {
  118. hvs.mtx.Lock()
  119. defer hvs.mtx.Unlock()
  120. return hvs.getVoteSet(round, types.VoteTypePrecommit)
  121. }
  122. // Last round and blockID that has +2/3 prevotes for a particular block or nil.
  123. // Returns -1 if no such round exists.
  124. func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID) {
  125. hvs.mtx.Lock()
  126. defer hvs.mtx.Unlock()
  127. for r := hvs.round; r >= 0; r-- {
  128. rvs := hvs.getVoteSet(r, types.VoteTypePrevote)
  129. polBlockID, ok := rvs.TwoThirdsMajority()
  130. if ok {
  131. return r, polBlockID
  132. }
  133. }
  134. return -1, types.BlockID{}
  135. }
  136. func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {
  137. rvs, ok := hvs.roundVoteSets[round]
  138. if !ok {
  139. return nil
  140. }
  141. switch type_ {
  142. case types.VoteTypePrevote:
  143. return rvs.Prevotes
  144. case types.VoteTypePrecommit:
  145. return rvs.Precommits
  146. default:
  147. PanicSanity(Fmt("Unexpected vote type %X", type_))
  148. return nil
  149. }
  150. }
  151. func (hvs *HeightVoteSet) String() string {
  152. return hvs.StringIndented("")
  153. }
  154. func (hvs *HeightVoteSet) StringIndented(indent string) string {
  155. hvs.mtx.Lock()
  156. defer hvs.mtx.Unlock()
  157. vsStrings := make([]string, 0, (len(hvs.roundVoteSets)+1)*2)
  158. // rounds 0 ~ hvs.round inclusive
  159. for round := 0; round <= hvs.round; round++ {
  160. voteSetString := hvs.roundVoteSets[round].Prevotes.StringShort()
  161. vsStrings = append(vsStrings, voteSetString)
  162. voteSetString = hvs.roundVoteSets[round].Precommits.StringShort()
  163. vsStrings = append(vsStrings, voteSetString)
  164. }
  165. // all other peer catchup rounds
  166. for round, roundVoteSet := range hvs.roundVoteSets {
  167. if round <= hvs.round {
  168. continue
  169. }
  170. voteSetString := roundVoteSet.Prevotes.StringShort()
  171. vsStrings = append(vsStrings, voteSetString)
  172. voteSetString = roundVoteSet.Precommits.StringShort()
  173. vsStrings = append(vsStrings, voteSetString)
  174. }
  175. return Fmt(`HeightVoteSet{H:%v R:0~%v
  176. %s %v
  177. %s}`,
  178. hvs.height, hvs.round,
  179. indent, strings.Join(vsStrings, "\n"+indent+" "),
  180. indent)
  181. }
  182. // If a peer claims that it has 2/3 majority for given blockKey, call this.
  183. // NOTE: if there are too many peers, or too much peer churn,
  184. // this can cause memory issues.
  185. // TODO: implement ability to remove peers too
  186. func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID string, blockID types.BlockID) {
  187. hvs.mtx.Lock()
  188. defer hvs.mtx.Unlock()
  189. if !types.IsVoteTypeValid(type_) {
  190. return
  191. }
  192. voteSet := hvs.getVoteSet(round, type_)
  193. if voteSet == nil {
  194. return
  195. }
  196. voteSet.SetPeerMaj23(peerID, blockID)
  197. }