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.

146 lines
4.0 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. // Keeps track of VoteSets for all the rounds of a height.
  14. // We add the commit votes to all the affected rounds,
  15. // and for new rounds carry over the commit set. Commits have
  16. // an associated round, so the performance hit won't be O(rounds).
  17. type HeightVoteSet struct {
  18. height uint
  19. valSet *sm.ValidatorSet
  20. mtx sync.Mutex
  21. round uint // max tracked round
  22. roundVoteSets map[uint]RoundVoteSet // keys: [0...round]
  23. commits *VoteSet // all commits for height
  24. }
  25. func NewHeightVoteSet(height uint, valSet *sm.ValidatorSet) *HeightVoteSet {
  26. hvs := &HeightVoteSet{
  27. height: height,
  28. valSet: valSet,
  29. roundVoteSets: make(map[uint]RoundVoteSet),
  30. commits: NewVoteSet(height, 0, types.VoteTypeCommit, valSet),
  31. }
  32. hvs.SetRound(0)
  33. return hvs
  34. }
  35. func (hvs *HeightVoteSet) Height() uint {
  36. return hvs.height
  37. }
  38. func (hvs *HeightVoteSet) Round() uint {
  39. hvs.mtx.Lock()
  40. defer hvs.mtx.Unlock()
  41. return hvs.round
  42. }
  43. // Create more RoundVoteSets up to round with all commits carried over.
  44. func (hvs *HeightVoteSet) SetRound(round uint) {
  45. hvs.mtx.Lock()
  46. defer hvs.mtx.Unlock()
  47. if hvs.round != 0 && (round < hvs.round+1) {
  48. panic("SetRound() must increment hvs.round")
  49. }
  50. for r := hvs.round + 1; r <= round; r++ {
  51. prevotes := NewVoteSet(hvs.height, r, types.VoteTypePrevote, hvs.valSet)
  52. prevotes.AddFromCommits(hvs.commits)
  53. precommits := NewVoteSet(hvs.height, r, types.VoteTypePrecommit, hvs.valSet)
  54. precommits.AddFromCommits(hvs.commits)
  55. hvs.roundVoteSets[r] = RoundVoteSet{
  56. Prevotes: prevotes,
  57. Precommits: precommits,
  58. }
  59. }
  60. hvs.round = round
  61. }
  62. // CONTRACT: if err == nil, added == true
  63. func (hvs *HeightVoteSet) AddByAddress(address []byte, vote *types.Vote) (added bool, index uint, err error) {
  64. hvs.mtx.Lock()
  65. defer hvs.mtx.Unlock()
  66. voteSet := hvs.getVoteSet(vote.Round, vote.Type)
  67. if voteSet == nil {
  68. return
  69. }
  70. added, index, err = voteSet.AddByAddress(address, vote)
  71. if err != nil {
  72. return
  73. }
  74. // If vote is commit, also add to all prevote/precommit for future rounds.
  75. if vote.Type == types.VoteTypeCommit {
  76. for round := vote.Round + 1; round <= hvs.round; round++ {
  77. voteSet := hvs.getVoteSet(round, types.VoteTypePrevote)
  78. _, _, err = voteSet.AddByAddress(address, vote)
  79. if err != nil {
  80. // TODO slash for prevote after commit
  81. log.Warn("Prevote after commit", "address", address, "vote", vote)
  82. }
  83. voteSet = hvs.getVoteSet(round, types.VoteTypePrecommit)
  84. _, _, err = voteSet.AddByAddress(address, vote)
  85. if err != nil {
  86. // TODO slash for prevote after commit
  87. log.Warn("Prevote after commit", "address", address, "vote", vote)
  88. }
  89. }
  90. }
  91. return
  92. }
  93. func (hvs *HeightVoteSet) GetVoteSet(round uint, type_ byte) *VoteSet {
  94. hvs.mtx.Lock()
  95. defer hvs.mtx.Unlock()
  96. return hvs.getVoteSet(round, type_)
  97. }
  98. func (hvs *HeightVoteSet) getVoteSet(round uint, type_ byte) *VoteSet {
  99. if type_ == types.VoteTypeCommit {
  100. return hvs.commits
  101. }
  102. rvs, ok := hvs.roundVoteSets[round]
  103. if !ok {
  104. return nil
  105. }
  106. switch type_ {
  107. case types.VoteTypePrevote:
  108. return rvs.Prevotes
  109. case types.VoteTypePrecommit:
  110. return rvs.Precommits
  111. default:
  112. panic(Fmt("Unexpected vote type %X", type_))
  113. }
  114. }
  115. func (hvs *HeightVoteSet) String() string {
  116. return hvs.StringIndented("")
  117. }
  118. func (hvs *HeightVoteSet) StringIndented(indent string) string {
  119. vsStrings := make([]string, 0, hvs.round*2+1)
  120. vsStrings = append(vsStrings, hvs.commits.StringShort())
  121. for round := uint(0); round <= hvs.round; round++ {
  122. voteSetString := hvs.roundVoteSets[round].Prevotes.StringShort()
  123. vsStrings = append(vsStrings, voteSetString)
  124. voteSetString = hvs.roundVoteSets[round].Precommits.StringShort()
  125. vsStrings = append(vsStrings, voteSetString)
  126. }
  127. return Fmt(`HeightVoteSet{H:%v R:0~%v
  128. %s %v
  129. %s}`,
  130. indent, strings.Join(vsStrings, "\n"+indent+" "),
  131. indent)
  132. }