You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
3.7 KiB

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