github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/consensus/types/round_state.go (about) 1 package types 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "time" 7 8 "github.com/badrootd/nibiru-cometbft/libs/bytes" 9 "github.com/badrootd/nibiru-cometbft/types" 10 ) 11 12 //----------------------------------------------------------------------------- 13 // RoundStepType enum type 14 15 // RoundStepType enumerates the state of the consensus state machine 16 type RoundStepType uint8 // These must be numeric, ordered. 17 18 // RoundStepType 19 const ( 20 RoundStepNewHeight = RoundStepType(0x01) // Wait til CommitTime + timeoutCommit 21 RoundStepNewRound = RoundStepType(0x02) // Setup new round and go to RoundStepPropose 22 RoundStepPropose = RoundStepType(0x03) // Did propose, gossip proposal 23 RoundStepPrevote = RoundStepType(0x04) // Did prevote, gossip prevotes 24 RoundStepPrevoteWait = RoundStepType(0x05) // Did receive any +2/3 prevotes, start timeout 25 RoundStepPrecommit = RoundStepType(0x06) // Did precommit, gossip precommits 26 RoundStepPrecommitWait = RoundStepType(0x07) // Did receive any +2/3 precommits, start timeout 27 RoundStepCommit = RoundStepType(0x08) // Entered commit state machine 28 // NOTE: RoundStepNewHeight acts as RoundStepCommitWait. 29 30 // NOTE: Update IsValid method if you change this! 31 ) 32 33 // IsValid returns true if the step is valid, false if unknown/undefined. 34 func (rs RoundStepType) IsValid() bool { 35 return uint8(rs) >= 0x01 && uint8(rs) <= 0x08 36 } 37 38 // String returns a string 39 func (rs RoundStepType) String() string { 40 switch rs { 41 case RoundStepNewHeight: 42 return "RoundStepNewHeight" 43 case RoundStepNewRound: 44 return "RoundStepNewRound" 45 case RoundStepPropose: 46 return "RoundStepPropose" 47 case RoundStepPrevote: 48 return "RoundStepPrevote" 49 case RoundStepPrevoteWait: 50 return "RoundStepPrevoteWait" 51 case RoundStepPrecommit: 52 return "RoundStepPrecommit" 53 case RoundStepPrecommitWait: 54 return "RoundStepPrecommitWait" 55 case RoundStepCommit: 56 return "RoundStepCommit" 57 default: 58 return "RoundStepUnknown" // Cannot panic. 59 } 60 } 61 62 //----------------------------------------------------------------------------- 63 64 // RoundState defines the internal consensus state. 65 // NOTE: Not thread safe. Should only be manipulated by functions downstream 66 // of the cs.receiveRoutine 67 type RoundState struct { 68 Height int64 `json:"height"` // Height we are working on 69 Round int32 `json:"round"` 70 Step RoundStepType `json:"step"` 71 StartTime time.Time `json:"start_time"` 72 73 // Subjective time when +2/3 precommits for Block at Round were found 74 CommitTime time.Time `json:"commit_time"` 75 Validators *types.ValidatorSet `json:"validators"` 76 Proposal *types.Proposal `json:"proposal"` 77 ProposalBlock *types.Block `json:"proposal_block"` 78 ProposalBlockParts *types.PartSet `json:"proposal_block_parts"` 79 LockedRound int32 `json:"locked_round"` 80 LockedBlock *types.Block `json:"locked_block"` 81 LockedBlockParts *types.PartSet `json:"locked_block_parts"` 82 83 // The variables below starting with "Valid..." derive their name from 84 // the algorithm presented in this paper: 85 // [The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938). 86 // Therefore, "Valid...": 87 // * means that the block or round that the variable refers to has 88 // received 2/3+ non-`nil` prevotes (a.k.a. a *polka*) 89 // * has nothing to do with whether the Application returned "Accept" in its 90 // response to `ProcessProposal`, or "Reject" 91 92 // Last known round with POL for non-nil valid block. 93 ValidRound int32 `json:"valid_round"` 94 ValidBlock *types.Block `json:"valid_block"` // Last known block of POL mentioned above. 95 96 // Last known block parts of POL mentioned above. 97 ValidBlockParts *types.PartSet `json:"valid_block_parts"` 98 Votes *HeightVoteSet `json:"votes"` 99 CommitRound int32 `json:"commit_round"` // 100 LastCommit *types.VoteSet `json:"last_commit"` // Last precommits at Height-1 101 LastValidators *types.ValidatorSet `json:"last_validators"` 102 TriggeredTimeoutPrecommit bool `json:"triggered_timeout_precommit"` 103 } 104 105 // Compressed version of the RoundState for use in RPC 106 type RoundStateSimple struct { 107 HeightRoundStep string `json:"height/round/step"` 108 StartTime time.Time `json:"start_time"` 109 ProposalBlockHash bytes.HexBytes `json:"proposal_block_hash"` 110 LockedBlockHash bytes.HexBytes `json:"locked_block_hash"` 111 ValidBlockHash bytes.HexBytes `json:"valid_block_hash"` 112 Votes json.RawMessage `json:"height_vote_set"` 113 Proposer types.ValidatorInfo `json:"proposer"` 114 } 115 116 // Compress the RoundState to RoundStateSimple 117 func (rs *RoundState) RoundStateSimple() RoundStateSimple { 118 votesJSON, err := rs.Votes.MarshalJSON() 119 if err != nil { 120 panic(err) 121 } 122 123 addr := rs.Validators.GetProposer().Address 124 idx, _ := rs.Validators.GetByAddress(addr) 125 126 return RoundStateSimple{ 127 HeightRoundStep: fmt.Sprintf("%d/%d/%d", rs.Height, rs.Round, rs.Step), 128 StartTime: rs.StartTime, 129 ProposalBlockHash: rs.ProposalBlock.Hash(), 130 LockedBlockHash: rs.LockedBlock.Hash(), 131 ValidBlockHash: rs.ValidBlock.Hash(), 132 Votes: votesJSON, 133 Proposer: types.ValidatorInfo{ 134 Address: addr, 135 Index: idx, 136 }, 137 } 138 } 139 140 // NewRoundEvent returns the RoundState with proposer information as an event. 141 func (rs *RoundState) NewRoundEvent() types.EventDataNewRound { 142 addr := rs.Validators.GetProposer().Address 143 idx, _ := rs.Validators.GetByAddress(addr) 144 145 return types.EventDataNewRound{ 146 Height: rs.Height, 147 Round: rs.Round, 148 Step: rs.Step.String(), 149 Proposer: types.ValidatorInfo{ 150 Address: addr, 151 Index: idx, 152 }, 153 } 154 } 155 156 // CompleteProposalEvent returns information about a proposed block as an event. 157 func (rs *RoundState) CompleteProposalEvent() types.EventDataCompleteProposal { 158 // We must construct BlockID from ProposalBlock and ProposalBlockParts 159 // cs.Proposal is not guaranteed to be set when this function is called 160 blockID := types.BlockID{ 161 Hash: rs.ProposalBlock.Hash(), 162 PartSetHeader: rs.ProposalBlockParts.Header(), 163 } 164 165 return types.EventDataCompleteProposal{ 166 Height: rs.Height, 167 Round: rs.Round, 168 Step: rs.Step.String(), 169 BlockID: blockID, 170 } 171 } 172 173 // RoundStateEvent returns the H/R/S of the RoundState as an event. 174 func (rs *RoundState) RoundStateEvent() types.EventDataRoundState { 175 return types.EventDataRoundState{ 176 Height: rs.Height, 177 Round: rs.Round, 178 Step: rs.Step.String(), 179 } 180 } 181 182 // String returns a string 183 func (rs *RoundState) String() string { 184 return rs.StringIndented("") 185 } 186 187 // StringIndented returns a string 188 func (rs *RoundState) StringIndented(indent string) string { 189 return fmt.Sprintf(`RoundState{ 190 %s H:%v R:%v S:%v 191 %s StartTime: %v 192 %s CommitTime: %v 193 %s Validators: %v 194 %s Proposal: %v 195 %s ProposalBlock: %v %v 196 %s LockedRound: %v 197 %s LockedBlock: %v %v 198 %s ValidRound: %v 199 %s ValidBlock: %v %v 200 %s Votes: %v 201 %s LastCommit: %v 202 %s LastValidators:%v 203 %s}`, 204 indent, rs.Height, rs.Round, rs.Step, 205 indent, rs.StartTime, 206 indent, rs.CommitTime, 207 indent, rs.Validators.StringIndented(indent+" "), 208 indent, rs.Proposal, 209 indent, rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort(), 210 indent, rs.LockedRound, 211 indent, rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort(), 212 indent, rs.ValidRound, 213 indent, rs.ValidBlockParts.StringShort(), rs.ValidBlock.StringShort(), 214 indent, rs.Votes.StringIndented(indent+" "), 215 indent, rs.LastCommit.StringShort(), 216 indent, rs.LastValidators.StringIndented(indent+" "), 217 indent) 218 } 219 220 // StringShort returns a string 221 func (rs *RoundState) StringShort() string { 222 return fmt.Sprintf(`RoundState{H:%v R:%v S:%v ST:%v}`, 223 rs.Height, rs.Round, rs.Step, rs.StartTime) 224 }