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.

182 lines
5.0 KiB

10 years ago
10 years ago
  1. package consensus
  2. import (
  3. "strings"
  4. "sync"
  5. . "github.com/tendermint/tendermint/common"
  6. sm "github.com/tendermint/tendermint/state"
  7. "github.com/tendermint/tendermint/types"
  8. )
  9. type RoundVoteSet struct {
  10. Prevotes *VoteSet
  11. Precommits *VoteSet
  12. }
  13. /*
  14. Keeps track of all VoteSets from round 0 to round 'round'.
  15. Also keeps track of up to one RoundVoteSet greater than
  16. 'round' from each peer, to facilitate catchup syncing of commits.
  17. A commit is +2/3 precommits for a block at a round,
  18. but which round is not known in advance, so when a peer
  19. provides a precommit for a round greater than mtx.round,
  20. we create a new entry in roundVoteSets but also remember the
  21. peer to prevent abuse.
  22. */
  23. type HeightVoteSet struct {
  24. height int
  25. valSet *sm.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(height int, valSet *sm.ValidatorSet) *HeightVoteSet {
  32. hvs := &HeightVoteSet{
  33. height: height,
  34. valSet: valSet,
  35. roundVoteSets: make(map[int]RoundVoteSet),
  36. peerCatchupRounds: make(map[string]int),
  37. }
  38. hvs.addRound(0)
  39. hvs.round = 0
  40. return hvs
  41. }
  42. func (hvs *HeightVoteSet) Height() int {
  43. return hvs.height
  44. }
  45. func (hvs *HeightVoteSet) Round() int {
  46. hvs.mtx.Lock()
  47. defer hvs.mtx.Unlock()
  48. return hvs.round
  49. }
  50. // Create more RoundVoteSets up to round.
  51. func (hvs *HeightVoteSet) SetRound(round int) {
  52. hvs.mtx.Lock()
  53. defer hvs.mtx.Unlock()
  54. if hvs.round != 0 && (round < hvs.round+1) {
  55. panic("SetRound() must increment hvs.round")
  56. }
  57. for r := hvs.round + 1; r <= round; r++ {
  58. if _, ok := hvs.roundVoteSets[r]; ok {
  59. continue // Already exists because peerCatchupRounds.
  60. }
  61. hvs.addRound(r)
  62. }
  63. hvs.round = round
  64. }
  65. func (hvs *HeightVoteSet) addRound(round int) {
  66. if _, ok := hvs.roundVoteSets[round]; ok {
  67. panic("addRound() for an existing round")
  68. }
  69. log.Debug("addRound(round)", "round", round)
  70. prevotes := NewVoteSet(hvs.height, round, types.VoteTypePrevote, hvs.valSet)
  71. precommits := NewVoteSet(hvs.height, round, types.VoteTypePrecommit, hvs.valSet)
  72. hvs.roundVoteSets[round] = RoundVoteSet{
  73. Prevotes: prevotes,
  74. Precommits: precommits,
  75. }
  76. }
  77. // Duplicate votes return added=false, err=nil.
  78. // By convention, peerKey is "" if origin is self.
  79. func (hvs *HeightVoteSet) AddByAddress(address []byte, vote *types.Vote, peerKey string) (added bool, index int, err error) {
  80. hvs.mtx.Lock()
  81. defer hvs.mtx.Unlock()
  82. voteSet := hvs.getVoteSet(vote.Round, vote.Type)
  83. if voteSet == nil {
  84. if _, ok := hvs.peerCatchupRounds[peerKey]; !ok {
  85. hvs.addRound(vote.Round)
  86. hvs.peerCatchupRounds[peerKey] = vote.Round
  87. } else {
  88. // Peer has sent a vote that does not match our round,
  89. // for more than one round. Bad peer!
  90. // TODO punish peer.
  91. log.Warn("Deal with peer giving votes from unwanted rounds")
  92. }
  93. return
  94. }
  95. added, index, err = voteSet.AddByAddress(address, vote)
  96. return
  97. }
  98. func (hvs *HeightVoteSet) Prevotes(round int) *VoteSet {
  99. hvs.mtx.Lock()
  100. defer hvs.mtx.Unlock()
  101. return hvs.getVoteSet(round, types.VoteTypePrevote)
  102. }
  103. func (hvs *HeightVoteSet) Precommits(round int) *VoteSet {
  104. hvs.mtx.Lock()
  105. defer hvs.mtx.Unlock()
  106. return hvs.getVoteSet(round, types.VoteTypePrecommit)
  107. }
  108. // Last round that has +2/3 prevotes for a particular block or nik.
  109. // Returns -1 if no such round exists.
  110. func (hvs *HeightVoteSet) POLRound() int {
  111. hvs.mtx.Lock()
  112. defer hvs.mtx.Unlock()
  113. for r := hvs.round; r >= 0; r-- {
  114. if hvs.getVoteSet(r, types.VoteTypePrevote).HasTwoThirdsMajority() {
  115. return r
  116. }
  117. }
  118. return -1
  119. }
  120. func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *VoteSet {
  121. log.Debug("getVoteSet(round)", "round", round, "type", type_)
  122. rvs, ok := hvs.roundVoteSets[round]
  123. if !ok {
  124. return nil
  125. }
  126. switch type_ {
  127. case types.VoteTypePrevote:
  128. return rvs.Prevotes
  129. case types.VoteTypePrecommit:
  130. return rvs.Precommits
  131. default:
  132. panic(Fmt("Unexpected vote type %X", type_))
  133. }
  134. }
  135. func (hvs *HeightVoteSet) String() string {
  136. return hvs.StringIndented("")
  137. }
  138. func (hvs *HeightVoteSet) StringIndented(indent string) string {
  139. vsStrings := make([]string, 0, (len(hvs.roundVoteSets)+1)*2)
  140. // rounds 0 ~ hvs.round inclusive
  141. for round := 0; round <= hvs.round; round++ {
  142. voteSetString := hvs.roundVoteSets[round].Prevotes.StringShort()
  143. vsStrings = append(vsStrings, voteSetString)
  144. voteSetString = hvs.roundVoteSets[round].Precommits.StringShort()
  145. vsStrings = append(vsStrings, voteSetString)
  146. }
  147. // all other peer catchup rounds
  148. for round, roundVoteSet := range hvs.roundVoteSets {
  149. if round <= hvs.round {
  150. continue
  151. }
  152. voteSetString := roundVoteSet.Prevotes.StringShort()
  153. vsStrings = append(vsStrings, voteSetString)
  154. voteSetString = roundVoteSet.Precommits.StringShort()
  155. vsStrings = append(vsStrings, voteSetString)
  156. }
  157. return Fmt(`HeightVoteSet{H:%v R:0~%v
  158. %s %v
  159. %s}`,
  160. hvs.height, hvs.round,
  161. indent, strings.Join(vsStrings, "\n"+indent+" "),
  162. indent)
  163. }