github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/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  	tmproto "github.com/tendermint/tendermint/proto/types"
    12  )
    13  
    14  const (
    15  	// MaxVoteBytes is a maximum vote size (including amino overhead).
    16  	MaxVoteBytes int64  = 223
    17  	nilVoteStr   string = "nil-Vote"
    18  )
    19  
    20  var (
    21  	ErrVoteUnexpectedStep            = errors.New("unexpected step")
    22  	ErrVoteInvalidValidatorIndex     = errors.New("invalid validator index")
    23  	ErrVoteInvalidValidatorAddress   = errors.New("invalid validator address")
    24  	ErrVoteInvalidSignature          = errors.New("invalid signature")
    25  	ErrVoteInvalidBlockHash          = errors.New("invalid block hash")
    26  	ErrVoteNonDeterministicSignature = errors.New("non-deterministic signature")
    27  	ErrVoteNil                       = errors.New("nil vote")
    28  )
    29  
    30  type ErrVoteConflictingVotes struct {
    31  	*DuplicateVoteEvidence
    32  }
    33  
    34  func (err *ErrVoteConflictingVotes) Error() string {
    35  	return fmt.Sprintf("Conflicting votes from validator %v", err.PubKey.Address())
    36  }
    37  
    38  func NewConflictingVoteError(val *Validator, vote1, vote2 *Vote) *ErrVoteConflictingVotes {
    39  	return &ErrVoteConflictingVotes{
    40  		&DuplicateVoteEvidence{
    41  			PubKey: val.PubKey,
    42  			VoteA:  vote1,
    43  			VoteB:  vote2,
    44  		},
    45  	}
    46  }
    47  
    48  // Address is hex bytes.
    49  type Address = crypto.Address
    50  
    51  // Vote represents a prevote, precommit, or commit vote from validators for
    52  // consensus.
    53  type Vote struct {
    54  	Type             SignedMsgType `json:"type"`
    55  	Height           int64         `json:"height"`
    56  	Round            int           `json:"round"`
    57  	BlockID          BlockID       `json:"block_id"` // zero if vote is nil.
    58  	Timestamp        time.Time     `json:"timestamp"`
    59  	ValidatorAddress Address       `json:"validator_address"`
    60  	ValidatorIndex   int           `json:"validator_index"`
    61  	Signature        []byte        `json:"signature"`
    62  }
    63  
    64  // CommitSig converts the Vote to a CommitSig.
    65  // If the Vote is nil, the CommitSig will be nil.
    66  func (vote *Vote) CommitSig() *CommitSig {
    67  	if vote == nil {
    68  		return nil
    69  	}
    70  	cs := CommitSig(*vote)
    71  	return &cs
    72  }
    73  
    74  func (vote *Vote) SignBytes(chainID string) []byte {
    75  	bz, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeVote(chainID, vote))
    76  	if err != nil {
    77  		panic(err)
    78  	}
    79  	return bz
    80  }
    81  
    82  func (vote *Vote) Copy() *Vote {
    83  	voteCopy := *vote
    84  	return &voteCopy
    85  }
    86  
    87  func (vote *Vote) String() string {
    88  	if vote == nil {
    89  		return nilVoteStr
    90  	}
    91  
    92  	var typeString string
    93  	switch vote.Type {
    94  	case PrevoteType:
    95  		typeString = "Prevote"
    96  	case PrecommitType:
    97  		typeString = "Precommit"
    98  	default:
    99  		panic("Unknown vote type")
   100  	}
   101  
   102  	return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}",
   103  		vote.ValidatorIndex,
   104  		tmbytes.Fingerprint(vote.ValidatorAddress),
   105  		vote.Height,
   106  		vote.Round,
   107  		vote.Type,
   108  		typeString,
   109  		tmbytes.Fingerprint(vote.BlockID.Hash),
   110  		tmbytes.Fingerprint(vote.Signature),
   111  		CanonicalTime(vote.Timestamp),
   112  	)
   113  }
   114  
   115  func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
   116  	if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) {
   117  		return ErrVoteInvalidValidatorAddress
   118  	}
   119  
   120  	if !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) {
   121  		return ErrVoteInvalidSignature
   122  	}
   123  	return nil
   124  }
   125  
   126  // ValidateBasic performs basic validation.
   127  func (vote *Vote) ValidateBasic() error {
   128  	if !IsVoteTypeValid(vote.Type) {
   129  		return errors.New("invalid Type")
   130  	}
   131  	if vote.Height < 0 {
   132  		return errors.New("negative Height")
   133  	}
   134  	if vote.Round < 0 {
   135  		return errors.New("negative Round")
   136  	}
   137  
   138  	// NOTE: Timestamp validation is subtle and handled elsewhere.
   139  
   140  	if err := vote.BlockID.ValidateBasic(); err != nil {
   141  		return fmt.Errorf("wrong BlockID: %v", err)
   142  	}
   143  	// BlockID.ValidateBasic would not err if we for instance have an empty hash but a
   144  	// non-empty PartsSetHeader:
   145  	if !vote.BlockID.IsZero() && !vote.BlockID.IsComplete() {
   146  		return fmt.Errorf("blockID must be either empty or complete, got: %v", vote.BlockID)
   147  	}
   148  	if len(vote.ValidatorAddress) != crypto.AddressSize {
   149  		return fmt.Errorf("expected ValidatorAddress size to be %d bytes, got %d bytes",
   150  			crypto.AddressSize,
   151  			len(vote.ValidatorAddress),
   152  		)
   153  	}
   154  	if vote.ValidatorIndex < 0 {
   155  		return errors.New("negative ValidatorIndex")
   156  	}
   157  	if len(vote.Signature) == 0 {
   158  		return errors.New("signature is missing")
   159  	}
   160  	if len(vote.Signature) > MaxSignatureSize {
   161  		return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize)
   162  	}
   163  	return nil
   164  }
   165  
   166  // ToProto converts the handwritten type to proto generated type
   167  // return type, nil if everything converts safely, otherwise nil, error
   168  func (vote *Vote) ToProto() *tmproto.Vote {
   169  	if vote == nil {
   170  		return nil
   171  	}
   172  
   173  	return &tmproto.Vote{
   174  		Type:             tmproto.SignedMsgType(vote.Type),
   175  		Height:           vote.Height,
   176  		Round:            int64(vote.Round),
   177  		BlockID:          vote.BlockID.ToProto(),
   178  		Timestamp:        vote.Timestamp,
   179  		ValidatorAddress: vote.ValidatorAddress,
   180  		ValidatorIndex:   int64(vote.ValidatorIndex),
   181  		Signature:        vote.Signature,
   182  	}
   183  }
   184  
   185  //FromProto converts a proto generetad type to a handwritten type
   186  // return type, nil if everything converts safely, otherwise nil, error
   187  func VoteFromProto(pv *tmproto.Vote) (*Vote, error) {
   188  	if pv == nil {
   189  		return nil, errors.New("nil vote")
   190  	}
   191  
   192  	blockID, err := BlockIDFromProto(&pv.BlockID)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	vote := new(Vote)
   198  	vote.Type = SignedMsgType(pv.Type)
   199  	vote.Height = pv.Height
   200  	vote.Round = int(pv.Round)
   201  	vote.BlockID = *blockID
   202  	vote.Timestamp = pv.Timestamp
   203  	vote.ValidatorAddress = pv.ValidatorAddress
   204  	vote.ValidatorIndex = int(pv.ValidatorIndex)
   205  	vote.Signature = pv.Signature
   206  
   207  	return vote, vote.ValidateBasic()
   208  }