github.com/Finschia/ostracon@v1.1.5/types/vote.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "time" 8 9 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 10 11 "github.com/Finschia/ostracon/crypto" 12 "github.com/Finschia/ostracon/crypto/ed25519" 13 tmbytes "github.com/Finschia/ostracon/libs/bytes" 14 "github.com/Finschia/ostracon/libs/protoio" 15 ) 16 17 const ( 18 nilVoteStr string = "nil-Vote" 19 ) 20 21 var ( 22 ErrVoteUnexpectedStep = errors.New("unexpected step") 23 ErrVoteInvalidValidatorIndex = errors.New("invalid validator index") 24 ErrVoteInvalidValidatorAddress = errors.New("invalid validator address") 25 ErrVoteInvalidSignature = errors.New("invalid signature") 26 ErrVoteInvalidBlockHash = errors.New("invalid block hash") 27 ErrVoteNonDeterministicSignature = errors.New("non-deterministic signature") 28 ErrVoteNil = errors.New("nil vote") 29 ) 30 31 type ErrVoteConflictingVotes struct { 32 VoteA *Vote 33 VoteB *Vote 34 } 35 36 func (err *ErrVoteConflictingVotes) Error() string { 37 return fmt.Sprintf("conflicting votes from validator %X", err.VoteA.ValidatorAddress) 38 } 39 40 func NewConflictingVoteError(vote1, vote2 *Vote) *ErrVoteConflictingVotes { 41 return &ErrVoteConflictingVotes{ 42 VoteA: vote1, 43 VoteB: vote2, 44 } 45 } 46 47 // Address is hex bytes. 48 type Address = crypto.Address 49 50 // Vote represents a prevote, precommit, or commit vote from validators for 51 // consensus. 52 type Vote struct { 53 Type tmproto.SignedMsgType `json:"type"` 54 Height int64 `json:"height"` 55 Round int32 `json:"round"` // assume there will not be greater than 2_147_483_647 rounds 56 BlockID BlockID `json:"block_id"` // zero if vote is nil. 57 Timestamp time.Time `json:"timestamp"` 58 ValidatorAddress Address `json:"validator_address"` 59 ValidatorIndex int32 `json:"validator_index"` 60 Signature []byte `json:"signature"` 61 } 62 63 const MaxVoteBytes int64 = (1 + 1) + // Type 64 (1 + 9) + // Height 65 (1 + 5) + // Round 66 (1 + 76 + 1) + // BlockID 67 (1 + 17 + 1) + // Timestamp 68 (1 + 20 + 1) + // ValidatorAddress 69 (1 + 5) + // ValidatorIndex 70 (1 + ed25519.SignatureSize + 1) // Signature 71 72 // CommitSig converts the Vote to a CommitSig. 73 func (vote *Vote) CommitSig() CommitSig { 74 if vote == nil { 75 return NewCommitSigAbsent() 76 } 77 78 var blockIDFlag BlockIDFlag 79 switch { 80 case vote.BlockID.IsComplete(): 81 blockIDFlag = BlockIDFlagCommit 82 case vote.BlockID.IsZero(): 83 blockIDFlag = BlockIDFlagNil 84 default: 85 panic(fmt.Sprintf("Invalid vote %v - expected BlockID to be either empty or complete", vote)) 86 } 87 88 return CommitSig{ 89 BlockIDFlag: blockIDFlag, 90 ValidatorAddress: vote.ValidatorAddress, 91 Timestamp: vote.Timestamp, 92 Signature: vote.Signature, 93 } 94 } 95 96 // VoteSignBytes returns the proto-encoding of the canonicalized Vote, for 97 // signing. Panics is the marshaling fails. 98 // 99 // The encoded Protobuf message is varint length-prefixed (using MarshalDelimited) 100 // for backwards-compatibility with the Amino encoding, due to e.g. hardware 101 // devices that rely on this encoding. 102 // 103 // See CanonicalizeVote 104 func VoteSignBytes(chainID string, vote *tmproto.Vote) []byte { 105 pb := CanonicalizeVote(chainID, vote) 106 bz, err := protoio.MarshalDelimited(&pb) 107 if err != nil { 108 panic(err) 109 } 110 111 return bz 112 } 113 114 func (vote *Vote) Copy() *Vote { 115 voteCopy := *vote 116 return &voteCopy 117 } 118 119 // String returns a string representation of Vote. 120 // 121 // 1. validator index 122 // 2. first 6 bytes of validator address 123 // 3. height 124 // 4. round, 125 // 5. type byte 126 // 6. type string 127 // 7. first 6 bytes of block hash 128 // 8. first 6 bytes of signature 129 // 9. timestamp 130 func (vote *Vote) String() string { 131 if vote == nil { 132 return nilVoteStr 133 } 134 135 var typeString string 136 switch vote.Type { 137 case tmproto.PrevoteType: 138 typeString = "Prevote" 139 case tmproto.PrecommitType: 140 typeString = "Precommit" 141 default: 142 panic("Unknown vote type") 143 } 144 145 return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}", 146 vote.ValidatorIndex, 147 tmbytes.Fingerprint(vote.ValidatorAddress), 148 vote.Height, 149 vote.Round, 150 vote.Type, 151 typeString, 152 tmbytes.Fingerprint(vote.BlockID.Hash), 153 tmbytes.Fingerprint(vote.Signature), 154 CanonicalTime(vote.Timestamp), 155 ) 156 } 157 158 func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error { 159 if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) { 160 return ErrVoteInvalidValidatorAddress 161 } 162 v := vote.ToProto() 163 if !pubKey.VerifySignature(VoteSignBytes(chainID, v), vote.Signature) { 164 return ErrVoteInvalidSignature 165 } 166 return nil 167 } 168 169 // ValidateBasic performs basic validation. 170 func (vote *Vote) ValidateBasic() error { 171 if !IsVoteTypeValid(vote.Type) { 172 return errors.New("invalid Type") 173 } 174 175 if vote.Height < 0 { 176 return errors.New("negative Height") 177 } 178 179 if vote.Round < 0 { 180 return errors.New("negative Round") 181 } 182 183 // NOTE: Timestamp validation is subtle and handled elsewhere. 184 185 if err := vote.BlockID.ValidateBasic(); err != nil { 186 return fmt.Errorf("wrong BlockID: %v", err) 187 } 188 189 // BlockID.ValidateBasic would not err if we for instance have an empty hash but a 190 // non-empty PartsSetHeader: 191 if !vote.BlockID.IsZero() && !vote.BlockID.IsComplete() { 192 return fmt.Errorf("blockID must be either empty or complete, got: %v", vote.BlockID) 193 } 194 195 if len(vote.ValidatorAddress) != crypto.AddressSize { 196 return fmt.Errorf("expected ValidatorAddress size to be %d bytes, got %d bytes", 197 crypto.AddressSize, 198 len(vote.ValidatorAddress), 199 ) 200 } 201 if vote.ValidatorIndex < 0 { 202 return errors.New("negative ValidatorIndex") 203 } 204 if len(vote.Signature) == 0 { 205 return errors.New("signature is missing") 206 } 207 208 if len(vote.Signature) > MaxSignatureSize { 209 return fmt.Errorf("signature is too big %d (max: %d)", len(vote.Signature), MaxSignatureSize) 210 } 211 212 return nil 213 } 214 215 // ToProto converts the handwritten type to proto generated type 216 // return type, nil if everything converts safely, otherwise nil, error 217 func (vote *Vote) ToProto() *tmproto.Vote { 218 if vote == nil { 219 return nil 220 } 221 222 return &tmproto.Vote{ 223 Type: vote.Type, 224 Height: vote.Height, 225 Round: vote.Round, 226 BlockID: vote.BlockID.ToProto(), 227 Timestamp: vote.Timestamp, 228 ValidatorAddress: vote.ValidatorAddress, 229 ValidatorIndex: vote.ValidatorIndex, 230 Signature: vote.Signature, 231 } 232 } 233 234 // FromProto converts a proto generetad type to a handwritten type 235 // return type, nil if everything converts safely, otherwise nil, error 236 func VoteFromProto(pv *tmproto.Vote) (*Vote, error) { 237 if pv == nil { 238 return nil, errors.New("nil vote") 239 } 240 241 blockID, err := BlockIDFromProto(&pv.BlockID) 242 if err != nil { 243 return nil, err 244 } 245 246 vote := new(Vote) 247 vote.Type = pv.Type 248 vote.Height = pv.Height 249 vote.Round = pv.Round 250 vote.BlockID = *blockID 251 vote.Timestamp = pv.Timestamp 252 vote.ValidatorAddress = pv.ValidatorAddress 253 vote.ValidatorIndex = pv.ValidatorIndex 254 vote.Signature = pv.Signature 255 256 return vote, vote.ValidateBasic() 257 }