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.

270 lines
7.6 KiB

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