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.

267 lines
7.6 KiB

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