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  }