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  }