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

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