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 }