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.

181 lines
4.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. height int
  24. valSet *types.ValidatorSet
  25. mtx sync.Mutex
  26. round int // max tracked round
  27. roundVoteSets map[int]RoundVoteSet // keys: [0...round]
  28. peerCatchupRounds map[string]int // keys: peer.Key; values: round
  29. }
  30. func NewHeightVoteSet(height int, valSet *types.ValidatorSet) *HeightVoteSet {
  31. hvs := &HeightVoteSet{
  32. height: height,
  33. valSet: valSet,
  34. roundVoteSets: make(map[int]RoundVoteSet),
  35. peerCatchupRounds: make(map[string]int),
  36. }
  37. hvs.addRound(0)
  38. hvs.round = 0
  39. return hvs
  40. }
  41. func (hvs *HeightVoteSet) Height() int {
  42. return hvs.height
  43. }
  44. func (hvs *HeightVoteSet) Round() int {
  45. hvs.mtx.Lock()
  46. defer hvs.mtx.Unlock()
  47. return hvs.round
  48. }
  49. // Create more RoundVoteSets up to round.
  50. func (hvs *HeightVoteSet) SetRound(round int) {
  51. hvs.mtx.Lock()
  52. defer hvs.mtx.Unlock()
  53. if hvs.round != 0 && (round < hvs.round+1) {
  54. PanicSanity("SetRound() must increment hvs.round")
  55. }
  56. for r := hvs.round + 1; r <= round; r++ {
  57. if _, ok := hvs.roundVoteSets[r]; ok {
  58. continue // Already exists because peerCatchupRounds.
  59. }
  60. hvs.addRound(r)
  61. }
  62. hvs.round = round
  63. }
  64. func (hvs *HeightVoteSet) addRound(round int) {
  65. if _, ok := hvs.roundVoteSets[round]; ok {
  66. PanicSanity("addRound() for an existing round")
  67. }
  68. log.Debug("addRound(round)", "round", round)
  69. prevotes := types.NewVoteSet(hvs.height, round, types.VoteTypePrevote, hvs.valSet)
  70. precommits := types.NewVoteSet(hvs.height, round, types.VoteTypePrecommit, hvs.valSet)
  71. hvs.roundVoteSets[round] = RoundVoteSet{
  72. Prevotes: prevotes,
  73. Precommits: precommits,
  74. }
  75. }
  76. // Duplicate votes return added=false, err=nil.
  77. // By convention, peerKey is "" if origin is self.
  78. func (hvs *HeightVoteSet) AddByIndex(valIndex int, vote *types.Vote, peerKey string) (added bool, address []byte, err error) {
  79. hvs.mtx.Lock()
  80. defer hvs.mtx.Unlock()
  81. voteSet := hvs.getVoteSet(vote.Round, vote.Type)
  82. if voteSet == nil {
  83. if _, ok := hvs.peerCatchupRounds[peerKey]; !ok {
  84. hvs.addRound(vote.Round)
  85. hvs.peerCatchupRounds[peerKey] = vote.Round
  86. } else {
  87. // Peer has sent a vote that does not match our round,
  88. // for more than one round. Bad peer!
  89. // TODO punish peer.
  90. log.Warn("Deal with peer giving votes from unwanted rounds")
  91. }
  92. return
  93. }
  94. added, address, err = voteSet.AddByIndex(valIndex, vote)
  95. return
  96. }
  97. func (hvs *HeightVoteSet) Prevotes(round int) *types.VoteSet {
  98. hvs.mtx.Lock()
  99. defer hvs.mtx.Unlock()
  100. return hvs.getVoteSet(round, types.VoteTypePrevote)
  101. }
  102. func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet {
  103. hvs.mtx.Lock()
  104. defer hvs.mtx.Unlock()
  105. return hvs.getVoteSet(round, types.VoteTypePrecommit)
  106. }
  107. // Last round that has +2/3 prevotes for a particular block or nil.
  108. // Returns -1 if no such round exists.
  109. func (hvs *HeightVoteSet) POLRound() int {
  110. hvs.mtx.Lock()
  111. defer hvs.mtx.Unlock()
  112. for r := hvs.round; r >= 0; r-- {
  113. if hvs.getVoteSet(r, types.VoteTypePrevote).HasTwoThirdsMajority() {
  114. return r
  115. }
  116. }
  117. return -1
  118. }
  119. func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {
  120. rvs, ok := hvs.roundVoteSets[round]
  121. if !ok {
  122. return nil
  123. }
  124. switch type_ {
  125. case types.VoteTypePrevote:
  126. return rvs.Prevotes
  127. case types.VoteTypePrecommit:
  128. return rvs.Precommits
  129. default:
  130. PanicSanity(Fmt("Unexpected vote type %X", type_))
  131. return nil
  132. }
  133. }
  134. func (hvs *HeightVoteSet) String() string {
  135. return hvs.StringIndented("")
  136. }
  137. func (hvs *HeightVoteSet) StringIndented(indent string) string {
  138. vsStrings := make([]string, 0, (len(hvs.roundVoteSets)+1)*2)
  139. // rounds 0 ~ hvs.round inclusive
  140. for round := 0; round <= hvs.round; round++ {
  141. voteSetString := hvs.roundVoteSets[round].Prevotes.StringShort()
  142. vsStrings = append(vsStrings, voteSetString)
  143. voteSetString = hvs.roundVoteSets[round].Precommits.StringShort()
  144. vsStrings = append(vsStrings, voteSetString)
  145. }
  146. // all other peer catchup rounds
  147. for round, roundVoteSet := range hvs.roundVoteSets {
  148. if round <= hvs.round {
  149. continue
  150. }
  151. voteSetString := roundVoteSet.Prevotes.StringShort()
  152. vsStrings = append(vsStrings, voteSetString)
  153. voteSetString = roundVoteSet.Precommits.StringShort()
  154. vsStrings = append(vsStrings, voteSetString)
  155. }
  156. return Fmt(`HeightVoteSet{H:%v R:0~%v
  157. %s %v
  158. %s}`,
  159. hvs.height, hvs.round,
  160. indent, strings.Join(vsStrings, "\n"+indent+" "),
  161. indent)
  162. }