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.

204 lines
6.2 KiB

  1. package consensus
  2. import (
  3. "sync"
  4. "time"
  5. . "github.com/tendermint/tendermint/blocks"
  6. . "github.com/tendermint/tendermint/common"
  7. . "github.com/tendermint/tendermint/state"
  8. )
  9. var (
  10. consensusStateKey = []byte("consensusState")
  11. )
  12. // Tracks consensus state across block heights and rounds.
  13. type ConsensusState struct {
  14. mtx sync.Mutex
  15. height uint32 // Height we are working on.
  16. validatorsR0 *ValidatorSet // A copy of the validators at round 0
  17. lockedProposal *BlockPartSet // A BlockPartSet of the locked proposal.
  18. startTime time.Time // Start of round 0 for this height.
  19. commits *VoteSet // Commits for this height.
  20. roundState *RoundState // The RoundState object for the current round.
  21. commitTime time.Time // Time at which a block was found to be committed by +2/3.
  22. }
  23. func NewConsensusState(state *State) *ConsensusState {
  24. cs := &ConsensusState{}
  25. cs.Update(state)
  26. return cs
  27. }
  28. func (cs *ConsensusState) LockProposal(blockPartSet *BlockPartSet) {
  29. cs.mtx.Lock()
  30. defer cs.mtx.Unlock()
  31. cs.lockedProposal = blockPartSet
  32. }
  33. func (cs *ConsensusState) UnlockProposal() {
  34. cs.mtx.Lock()
  35. defer cs.mtx.Unlock()
  36. cs.lockedProposal = nil
  37. }
  38. func (cs *ConsensusState) LockedProposal() *BlockPartSet {
  39. cs.mtx.Lock()
  40. defer cs.mtx.Unlock()
  41. return cs.lockedProposal
  42. }
  43. func (cs *ConsensusState) RoundState() *RoundState {
  44. cs.mtx.Lock()
  45. defer cs.mtx.Unlock()
  46. return cs.roundState
  47. }
  48. // Primarily gets called upon block commit by ConsensusManager.
  49. func (cs *ConsensusState) Update(state *State) {
  50. cs.mtx.Lock()
  51. defer cs.mtx.Unlock()
  52. // Sanity check state.
  53. stateHeight := state.Height()
  54. if stateHeight > 0 && stateHeight != cs.height+1 {
  55. Panicf("Update() expected state height of %v but found %v", cs.height+1, stateHeight)
  56. }
  57. // Reset fields based on state.
  58. cs.height = stateHeight
  59. cs.validatorsR0 = state.Validators().Copy() // NOTE: immutable.
  60. cs.lockedProposal = nil
  61. cs.startTime = state.CommitTime().Add(newBlockWaitDuration) // NOTE: likely future time.
  62. cs.commits = NewVoteSet(stateHeight, 0, VoteTypeCommit, cs.validatorsR0)
  63. // Setup the roundState
  64. cs.roundState = nil
  65. cs.setupRound(0)
  66. }
  67. // If cs.roundState isn't at round, set up new roundState at round.
  68. func (cs *ConsensusState) SetupRound(round uint16) {
  69. cs.mtx.Lock()
  70. defer cs.mtx.Unlock()
  71. if cs.roundState != nil && cs.roundState.Round >= round {
  72. return
  73. }
  74. cs.setupRound(round)
  75. }
  76. // Initialize roundState for given round.
  77. // Involves incrementing validators for each past rand.
  78. func (cs *ConsensusState) setupRound(round uint16) {
  79. // Increment validator accums as necessary.
  80. // We need to start with cs.validatorsR0 or cs.roundState.Validators
  81. var validators *ValidatorSet
  82. var validatorsRound uint16
  83. if cs.roundState == nil {
  84. // We have no roundState so we start from validatorsR0 at round 0.
  85. validators = cs.validatorsR0.Copy()
  86. validatorsRound = 0
  87. } else {
  88. // We have a previous roundState so we start from that.
  89. validators = cs.roundState.Validators.Copy()
  90. validatorsRound = cs.roundState.Round
  91. }
  92. // Increment all the way to round.
  93. for r := validatorsRound; r < round; r++ {
  94. validators.IncrementAccum()
  95. }
  96. roundState := NewRoundState(cs.height, round, cs.startTime, validators, cs.commits)
  97. cs.roundState = roundState
  98. }
  99. //-----------------------------------------------------------------------------
  100. const (
  101. RoundStepStart = uint8(0x00) // Round started.
  102. RoundStepProposal = uint8(0x01) // Did propose, broadcasting proposal.
  103. RoundStepBareVotes = uint8(0x02) // Did vote bare, broadcasting bare votes.
  104. RoundStepPrecommits = uint8(0x03) // Did precommit, broadcasting precommits.
  105. RoundStepCommitOrUnlock = uint8(0x04) // We committed at this round -- do not progress to the next round.
  106. )
  107. //-----------------------------------------------------------------------------
  108. // RoundState encapsulates all the state needed to engage in the consensus protocol.
  109. type RoundState struct {
  110. Height uint32 // Immutable
  111. Round uint16 // Immutable
  112. StartTime time.Time // Time in which consensus started for this height.
  113. Expires time.Time // Time after which this round is expired.
  114. Proposer *Validator // The proposer to propose a block for this round.
  115. Validators *ValidatorSet // All validators with modified accumPower for this round.
  116. Proposal *BlockPartSet // All block parts received for this round.
  117. RoundBareVotes *VoteSet // All votes received for this round.
  118. RoundPrecommits *VoteSet // All precommits received for this round.
  119. Commits *VoteSet // A shared object for all commit votes of this height.
  120. mtx sync.Mutex
  121. step uint8 // mutable
  122. }
  123. func NewRoundState(height uint32, round uint16, startTime time.Time,
  124. validators *ValidatorSet, commits *VoteSet) *RoundState {
  125. proposer := validators.GetProposer()
  126. blockPartSet := NewBlockPartSet(height, nil)
  127. roundBareVotes := NewVoteSet(height, round, VoteTypeBare, validators)
  128. roundPrecommits := NewVoteSet(height, round, VoteTypePrecommit, validators)
  129. rs := &RoundState{
  130. Height: height,
  131. Round: round,
  132. StartTime: startTime,
  133. Expires: calcRoundStartTime(round+1, startTime),
  134. Proposer: proposer,
  135. Validators: validators,
  136. Proposal: blockPartSet,
  137. RoundBareVotes: roundBareVotes,
  138. RoundPrecommits: roundPrecommits,
  139. Commits: commits,
  140. step: RoundStepStart,
  141. }
  142. return rs
  143. }
  144. // "source" is typically the Peer.Key of the peer that gave us this vote.
  145. func (rs *RoundState) AddVote(vote *Vote, source string) (added bool, rank uint8, err error) {
  146. switch vote.Type {
  147. case VoteTypeBare:
  148. return rs.RoundBareVotes.AddVote(vote, source)
  149. case VoteTypePrecommit:
  150. return rs.RoundPrecommits.AddVote(vote, source)
  151. case VoteTypeCommit:
  152. return rs.Commits.AddVote(vote, source)
  153. default:
  154. panic("Unknown vote type")
  155. }
  156. }
  157. func (rs *RoundState) Expired() bool {
  158. return time.Now().After(rs.Expires)
  159. }
  160. func (rs *RoundState) Step() uint8 {
  161. rs.mtx.Lock()
  162. defer rs.mtx.Unlock()
  163. return rs.step
  164. }
  165. func (rs *RoundState) SetStep(step uint8) bool {
  166. rs.mtx.Lock()
  167. defer rs.mtx.Unlock()
  168. if rs.step < step {
  169. rs.step = step
  170. return true
  171. } else {
  172. return false
  173. }
  174. }