github.com/aakash4dev/cometbft@v0.38.2/spec/consensus/signing.md (about)

     1  ---
     2  ---
     3  
     4  # Validator Signing
     5  
     6  Here we specify the rules for validating a proposal and vote before signing.
     7  First we include some general notes on validating data structures common to both types.
     8  We then provide specific validation rules for each. Finally, we include validation rules to prevent double-sigining.
     9  
    10  ## SignedMsgType
    11  
    12  The `SignedMsgType` is a single byte that refers to the type of the message
    13  being signed. It is defined in Go as follows:
    14  
    15  ```go
    16  // SignedMsgType is a type of signed message in the consensus.
    17  type SignedMsgType byte
    18  
    19  const (
    20   // Votes
    21   PrevoteType   SignedMsgType = 0x01
    22   PrecommitType SignedMsgType = 0x02
    23  
    24   // Proposals
    25   ProposalType SignedMsgType = 0x20
    26  )
    27  ```
    28  
    29  All signed messages must correspond to one of these types.
    30  
    31  ## Timestamp
    32  
    33  Timestamp validation is subtle and there are currently no bounds placed on the
    34  timestamp included in a proposal or vote. It is expected that validators will honestly
    35  report their local clock time. The median of all timestamps
    36  included in a commit is used as the timestamp for the next block height.
    37  
    38  Timestamps are expected to be strictly monotonic for a given validator, though
    39  this is not currently enforced.
    40  
    41  ## ChainID
    42  
    43  ChainID is an unstructured string with a max length of 50-bytes.
    44  In the future, the ChainID may become structured, and may take on longer lengths.
    45  For now, it is recommended that signers be configured for a particular ChainID,
    46  and to only sign votes and proposals corresponding to that ChainID.
    47  
    48  ## BlockID
    49  
    50  BlockID is the structure used to represent the block:
    51  
    52  ```go
    53  type BlockID struct {
    54   Hash        []byte
    55   PartsHeader PartSetHeader
    56  }
    57  
    58  type PartSetHeader struct {
    59   Hash  []byte
    60   Total int
    61  }
    62  ```
    63  
    64  To be included in a valid vote or proposal, BlockID must either represent a `nil` block, or a complete one.
    65  We introduce two methods, `BlockID.IsZero()` and `BlockID.IsComplete()` for these cases, respectively.
    66  
    67  `BlockID.IsZero()` returns true for BlockID `b` if each of the following
    68  are true:
    69  
    70  ```go
    71  b.Hash == nil
    72  b.PartsHeader.Total == 0
    73  b.PartsHeader.Hash == nil
    74  ```
    75  
    76  `BlockID.IsComplete()` returns true for BlockID `b` if each of the following
    77  are true:
    78  
    79  ```go
    80  len(b.Hash) == 32
    81  b.PartsHeader.Total > 0
    82  len(b.PartsHeader.Hash) == 32
    83  ```
    84  
    85  ## Proposals
    86  
    87  The structure of a proposal for signing looks like:
    88  
    89  ```go
    90  type CanonicalProposal struct {
    91   Type      SignedMsgType // type alias for byte
    92   Height    int64         `binary:"fixed64"`
    93   Round     int64         `binary:"fixed64"`
    94   POLRound  int64         `binary:"fixed64"`
    95   BlockID   BlockID
    96   Timestamp time.Time
    97   ChainID   string
    98  }
    99  ```
   100  
   101  A proposal is valid if each of the following lines evaluates to true for proposal `p`:
   102  
   103  ```go
   104  p.Type == 0x20
   105  p.Height > 0
   106  p.Round >= 0
   107  p.POLRound >= -1
   108  p.BlockID.IsComplete()
   109  ```
   110  
   111  In other words, a proposal is valid for signing if it contains the type of a Proposal
   112  (0x20), has a positive, non-zero height, a
   113  non-negative round, a POLRound not less than -1, and a complete BlockID.
   114  
   115  ## Votes
   116  
   117  The structure of a vote for signing looks like:
   118  
   119  ```go
   120  type CanonicalVote struct {
   121   Type      SignedMsgType // type alias for byte
   122   Height    int64         `binary:"fixed64"`
   123   Round     int64         `binary:"fixed64"`
   124   BlockID   BlockID
   125   Timestamp time.Time
   126   ChainID   string
   127  }
   128  ```
   129  
   130  A vote is valid if each of the following lines evaluates to true for vote `v`:
   131  
   132  ```go
   133  v.Type == 0x1 || v.Type == 0x2
   134  v.Height > 0
   135  v.Round >= 0
   136  v.BlockID.IsZero() || v.BlockID.IsComplete()
   137  ```
   138  
   139  In other words, a vote is valid for signing if it contains the type of a Prevote
   140  or Precommit (0x1 or 0x2, respectively), has a positive, non-zero height, a
   141  non-negative round, and an empty or valid BlockID.
   142  
   143  ## Invalid Votes and Proposals
   144  
   145  Votes and proposals which do not satisfy the above rules are considered invalid.
   146  Peers gossipping invalid votes and proposals may be disconnected from other peers on the network.
   147  Note, however, that there is not currently any explicit mechanism to punish validators signing votes or proposals that fail
   148  these basic validation rules.
   149  
   150  ## Double Signing
   151  
   152  Signers must be careful not to sign conflicting messages, also known as "double signing" or "equivocating".
   153  CometBFT has mechanisms to publish evidence of validators that signed conflicting votes, so they can be punished
   154  by the application. Note CometBFT does not currently handle evidence of conflciting proposals, though it may in the future.
   155  
   156  ### State
   157  
   158  To prevent such double signing, signers must track the height, round, and type of the last message signed.
   159  Assume the signer keeps the following state, `s`:
   160  
   161  ```go
   162  type LastSigned struct {
   163   Height int64
   164   Round int64
   165   Type SignedMsgType // byte
   166  }
   167  ```
   168  
   169  After signing a vote or proposal `m`, the signer sets:
   170  
   171  ```go
   172  s.Height = m.Height
   173  s.Round = m.Round
   174  s.Type = m.Type
   175  ```
   176  
   177  ### Proposals
   178  
   179  A signer should only sign a proposal `p` if any of the following lines are true:
   180  
   181  ```go
   182  p.Height > s.Height
   183  p.Height == s.Height && p.Round > s.Round
   184  ```
   185  
   186  In other words, a proposal should only be signed if it's at a higher height, or a higher round for the same height.
   187  Once a proposal or vote has been signed for a given height and round, a proposal should never be signed for the same height and round.
   188  
   189  ### Votes
   190  
   191  A signer should only sign a vote `v` if any of the following lines are true:
   192  
   193  ```go
   194  v.Height > s.Height
   195  v.Height == s.Height && v.Round > s.Round
   196  v.Height == s.Height && v.Round == s.Round && v.Step == 0x1 && s.Step == 0x20
   197  v.Height == s.Height && v.Round == s.Round && v.Step == 0x2 && s.Step != 0x2
   198  ```
   199  
   200  In other words, a vote should only be signed if it's:
   201  
   202  - at a higher height
   203  - at a higher round for the same height
   204  - a prevote for the same height and round where we haven't signed a prevote or precommit (but have signed a proposal)
   205  - a precommit for the same height and round where we haven't signed a precommit (but have signed a proposal and/or a prevote)
   206  
   207  This means that once a validator signs a prevote for a given height and round, the only other message it can sign for that height and round is a precommit.
   208  And once a validator signs a precommit for a given height and round, it must not sign any other message for that same height and round.
   209  
   210  Note this includes votes for `nil`, ie. where `BlockID.IsZero()` is true. If a
   211  signer has already signed a vote where `BlockID.IsZero()` is true, it cannot
   212  sign another vote with the same type for the same height and round where
   213  `BlockID.IsComplete()` is true. Thus only a single vote of a particular type
   214  (ie. 0x01 or 0x02) can be signed for the same height and round.
   215  
   216  ### Other Rules
   217  
   218  According to the rules of Tendermint consensus algorithm, adopted in CometBFT, once a validator precommits for
   219  a block, they become "locked" on that block, which means they can't prevote for
   220  another block unless they see sufficient justification (ie. a polka from a
   221  higher round). For more details, see the [consensus
   222  spec](https://arxiv.org/abs/1807.04938).
   223  
   224  Violating this rule is known as "amnesia". In contrast to equivocation,
   225  which is easy to detect, amnesia is difficult to detect without access to votes
   226  from all the validators, as this is what constitutes the justification for
   227  "unlocking". Hence, amnesia is not punished within the protocol, and cannot
   228  easily be prevented by a signer. If enough validators simultaneously commit an
   229  amnesia attack, they may cause a fork of the blockchain, at which point an
   230  off-chain protocol must be engaged to collect votes from all the validators and
   231  determine who misbehaved. For more details, see [fork
   232  detection](https://github.com/tendermint/tendermint/pull/3978).