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

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