github.com/evdatsion/aphelion-dpos-bft@v0.32.1/types/vote.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/evdatsion/aphelion-dpos-bft/crypto"
    10  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    11  )
    12  
    13  const (
    14  	// MaxVoteBytes is a maximum vote size (including amino overhead).
    15  	MaxVoteBytes int64 = 223
    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  	ErrVoteInvalidBlockHash          = errors.New("Invalid block hash")
    24  	ErrVoteNonDeterministicSignature = errors.New("Non-deterministic signature")
    25  	ErrVoteNil                       = errors.New("Nil vote")
    26  )
    27  
    28  type ErrVoteConflictingVotes struct {
    29  	*DuplicateVoteEvidence
    30  }
    31  
    32  func (err *ErrVoteConflictingVotes) Error() string {
    33  	return fmt.Sprintf("Conflicting votes from validator %v", err.PubKey.Address())
    34  }
    35  
    36  func NewConflictingVoteError(val *Validator, voteA, voteB *Vote) *ErrVoteConflictingVotes {
    37  	return &ErrVoteConflictingVotes{
    38  		&DuplicateVoteEvidence{
    39  			PubKey: val.PubKey,
    40  			VoteA:  voteA,
    41  			VoteB:  voteB,
    42  		},
    43  	}
    44  }
    45  
    46  // Address is hex bytes.
    47  type Address = crypto.Address
    48  
    49  // Vote represents a prevote, precommit, or commit vote from validators for
    50  // consensus.
    51  type Vote struct {
    52  	Type             SignedMsgType `json:"type"`
    53  	Height           int64         `json:"height"`
    54  	Round            int           `json:"round"`
    55  	BlockID          BlockID       `json:"block_id"` // zero if vote is nil.
    56  	Timestamp        time.Time     `json:"timestamp"`
    57  	ValidatorAddress Address       `json:"validator_address"`
    58  	ValidatorIndex   int           `json:"validator_index"`
    59  	Signature        []byte        `json:"signature"`
    60  }
    61  
    62  // CommitSig converts the Vote to a CommitSig.
    63  // If the Vote is nil, the CommitSig will be nil.
    64  func (vote *Vote) CommitSig() *CommitSig {
    65  	if vote == nil {
    66  		return nil
    67  	}
    68  	cs := CommitSig(*vote)
    69  	return &cs
    70  }
    71  
    72  func (vote *Vote) SignBytes(chainID string) []byte {
    73  	bz, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeVote(chainID, vote))
    74  	if err != nil {
    75  		panic(err)
    76  	}
    77  	return bz
    78  }
    79  
    80  func (vote *Vote) Copy() *Vote {
    81  	voteCopy := *vote
    82  	return &voteCopy
    83  }
    84  
    85  func (vote *Vote) String() string {
    86  	if vote == nil {
    87  		return "nil-Vote"
    88  	}
    89  	var typeString string
    90  	switch vote.Type {
    91  	case PrevoteType:
    92  		typeString = "Prevote"
    93  	case PrecommitType:
    94  		typeString = "Precommit"
    95  	default:
    96  		panic("Unknown vote type")
    97  	}
    98  
    99  	return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}",
   100  		vote.ValidatorIndex,
   101  		cmn.Fingerprint(vote.ValidatorAddress),
   102  		vote.Height,
   103  		vote.Round,
   104  		vote.Type,
   105  		typeString,
   106  		cmn.Fingerprint(vote.BlockID.Hash),
   107  		cmn.Fingerprint(vote.Signature),
   108  		CanonicalTime(vote.Timestamp),
   109  	)
   110  }
   111  
   112  func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
   113  	if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) {
   114  		return ErrVoteInvalidValidatorAddress
   115  	}
   116  
   117  	if !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) {
   118  		return ErrVoteInvalidSignature
   119  	}
   120  	return nil
   121  }
   122  
   123  // ValidateBasic performs basic validation.
   124  func (vote *Vote) ValidateBasic() error {
   125  	if !IsVoteTypeValid(vote.Type) {
   126  		return errors.New("Invalid Type")
   127  	}
   128  	if vote.Height < 0 {
   129  		return errors.New("Negative Height")
   130  	}
   131  	if vote.Round < 0 {
   132  		return errors.New("Negative Round")
   133  	}
   134  
   135  	// NOTE: Timestamp validation is subtle and handled elsewhere.
   136  
   137  	if err := vote.BlockID.ValidateBasic(); err != nil {
   138  		return fmt.Errorf("Wrong BlockID: %v", err)
   139  	}
   140  	// BlockID.ValidateBasic would not err if we for instance have an empty hash but a
   141  	// non-empty PartsSetHeader:
   142  	if !vote.BlockID.IsZero() && !vote.BlockID.IsComplete() {
   143  		return fmt.Errorf("BlockID must be either empty or complete, got: %v", vote.BlockID)
   144  	}
   145  	if len(vote.ValidatorAddress) != crypto.AddressSize {
   146  		return fmt.Errorf("Expected ValidatorAddress size to be %d bytes, got %d bytes",
   147  			crypto.AddressSize,
   148  			len(vote.ValidatorAddress),
   149  		)
   150  	}
   151  	if vote.ValidatorIndex < 0 {
   152  		return errors.New("Negative ValidatorIndex")
   153  	}
   154  	if len(vote.Signature) == 0 {
   155  		return errors.New("Signature is missing")
   156  	}
   157  	if len(vote.Signature) > MaxSignatureSize {
   158  		return fmt.Errorf("Signature is too big (max: %d)", MaxSignatureSize)
   159  	}
   160  	return nil
   161  }