github.com/evdatsion/aphelion-dpos-bft@v0.32.1/types/proposal.go (about) 1 package types 2 3 import ( 4 "errors" 5 "fmt" 6 "time" 7 8 cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common" 9 tmtime "github.com/evdatsion/aphelion-dpos-bft/types/time" 10 ) 11 12 var ( 13 ErrInvalidBlockPartSignature = errors.New("Error invalid block part signature") 14 ErrInvalidBlockPartHash = errors.New("Error invalid block part hash") 15 ) 16 17 // Proposal defines a block proposal for the consensus. 18 // It refers to the block by BlockID field. 19 // It must be signed by the correct proposer for the given Height/Round 20 // to be considered valid. It may depend on votes from a previous round, 21 // a so-called Proof-of-Lock (POL) round, as noted in the POLRound. 22 // If POLRound >= 0, then BlockID corresponds to the block that is locked in POLRound. 23 type Proposal struct { 24 Type SignedMsgType 25 Height int64 `json:"height"` 26 Round int `json:"round"` 27 POLRound int `json:"pol_round"` // -1 if null. 28 BlockID BlockID `json:"block_id"` 29 Timestamp time.Time `json:"timestamp"` 30 Signature []byte `json:"signature"` 31 } 32 33 // NewProposal returns a new Proposal. 34 // If there is no POLRound, polRound should be -1. 35 func NewProposal(height int64, round int, polRound int, blockID BlockID) *Proposal { 36 return &Proposal{ 37 Type: ProposalType, 38 Height: height, 39 Round: round, 40 BlockID: blockID, 41 POLRound: polRound, 42 Timestamp: tmtime.Now(), 43 } 44 } 45 46 // ValidateBasic performs basic validation. 47 func (p *Proposal) ValidateBasic() error { 48 if p.Type != ProposalType { 49 return errors.New("Invalid Type") 50 } 51 if p.Height < 0 { 52 return errors.New("Negative Height") 53 } 54 if p.Round < 0 { 55 return errors.New("Negative Round") 56 } 57 if p.POLRound < -1 { 58 return errors.New("Negative POLRound (exception: -1)") 59 } 60 if err := p.BlockID.ValidateBasic(); err != nil { 61 return fmt.Errorf("Wrong BlockID: %v", err) 62 } 63 // ValidateBasic above would pass even if the BlockID was empty: 64 if !p.BlockID.IsComplete() { 65 return fmt.Errorf("Expected a complete, non-empty BlockID, got: %v", p.BlockID) 66 } 67 68 // NOTE: Timestamp validation is subtle and handled elsewhere. 69 70 if len(p.Signature) == 0 { 71 return errors.New("Signature is missing") 72 } 73 if len(p.Signature) > MaxSignatureSize { 74 return fmt.Errorf("Signature is too big (max: %d)", MaxSignatureSize) 75 } 76 return nil 77 } 78 79 // String returns a string representation of the Proposal. 80 func (p *Proposal) String() string { 81 return fmt.Sprintf("Proposal{%v/%v (%v, %v) %X @ %s}", 82 p.Height, 83 p.Round, 84 p.BlockID, 85 p.POLRound, 86 cmn.Fingerprint(p.Signature), 87 CanonicalTime(p.Timestamp)) 88 } 89 90 // SignBytes returns the Proposal bytes for signing 91 func (p *Proposal) SignBytes(chainID string) []byte { 92 bz, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeProposal(chainID, p)) 93 if err != nil { 94 panic(err) 95 } 96 return bz 97 }