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.

218 lines
5.9 KiB

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