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