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.

179 lines
4.8 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. package consensus
  2. import (
  3. "bytes"
  4. "sync"
  5. "time"
  6. . "github.com/tendermint/tendermint/blocks"
  7. . "github.com/tendermint/tendermint/common"
  8. . "github.com/tendermint/tendermint/state"
  9. )
  10. // VoteSet helps collect signatures from validators at each height+round
  11. // for a predefined vote type.
  12. // Note that there three kinds of votes: (bare) votes, precommits, and commits.
  13. // A commit of prior rounds can be added added in lieu of votes/precommits.
  14. // TODO: test majority calculations etc.
  15. type VoteSet struct {
  16. height uint32
  17. round uint16
  18. type_ byte
  19. mtx sync.Mutex
  20. vset *ValidatorSet
  21. votes map[uint64]*Vote
  22. votesBitArray BitArray
  23. votesByBlockHash map[string]uint64
  24. totalVotes uint64
  25. twoThirdsMajority []byte
  26. twoThirdsCommitTime time.Time
  27. }
  28. // Constructs a new VoteSet struct used to accumulate votes for each round.
  29. func NewVoteSet(height uint32, round uint16, type_ byte, vset *ValidatorSet) *VoteSet {
  30. if type_ == VoteTypeCommit && round != 0 {
  31. panic("Expected round 0 for commit vote set")
  32. }
  33. return &VoteSet{
  34. height: height,
  35. round: round,
  36. type_: type_,
  37. vset: vset,
  38. votes: make(map[uint64]*Vote, vset.Size()),
  39. votesBitArray: NewBitArray(vset.Size()),
  40. votesByBlockHash: make(map[string]uint64),
  41. totalVotes: 0,
  42. }
  43. }
  44. // True if added, false if not.
  45. // Returns ErrVote[UnexpectedPhase|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
  46. func (vs *VoteSet) AddVote(vote *Vote) (bool, error) {
  47. vs.mtx.Lock()
  48. defer vs.mtx.Unlock()
  49. // Make sure the phase matches. (or that vote is commit && round < vs.round)
  50. if vote.Height != vs.height ||
  51. (vote.Type != VoteTypeCommit && vote.Round != vs.round) ||
  52. (vote.Type != VoteTypeCommit && vote.Type != vs.type_) ||
  53. (vote.Type == VoteTypeCommit && vs.type_ != VoteTypeCommit && vote.Round >= vs.round) {
  54. return false, ErrVoteUnexpectedPhase
  55. }
  56. // Ensure that signer is a validator.
  57. _, val := vs.vset.GetById(vote.SignerId)
  58. if val == nil {
  59. return false, ErrVoteInvalidAccount
  60. }
  61. // Check signature.
  62. if !val.Verify(vote) {
  63. // Bad signature.
  64. return false, ErrVoteInvalidSignature
  65. }
  66. return vs.addVote(vote)
  67. }
  68. func (vs *VoteSet) addVote(vote *Vote) (bool, error) {
  69. // If vote already exists, return false.
  70. if existingVote, ok := vs.votes[vote.SignerId]; ok {
  71. if bytes.Equal(existingVote.BlockHash, vote.BlockHash) {
  72. return false, nil
  73. } else {
  74. return false, ErrVoteConflictingSignature
  75. }
  76. }
  77. // Add vote.
  78. vs.votes[vote.SignerId] = vote
  79. voterIndex, val := vs.vset.GetById(vote.SignerId)
  80. if val == nil {
  81. return false, ErrVoteInvalidAccount
  82. }
  83. vs.votesBitArray.SetIndex(uint(voterIndex), true)
  84. totalBlockHashVotes := vs.votesByBlockHash[string(vote.BlockHash)] + val.VotingPower
  85. vs.votesByBlockHash[string(vote.BlockHash)] = totalBlockHashVotes
  86. vs.totalVotes += val.VotingPower
  87. // If we just nudged it up to two thirds majority, add it.
  88. if totalBlockHashVotes > vs.vset.TotalVotingPower()*2/3 &&
  89. (totalBlockHashVotes-val.VotingPower) <= vs.vset.TotalVotingPower()*2/3 {
  90. vs.twoThirdsMajority = vote.BlockHash
  91. vs.twoThirdsCommitTime = time.Now()
  92. }
  93. return true, nil
  94. }
  95. // Assumes that commits VoteSet is valid.
  96. func (vs *VoteSet) AddVotesFromCommits(commits *VoteSet) {
  97. commitVotes := commits.AllVotes()
  98. for _, commit := range commitVotes {
  99. if commit.Round < vs.round {
  100. vs.addVote(commit)
  101. }
  102. }
  103. }
  104. func (vs *VoteSet) BitArray() BitArray {
  105. vs.mtx.Lock()
  106. defer vs.mtx.Unlock()
  107. return vs.votesBitArray.Copy()
  108. }
  109. func (vs *VoteSet) GetVote(id uint64) *Vote {
  110. vs.mtx.Lock()
  111. defer vs.mtx.Unlock()
  112. return vs.votes[id]
  113. }
  114. func (vs *VoteSet) AllVotes() []*Vote {
  115. vs.mtx.Lock()
  116. defer vs.mtx.Unlock()
  117. votes := []*Vote{}
  118. for _, vote := range vs.votes {
  119. votes = append(votes, vote)
  120. }
  121. return votes
  122. }
  123. // Returns either a blockhash (or nil) that received +2/3 majority.
  124. // If there exists no such majority, returns (nil, false).
  125. func (vs *VoteSet) TwoThirdsMajority() (hash []byte, commitTime time.Time, ok bool) {
  126. vs.mtx.Lock()
  127. defer vs.mtx.Unlock()
  128. if vs.twoThirdsCommitTime.IsZero() {
  129. return nil, time.Time{}, false
  130. }
  131. return vs.twoThirdsMajority, vs.twoThirdsCommitTime, true
  132. }
  133. func (vs *VoteSet) MakePOL() *POL {
  134. vs.mtx.Lock()
  135. defer vs.mtx.Unlock()
  136. if vs.twoThirdsCommitTime.IsZero() {
  137. return nil
  138. }
  139. majHash := vs.twoThirdsMajority // hash may be nil.
  140. pol := &POL{
  141. Height: vs.height,
  142. Round: vs.round,
  143. BlockHash: majHash,
  144. }
  145. for _, vote := range vs.votes {
  146. if bytes.Equal(vote.BlockHash, majHash) {
  147. if vote.Type == VoteTypeBare {
  148. pol.Votes = append(pol.Votes, vote.Signature)
  149. } else if vote.Type == VoteTypeCommit {
  150. pol.Commits = append(pol.Votes, vote.Signature)
  151. pol.CommitRounds = append(pol.CommitRounds, vote.Round)
  152. } else {
  153. Panicf("Unexpected vote type %X", vote.Type)
  154. }
  155. }
  156. }
  157. return pol
  158. }