package types import ( "encoding/json" "fmt" "time" "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/types" ) //----------------------------------------------------------------------------- // RoundStepType enum type // RoundStepType enumerates the state of the consensus state machine type RoundStepType uint8 // These must be numeric, ordered. // RoundStepType const ( RoundStepNewHeight = RoundStepType(0x01) // Wait til CommitTime + timeoutCommit RoundStepNewRound = RoundStepType(0x02) // Setup new round and go to RoundStepPropose RoundStepPropose = RoundStepType(0x03) // Did propose, gossip proposal RoundStepPrevote = RoundStepType(0x04) // Did prevote, gossip prevotes RoundStepPrevoteWait = RoundStepType(0x05) // Did receive any +2/3 prevotes, start timeout RoundStepPrecommit = RoundStepType(0x06) // Did precommit, gossip precommits RoundStepPrecommitWait = RoundStepType(0x07) // Did receive any +2/3 precommits, start timeout RoundStepCommit = RoundStepType(0x08) // Entered commit state machine // NOTE: RoundStepNewHeight acts as RoundStepCommitWait. // NOTE: Update IsValid method if you change this! ) // IsValid returns true if the step is valid, false if unknown/undefined. func (rs RoundStepType) IsValid() bool { return uint8(rs) >= 0x01 && uint8(rs) <= 0x08 } // String returns a string func (rs RoundStepType) String() string { switch rs { case RoundStepNewHeight: return "RoundStepNewHeight" case RoundStepNewRound: return "RoundStepNewRound" case RoundStepPropose: return "RoundStepPropose" case RoundStepPrevote: return "RoundStepPrevote" case RoundStepPrevoteWait: return "RoundStepPrevoteWait" case RoundStepPrecommit: return "RoundStepPrecommit" case RoundStepPrecommitWait: return "RoundStepPrecommitWait" case RoundStepCommit: return "RoundStepCommit" default: return "RoundStepUnknown" // Cannot panic. } } //----------------------------------------------------------------------------- // RoundState defines the internal consensus state. // NOTE: Not thread safe. Should only be manipulated by functions downstream // of the cs.receiveRoutine type RoundState struct { Height int64 `json:"height,string"` // Height we are working on Round int32 `json:"round"` Step RoundStepType `json:"step"` StartTime time.Time `json:"start_time"` // Subjective time when +2/3 precommits for Block at Round were found CommitTime time.Time `json:"commit_time"` Validators *types.ValidatorSet `json:"validators"` Proposal *types.Proposal `json:"proposal"` ProposalBlock *types.Block `json:"proposal_block"` ProposalBlockParts *types.PartSet `json:"proposal_block_parts"` LockedRound int32 `json:"locked_round"` LockedBlock *types.Block `json:"locked_block"` LockedBlockParts *types.PartSet `json:"locked_block_parts"` // Last known round with POL for non-nil valid block. ValidRound int32 `json:"valid_round"` ValidBlock *types.Block `json:"valid_block"` // Last known block of POL mentioned above. // Last known block parts of POL mentioned above. ValidBlockParts *types.PartSet `json:"valid_block_parts"` Votes *HeightVoteSet `json:"votes"` CommitRound int32 `json:"commit_round"` // LastCommit *types.VoteSet `json:"last_commit"` // Last precommits at Height-1 LastValidators *types.ValidatorSet `json:"last_validators"` TriggeredTimeoutPrecommit bool `json:"triggered_timeout_precommit"` } // Compressed version of the RoundState for use in RPC type RoundStateSimple struct { HeightRoundStep string `json:"height/round/step"` StartTime time.Time `json:"start_time"` ProposalBlockHash bytes.HexBytes `json:"proposal_block_hash"` LockedBlockHash bytes.HexBytes `json:"locked_block_hash"` ValidBlockHash bytes.HexBytes `json:"valid_block_hash"` Votes json.RawMessage `json:"height_vote_set"` Proposer types.ValidatorInfo `json:"proposer"` } // Compress the RoundState to RoundStateSimple func (rs *RoundState) RoundStateSimple() RoundStateSimple { votesJSON, err := rs.Votes.MarshalJSON() if err != nil { panic(err) } addr := rs.Validators.GetProposer().Address idx, _ := rs.Validators.GetByAddress(addr) return RoundStateSimple{ HeightRoundStep: fmt.Sprintf("%d/%d/%d", rs.Height, rs.Round, rs.Step), StartTime: rs.StartTime, ProposalBlockHash: rs.ProposalBlock.Hash(), LockedBlockHash: rs.LockedBlock.Hash(), ValidBlockHash: rs.ValidBlock.Hash(), Votes: votesJSON, Proposer: types.ValidatorInfo{ Address: addr, Index: idx, }, } } // NewRoundEvent returns the RoundState with proposer information as an event. func (rs *RoundState) NewRoundEvent() types.EventDataNewRound { addr := rs.Validators.GetProposer().Address idx, _ := rs.Validators.GetByAddress(addr) return types.EventDataNewRound{ Height: rs.Height, Round: rs.Round, Step: rs.Step.String(), Proposer: types.ValidatorInfo{ Address: addr, Index: idx, }, } } // CompleteProposalEvent returns information about a proposed block as an event. func (rs *RoundState) CompleteProposalEvent() types.EventDataCompleteProposal { // We must construct BlockID from ProposalBlock and ProposalBlockParts // cs.Proposal is not guaranteed to be set when this function is called blockID := types.BlockID{ Hash: rs.ProposalBlock.Hash(), PartSetHeader: rs.ProposalBlockParts.Header(), } return types.EventDataCompleteProposal{ Height: rs.Height, Round: rs.Round, Step: rs.Step.String(), BlockID: blockID, } } // RoundStateEvent returns the H/R/S of the RoundState as an event. func (rs *RoundState) RoundStateEvent() types.EventDataRoundState { return types.EventDataRoundState{ Height: rs.Height, Round: rs.Round, Step: rs.Step.String(), } } // String returns a string func (rs *RoundState) String() string { return rs.StringIndented("") } // StringIndented returns a string func (rs *RoundState) StringIndented(indent string) string { return fmt.Sprintf(`RoundState{ %s H:%v R:%v S:%v %s StartTime: %v %s CommitTime: %v %s Validators: %v %s Proposal: %v %s ProposalBlock: %v %v %s LockedRound: %v %s LockedBlock: %v %v %s ValidRound: %v %s ValidBlock: %v %v %s Votes: %v %s LastCommit: %v %s LastValidators:%v %s}`, indent, rs.Height, rs.Round, rs.Step, indent, rs.StartTime, indent, rs.CommitTime, indent, rs.Validators.StringIndented(indent+" "), indent, rs.Proposal, indent, rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort(), indent, rs.LockedRound, indent, rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort(), indent, rs.ValidRound, indent, rs.ValidBlockParts.StringShort(), rs.ValidBlock.StringShort(), indent, rs.Votes.StringIndented(indent+" "), indent, rs.LastCommit.StringShort(), indent, rs.LastValidators.StringIndented(indent+" "), indent) } // StringShort returns a string func (rs *RoundState) StringShort() string { return fmt.Sprintf(`RoundState{H:%v R:%v S:%v ST:%v}`, rs.Height, rs.Round, rs.Step, rs.StartTime) }