github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/types/vote.go (about) 1 package types 2 3 import ( 4 "errors" 5 "fmt" 6 "time" 7 8 "github.com/gnolang/gno/tm2/pkg/amino" 9 "github.com/gnolang/gno/tm2/pkg/crypto" 10 ) 11 12 const ( 13 // MaxVoteBytes is a maximum vote size (including amino overhead). 14 MaxVoteBytes int = 247 15 nilVoteStr string = "nil-Vote" 16 ) 17 18 var ( 19 ErrVoteUnexpectedStep = errors.New("unexpected step") 20 ErrVoteInvalidValidatorIndex = errors.New("invalid validator index") 21 ErrVoteInvalidValidatorAddress = errors.New("invalid validator address") 22 ErrVoteInvalidSignature = errors.New("invalid signature") 23 ErrVoteNonDeterministicSignature = errors.New("non-deterministic signature") 24 ErrVoteNil = errors.New("nil vote") 25 ) 26 27 type VoteConflictingVotesError struct { 28 *DuplicateVoteEvidence 29 } 30 31 func (err *VoteConflictingVotesError) Error() string { 32 return fmt.Sprintf("Conflicting votes from validator %v", err.PubKey.Address()) 33 } 34 35 func NewConflictingVoteError(val *Validator, voteA, voteB *Vote) *VoteConflictingVotesError { 36 return &VoteConflictingVotesError{ 37 &DuplicateVoteEvidence{ 38 PubKey: val.PubKey, 39 VoteA: voteA, 40 VoteB: voteB, 41 }, 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 SignedMsgType `json:"type"` 52 Height int64 `json:"height"` 53 Round int `json:"round"` 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 int `json:"validator_index"` 58 Signature []byte `json:"signature"` 59 } 60 61 // CommitSig converts the Vote to a CommitSig. 62 // If the Vote is nil, the CommitSig will be nil. 63 func (vote *Vote) CommitSig() *CommitSig { 64 if vote == nil { 65 return nil 66 } 67 cs := CommitSig(*vote) 68 return &cs 69 } 70 71 func (vote *Vote) SignBytes(chainID string) []byte { 72 bz, err := amino.MarshalSized(CanonicalizeVote(chainID, vote)) 73 if err != nil { 74 panic(err) 75 } 76 return bz 77 } 78 79 func (vote *Vote) Copy() *Vote { 80 voteCopy := *vote 81 return &voteCopy 82 } 83 84 func (vote *Vote) String() string { 85 if vote == nil { 86 return nilVoteStr 87 } 88 var typeString string 89 switch vote.Type { 90 case PrevoteType: 91 typeString = "Prevote" 92 case PrecommitType: 93 typeString = "Precommit" 94 default: 95 panic("Unknown vote type") 96 } 97 98 return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}", 99 vote.ValidatorIndex, 100 fingerprint(vote.ValidatorAddress[:]), 101 vote.Height, 102 vote.Round, 103 vote.Type, 104 typeString, 105 fingerprint(vote.BlockID.Hash), 106 fingerprint(vote.Signature), 107 CanonicalTime(vote.Timestamp), 108 ) 109 } 110 111 func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error { 112 if pubKey.Address() != vote.ValidatorAddress { 113 return ErrVoteInvalidValidatorAddress 114 } 115 116 if !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) { 117 return ErrVoteInvalidSignature 118 } 119 return nil 120 } 121 122 // ValidateBasic performs basic validation. 123 func (vote *Vote) ValidateBasic() error { 124 if !IsVoteTypeValid(vote.Type) { 125 return errors.New("invalid Type") 126 } 127 if vote.Height < 0 { 128 return errors.New("negative Height") 129 } 130 if vote.Round < 0 { 131 return errors.New("negative Round") 132 } 133 134 // NOTE: Timestamp validation is subtle and handled elsewhere. 135 136 if err := vote.BlockID.ValidateBasic(); err != nil { 137 return fmt.Errorf("wrong BlockID: %w", err) 138 } 139 // BlockID.ValidateBasic would not err if we for instance have an empty hash but a 140 // non-empty PartsSetHeader: 141 if !vote.BlockID.IsZero() && !vote.BlockID.IsComplete() { 142 return fmt.Errorf("BlockID must be either empty or complete, got: %v", vote.BlockID) 143 } 144 if len(vote.ValidatorAddress) != crypto.AddressSize { 145 return fmt.Errorf("expected ValidatorAddress size to be %d bytes, got %d bytes", 146 crypto.AddressSize, 147 len(vote.ValidatorAddress), 148 ) 149 } 150 if vote.ValidatorAddress.IsZero() { 151 return fmt.Errorf("expected ValidatorAddress to be non-zero") 152 } 153 if vote.ValidatorIndex < 0 { 154 return errors.New("negative ValidatorIndex") 155 } 156 if len(vote.Signature) == 0 { 157 return errors.New("signature is missing") 158 } 159 if len(vote.Signature) > MaxSignatureSize { 160 return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize) 161 } 162 return nil 163 }