github.com/Finschia/ostracon@v1.1.5/types/vote.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    10  
    11  	"github.com/Finschia/ostracon/crypto"
    12  	"github.com/Finschia/ostracon/crypto/ed25519"
    13  	tmbytes "github.com/Finschia/ostracon/libs/bytes"
    14  	"github.com/Finschia/ostracon/libs/protoio"
    15  )
    16  
    17  const (
    18  	nilVoteStr string = "nil-Vote"
    19  )
    20  
    21  var (
    22  	ErrVoteUnexpectedStep            = errors.New("unexpected step")
    23  	ErrVoteInvalidValidatorIndex     = errors.New("invalid validator index")
    24  	ErrVoteInvalidValidatorAddress   = errors.New("invalid validator address")
    25  	ErrVoteInvalidSignature          = errors.New("invalid signature")
    26  	ErrVoteInvalidBlockHash          = errors.New("invalid block hash")
    27  	ErrVoteNonDeterministicSignature = errors.New("non-deterministic signature")
    28  	ErrVoteNil                       = errors.New("nil vote")
    29  )
    30  
    31  type ErrVoteConflictingVotes struct {
    32  	VoteA *Vote
    33  	VoteB *Vote
    34  }
    35  
    36  func (err *ErrVoteConflictingVotes) Error() string {
    37  	return fmt.Sprintf("conflicting votes from validator %X", err.VoteA.ValidatorAddress)
    38  }
    39  
    40  func NewConflictingVoteError(vote1, vote2 *Vote) *ErrVoteConflictingVotes {
    41  	return &ErrVoteConflictingVotes{
    42  		VoteA: vote1,
    43  		VoteB: vote2,
    44  	}
    45  }
    46  
    47  // Address is hex bytes.
    48  type Address = crypto.Address
    49  
    50  // Vote represents a prevote, precommit, or commit vote from validators for
    51  // consensus.
    52  type Vote struct {
    53  	Type             tmproto.SignedMsgType `json:"type"`
    54  	Height           int64                 `json:"height"`
    55  	Round            int32                 `json:"round"`    // assume there will not be greater than 2_147_483_647 rounds
    56  	BlockID          BlockID               `json:"block_id"` // zero if vote is nil.
    57  	Timestamp        time.Time             `json:"timestamp"`
    58  	ValidatorAddress Address               `json:"validator_address"`
    59  	ValidatorIndex   int32                 `json:"validator_index"`
    60  	Signature        []byte                `json:"signature"`
    61  }
    62  
    63  const MaxVoteBytes int64 = (1 + 1) + // Type
    64  	(1 + 9) + // Height
    65  	(1 + 5) + // Round
    66  	(1 + 76 + 1) + // BlockID
    67  	(1 + 17 + 1) + // Timestamp
    68  	(1 + 20 + 1) + // ValidatorAddress
    69  	(1 + 5) + // ValidatorIndex
    70  	(1 + ed25519.SignatureSize + 1) // Signature
    71  
    72  // CommitSig converts the Vote to a CommitSig.
    73  func (vote *Vote) CommitSig() CommitSig {
    74  	if vote == nil {
    75  		return NewCommitSigAbsent()
    76  	}
    77  
    78  	var blockIDFlag BlockIDFlag
    79  	switch {
    80  	case vote.BlockID.IsComplete():
    81  		blockIDFlag = BlockIDFlagCommit
    82  	case vote.BlockID.IsZero():
    83  		blockIDFlag = BlockIDFlagNil
    84  	default:
    85  		panic(fmt.Sprintf("Invalid vote %v - expected BlockID to be either empty or complete", vote))
    86  	}
    87  
    88  	return CommitSig{
    89  		BlockIDFlag:      blockIDFlag,
    90  		ValidatorAddress: vote.ValidatorAddress,
    91  		Timestamp:        vote.Timestamp,
    92  		Signature:        vote.Signature,
    93  	}
    94  }
    95  
    96  // VoteSignBytes returns the proto-encoding of the canonicalized Vote, for
    97  // signing. Panics is the marshaling fails.
    98  //
    99  // The encoded Protobuf message is varint length-prefixed (using MarshalDelimited)
   100  // for backwards-compatibility with the Amino encoding, due to e.g. hardware
   101  // devices that rely on this encoding.
   102  //
   103  // See CanonicalizeVote
   104  func VoteSignBytes(chainID string, vote *tmproto.Vote) []byte {
   105  	pb := CanonicalizeVote(chainID, vote)
   106  	bz, err := protoio.MarshalDelimited(&pb)
   107  	if err != nil {
   108  		panic(err)
   109  	}
   110  
   111  	return bz
   112  }
   113  
   114  func (vote *Vote) Copy() *Vote {
   115  	voteCopy := *vote
   116  	return &voteCopy
   117  }
   118  
   119  // String returns a string representation of Vote.
   120  //
   121  // 1. validator index
   122  // 2. first 6 bytes of validator address
   123  // 3. height
   124  // 4. round,
   125  // 5. type byte
   126  // 6. type string
   127  // 7. first 6 bytes of block hash
   128  // 8. first 6 bytes of signature
   129  // 9. timestamp
   130  func (vote *Vote) String() string {
   131  	if vote == nil {
   132  		return nilVoteStr
   133  	}
   134  
   135  	var typeString string
   136  	switch vote.Type {
   137  	case tmproto.PrevoteType:
   138  		typeString = "Prevote"
   139  	case tmproto.PrecommitType:
   140  		typeString = "Precommit"
   141  	default:
   142  		panic("Unknown vote type")
   143  	}
   144  
   145  	return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}",
   146  		vote.ValidatorIndex,
   147  		tmbytes.Fingerprint(vote.ValidatorAddress),
   148  		vote.Height,
   149  		vote.Round,
   150  		vote.Type,
   151  		typeString,
   152  		tmbytes.Fingerprint(vote.BlockID.Hash),
   153  		tmbytes.Fingerprint(vote.Signature),
   154  		CanonicalTime(vote.Timestamp),
   155  	)
   156  }
   157  
   158  func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
   159  	if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) {
   160  		return ErrVoteInvalidValidatorAddress
   161  	}
   162  	v := vote.ToProto()
   163  	if !pubKey.VerifySignature(VoteSignBytes(chainID, v), vote.Signature) {
   164  		return ErrVoteInvalidSignature
   165  	}
   166  	return nil
   167  }
   168  
   169  // ValidateBasic performs basic validation.
   170  func (vote *Vote) ValidateBasic() error {
   171  	if !IsVoteTypeValid(vote.Type) {
   172  		return errors.New("invalid Type")
   173  	}
   174  
   175  	if vote.Height < 0 {
   176  		return errors.New("negative Height")
   177  	}
   178  
   179  	if vote.Round < 0 {
   180  		return errors.New("negative Round")
   181  	}
   182  
   183  	// NOTE: Timestamp validation is subtle and handled elsewhere.
   184  
   185  	if err := vote.BlockID.ValidateBasic(); err != nil {
   186  		return fmt.Errorf("wrong BlockID: %v", err)
   187  	}
   188  
   189  	// BlockID.ValidateBasic would not err if we for instance have an empty hash but a
   190  	// non-empty PartsSetHeader:
   191  	if !vote.BlockID.IsZero() && !vote.BlockID.IsComplete() {
   192  		return fmt.Errorf("blockID must be either empty or complete, got: %v", vote.BlockID)
   193  	}
   194  
   195  	if len(vote.ValidatorAddress) != crypto.AddressSize {
   196  		return fmt.Errorf("expected ValidatorAddress size to be %d bytes, got %d bytes",
   197  			crypto.AddressSize,
   198  			len(vote.ValidatorAddress),
   199  		)
   200  	}
   201  	if vote.ValidatorIndex < 0 {
   202  		return errors.New("negative ValidatorIndex")
   203  	}
   204  	if len(vote.Signature) == 0 {
   205  		return errors.New("signature is missing")
   206  	}
   207  
   208  	if len(vote.Signature) > MaxSignatureSize {
   209  		return fmt.Errorf("signature is too big %d (max: %d)", len(vote.Signature), MaxSignatureSize)
   210  	}
   211  
   212  	return nil
   213  }
   214  
   215  // ToProto converts the handwritten type to proto generated type
   216  // return type, nil if everything converts safely, otherwise nil, error
   217  func (vote *Vote) ToProto() *tmproto.Vote {
   218  	if vote == nil {
   219  		return nil
   220  	}
   221  
   222  	return &tmproto.Vote{
   223  		Type:             vote.Type,
   224  		Height:           vote.Height,
   225  		Round:            vote.Round,
   226  		BlockID:          vote.BlockID.ToProto(),
   227  		Timestamp:        vote.Timestamp,
   228  		ValidatorAddress: vote.ValidatorAddress,
   229  		ValidatorIndex:   vote.ValidatorIndex,
   230  		Signature:        vote.Signature,
   231  	}
   232  }
   233  
   234  // FromProto converts a proto generetad type to a handwritten type
   235  // return type, nil if everything converts safely, otherwise nil, error
   236  func VoteFromProto(pv *tmproto.Vote) (*Vote, error) {
   237  	if pv == nil {
   238  		return nil, errors.New("nil vote")
   239  	}
   240  
   241  	blockID, err := BlockIDFromProto(&pv.BlockID)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	vote := new(Vote)
   247  	vote.Type = pv.Type
   248  	vote.Height = pv.Height
   249  	vote.Round = pv.Round
   250  	vote.BlockID = *blockID
   251  	vote.Timestamp = pv.Timestamp
   252  	vote.ValidatorAddress = pv.ValidatorAddress
   253  	vote.ValidatorIndex = pv.ValidatorIndex
   254  	vote.Signature = pv.Signature
   255  
   256  	return vote, vote.ValidateBasic()
   257  }