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.

265 lines
7.4 KiB

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