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