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