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.

177 lines
4.8 KiB

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