github.com/DFWallet/tendermint-cosmos@v0.0.2/types/vote.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "time" 8 9 "github.com/DFWallet/tendermint-cosmos/crypto" 10 tmbytes "github.com/DFWallet/tendermint-cosmos/libs/bytes" 11 "github.com/DFWallet/tendermint-cosmos/libs/protoio" 12 tmproto "github.com/DFWallet/tendermint-cosmos/proto/tendermint/types" 13 ) 14 15 const ( 16 nilVoteStr string = "nil-Vote" 17 ) 18 19 var ( 20 ErrVoteUnexpectedStep = errors.New("unexpected step") 21 ErrVoteInvalidValidatorIndex = errors.New("invalid validator index") 22 ErrVoteInvalidValidatorAddress = errors.New("invalid validator address") 23 ErrVoteInvalidSignature = errors.New("invalid signature") 24 ErrVoteInvalidBlockHash = errors.New("invalid block hash") 25 ErrVoteNonDeterministicSignature = errors.New("non-deterministic signature") 26 ErrVoteNil = errors.New("nil vote") 27 ) 28 29 type ErrVoteConflictingVotes struct { 30 VoteA *Vote 31 VoteB *Vote 32 } 33 34 func (err *ErrVoteConflictingVotes) Error() string { 35 return fmt.Sprintf("conflicting votes from validator %X", err.VoteA.ValidatorAddress) 36 } 37 38 func NewConflictingVoteError(vote1, vote2 *Vote) *ErrVoteConflictingVotes { 39 return &ErrVoteConflictingVotes{ 40 VoteA: vote1, 41 VoteB: 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. Panics is the marshaling fails. 87 // 88 // The encoded Protobuf message is varint length-prefixed (using MarshalDelimited) 89 // for backwards-compatibility with the Amino encoding, due to e.g. hardware 90 // devices that rely on this encoding. 91 // 92 // See CanonicalizeVote 93 func VoteSignBytes(chainID string, vote *tmproto.Vote) []byte { 94 pb := CanonicalizeVote(chainID, vote) 95 bz, err := protoio.MarshalDelimited(&pb) 96 if err != nil { 97 panic(err) 98 } 99 100 return bz 101 } 102 103 func (vote *Vote) Copy() *Vote { 104 voteCopy := *vote 105 return &voteCopy 106 } 107 108 // String returns a string representation of Vote. 109 // 110 // 1. validator index 111 // 2. first 6 bytes of validator address 112 // 3. height 113 // 4. round, 114 // 5. type byte 115 // 6. type string 116 // 7. first 6 bytes of block hash 117 // 8. first 6 bytes of signature 118 // 9. timestamp 119 func (vote *Vote) String() string { 120 if vote == nil { 121 return nilVoteStr 122 } 123 124 var typeString string 125 switch vote.Type { 126 case tmproto.PrevoteType: 127 typeString = "Prevote" 128 case tmproto.PrecommitType: 129 typeString = "Precommit" 130 default: 131 panic("Unknown vote type") 132 } 133 134 return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}", 135 vote.ValidatorIndex, 136 tmbytes.Fingerprint(vote.ValidatorAddress), 137 vote.Height, 138 vote.Round, 139 vote.Type, 140 typeString, 141 tmbytes.Fingerprint(vote.BlockID.Hash), 142 tmbytes.Fingerprint(vote.Signature), 143 CanonicalTime(vote.Timestamp), 144 ) 145 } 146 147 func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error { 148 if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) { 149 return ErrVoteInvalidValidatorAddress 150 } 151 v := vote.ToProto() 152 if !pubKey.VerifySignature(VoteSignBytes(chainID, v), vote.Signature) { 153 return ErrVoteInvalidSignature 154 } 155 return nil 156 } 157 158 // ValidateBasic performs basic validation. 159 func (vote *Vote) ValidateBasic() error { 160 if !IsVoteTypeValid(vote.Type) { 161 return errors.New("invalid Type") 162 } 163 164 if vote.Height < 0 { 165 return errors.New("negative Height") 166 } 167 168 if vote.Round < 0 { 169 return errors.New("negative Round") 170 } 171 172 // NOTE: Timestamp validation is subtle and handled elsewhere. 173 174 if err := vote.BlockID.ValidateBasic(); err != nil { 175 return fmt.Errorf("wrong BlockID: %v", err) 176 } 177 178 // BlockID.ValidateBasic would not err if we for instance have an empty hash but a 179 // non-empty PartsSetHeader: 180 if !vote.BlockID.IsZero() && !vote.BlockID.IsComplete() { 181 return fmt.Errorf("blockID must be either empty or complete, got: %v", vote.BlockID) 182 } 183 184 if len(vote.ValidatorAddress) != crypto.AddressSize { 185 return fmt.Errorf("expected ValidatorAddress size to be %d bytes, got %d bytes", 186 crypto.AddressSize, 187 len(vote.ValidatorAddress), 188 ) 189 } 190 if vote.ValidatorIndex < 0 { 191 return errors.New("negative ValidatorIndex") 192 } 193 if len(vote.Signature) == 0 { 194 return errors.New("signature is missing") 195 } 196 197 if len(vote.Signature) > MaxSignatureSize { 198 return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize) 199 } 200 201 return nil 202 } 203 204 // ToProto converts the handwritten type to proto generated type 205 // return type, nil if everything converts safely, otherwise nil, error 206 func (vote *Vote) ToProto() *tmproto.Vote { 207 if vote == nil { 208 return nil 209 } 210 211 return &tmproto.Vote{ 212 Type: vote.Type, 213 Height: vote.Height, 214 Round: vote.Round, 215 BlockID: vote.BlockID.ToProto(), 216 Timestamp: vote.Timestamp, 217 ValidatorAddress: vote.ValidatorAddress, 218 ValidatorIndex: vote.ValidatorIndex, 219 Signature: vote.Signature, 220 } 221 } 222 223 // FromProto converts a proto generetad type to a handwritten type 224 // return type, nil if everything converts safely, otherwise nil, error 225 func VoteFromProto(pv *tmproto.Vote) (*Vote, error) { 226 if pv == nil { 227 return nil, errors.New("nil vote") 228 } 229 230 blockID, err := BlockIDFromProto(&pv.BlockID) 231 if err != nil { 232 return nil, err 233 } 234 235 vote := new(Vote) 236 vote.Type = pv.Type 237 vote.Height = pv.Height 238 vote.Round = pv.Round 239 vote.BlockID = *blockID 240 vote.Timestamp = pv.Timestamp 241 vote.ValidatorAddress = pv.ValidatorAddress 242 vote.ValidatorIndex = pv.ValidatorIndex 243 vote.Signature = pv.Signature 244 245 return vote, vote.ValidateBasic() 246 }