github.com/vipernet-xyz/tm@v0.34.24/types/vote.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/vipernet-xyz/tm/crypto"
    10  	tmbytes "github.com/vipernet-xyz/tm/libs/bytes"
    11  	"github.com/vipernet-xyz/tm/libs/protoio"
    12  	tmproto "github.com/vipernet-xyz/tm/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  }