github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/types/proposal.go (about)

     1  package types
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/tendermint/tendermint/libs/bytes"
     9  	tmproto "github.com/tendermint/tendermint/proto/types"
    10  	tmtime "github.com/tendermint/tendermint/types/time"
    11  )
    12  
    13  var (
    14  	ErrInvalidBlockPartSignature = errors.New("error invalid block part signature")
    15  	ErrInvalidBlockPartHash      = errors.New("error invalid block part hash")
    16  )
    17  
    18  // Proposal defines a block proposal for the consensus.
    19  // It refers to the block by BlockID field.
    20  // It must be signed by the correct proposer for the given Height/Round
    21  // to be considered valid. It may depend on votes from a previous round,
    22  // a so-called Proof-of-Lock (POL) round, as noted in the POLRound.
    23  // If POLRound >= 0, then BlockID corresponds to the block that is locked in POLRound.
    24  type Proposal struct {
    25  	Type      SignedMsgType
    26  	Height    int64     `json:"height"`
    27  	Round     int       `json:"round"`
    28  	POLRound  int       `json:"pol_round"` // -1 if null.
    29  	BlockID   BlockID   `json:"block_id"`
    30  	Timestamp time.Time `json:"timestamp"`
    31  	Signature []byte    `json:"signature"`
    32  }
    33  
    34  // NewProposal returns a new Proposal.
    35  // If there is no POLRound, polRound should be -1.
    36  func NewProposal(height int64, round int, polRound int, blockID BlockID) *Proposal {
    37  	return &Proposal{
    38  		Type:      ProposalType,
    39  		Height:    height,
    40  		Round:     round,
    41  		BlockID:   blockID,
    42  		POLRound:  polRound,
    43  		Timestamp: tmtime.Now(),
    44  	}
    45  }
    46  
    47  // ValidateBasic performs basic validation.
    48  func (p *Proposal) ValidateBasic() error {
    49  	if p.Type != ProposalType {
    50  		return errors.New("invalid Type")
    51  	}
    52  	if p.Height < 0 {
    53  		return errors.New("negative Height")
    54  	}
    55  	if p.Round < 0 {
    56  		return errors.New("negative Round")
    57  	}
    58  	if p.POLRound < -1 {
    59  		return errors.New("negative POLRound (exception: -1)")
    60  	}
    61  	if err := p.BlockID.ValidateBasic(); err != nil {
    62  		return fmt.Errorf("wrong BlockID: %v", err)
    63  	}
    64  	// ValidateBasic above would pass even if the BlockID was empty:
    65  	if !p.BlockID.IsComplete() {
    66  		return fmt.Errorf("expected a complete, non-empty BlockID, got: %v", p.BlockID)
    67  	}
    68  
    69  	// NOTE: Timestamp validation is subtle and handled elsewhere.
    70  
    71  	if len(p.Signature) == 0 {
    72  		return errors.New("signature is missing")
    73  	}
    74  	if len(p.Signature) > MaxSignatureSize {
    75  		return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize)
    76  	}
    77  	return nil
    78  }
    79  
    80  // String returns a string representation of the Proposal.
    81  func (p *Proposal) String() string {
    82  	return fmt.Sprintf("Proposal{%v/%v (%v, %v) %X @ %s}",
    83  		p.Height,
    84  		p.Round,
    85  		p.BlockID,
    86  		p.POLRound,
    87  		bytes.Fingerprint(p.Signature),
    88  		CanonicalTime(p.Timestamp))
    89  }
    90  
    91  // SignBytes returns the Proposal bytes for signing
    92  func (p *Proposal) SignBytes(chainID string) []byte {
    93  	bz, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeProposal(chainID, p))
    94  	if err != nil {
    95  		panic(err)
    96  	}
    97  	return bz
    98  }
    99  
   100  // ToProto converts Proposal to protobuf
   101  func (p *Proposal) ToProto() *tmproto.Proposal {
   102  	if p == nil {
   103  		return nil
   104  	}
   105  	pb := new(tmproto.Proposal)
   106  
   107  	pb.BlockID = p.BlockID.ToProto()
   108  	pb.Type = tmproto.SignedMsgType(p.Type)
   109  	pb.Height = p.Height
   110  	pb.Round = int32(p.Round)
   111  	pb.PolRound = int32(p.POLRound)
   112  	pb.Timestamp = p.Timestamp
   113  	pb.Signature = p.Signature
   114  
   115  	return pb
   116  }
   117  
   118  // FromProto sets a protobuf Proposal to the given pointer.
   119  // It returns an error if the proposal is invalid.
   120  func ProposalFromProto(pp *tmproto.Proposal) (*Proposal, error) {
   121  	if pp == nil {
   122  		return nil, errors.New("nil proposal")
   123  	}
   124  
   125  	p := new(Proposal)
   126  
   127  	blockID, err := BlockIDFromProto(&pp.BlockID)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	p.BlockID = *blockID
   133  	p.Type = SignedMsgType(pp.Type)
   134  	p.Height = pp.Height
   135  	p.Round = int(pp.Round)
   136  	p.POLRound = int(pp.PolRound)
   137  	p.Timestamp = pp.Timestamp
   138  	p.Signature = pp.Signature
   139  
   140  	return p, p.ValidateBasic()
   141  }