package types import ( "bytes" "encoding" "errors" "time" "google.golang.org/protobuf/proto" ) var ( // ErrHeaderDataMismatch is returned when header and data metadata do not describe the same block. ErrHeaderDataMismatch = errors.New("header and data do not match") // ErrDataHashMismatch is returned when a header's data hash does not match its block data. ErrDataHashMismatch = errors.New("dataHash from the header does not match with hash of the block's data") ) // Version captures the consensus rules for processing a block in the blockchain, // including all blockchain data structures and the rules of the application's // state transition machine. // This is equivalent to the tmversion.Consensus type in Tendermint. type Version struct { Block uint64 App uint64 } var _ encoding.BinaryMarshaler = &Data{} var _ encoding.BinaryUnmarshaler = &Data{} // Metadata defines metadata for Data struct to help with p2p gossiping. type Metadata struct { ChainID string Height uint64 Time uint64 LastDataHash Hash } // Data defines Evolve block data. type Data struct { *Metadata Txs Txs } // SignedData combines Data and its signature. type SignedData struct { Data Signature Signature Signer Signer } // Signature represents signature of block creator. type Signature []byte // ValidateBasic performs basic validation of a signature. func (signature *Signature) ValidateBasic() error { if len(*signature) == 0 { return ErrSignatureEmpty } return nil } // Validate performs validation of data with respect to its header func Validate(header *SignedHeader, data *Data) error { // Validate Metadata only when available if data.Metadata != nil { if header.ChainID() != data.ChainID() || header.Height() != data.Height() || header.Time() != data.Time() { // skipping LastDataHash comparison as it needs access to previous header return ErrHeaderDataMismatch } } // exclude Metadata while computing the data hash for comparison d := Data{Txs: data.Txs} dataHash := d.DACommitment() if !bytes.Equal(dataHash[:], header.DataHash[:]) { return ErrDataHashMismatch } return nil } // New returns a new Block. func (d *Data) New() *Data { return new(Data) } // IsZero returns true if the block is nil. func (d *Data) IsZero() bool { return d == nil } // ChainID returns chain ID of the block. func (d *Data) ChainID() string { return d.Metadata.ChainID } // Height returns height of the block. func (d *Data) Height() uint64 { return d.Metadata.Height } // LastHeader returns last header hash of the block. func (d *Data) LastHeader() Hash { return d.LastDataHash } // Time returns time of the block. func (d *Data) Time() time.Time { return time.Unix(0, int64(d.Metadata.Time)) } // Verify Verifies a new, untrusted block against a trusted block. func (d *Data) Verify(untrustedData *Data) error { if untrustedData == nil { return errors.New("untrusted block cannot be nil") } dataHash := d.Hash() // Check if the data hash of the untrusted block matches the last data hash of the trusted block if !bytes.Equal(dataHash[:], untrustedData.LastDataHash[:]) { return errors.New("data hash of the trusted data does not match with last data hash of the untrusted data") } return nil } // Validate performs basic validation of a block. // this is used to implement the header interface for go header func (d *Data) Validate() error { // Validate that Metadata is set if d.Metadata == nil { return errors.New("data metadata cannot be nil") } // Validate ChainID is not empty if d.Metadata.ChainID == "" { return errors.New("chain ID cannot be empty") } // Validate Height is non-zero for non-genesis blocks if d.Metadata.Height == 0 { return errors.New("height cannot be zero") } // Validate timestamp - must be non-zero if d.Metadata.Time == 0 { return errors.New("timestamp cannot be zero") } // Check timestamp is not too far in the future (allow some clock drift) maxAllowedTime := time.Now().Add(MaxClockDrift) dataTime := d.Time() if dataTime.After(maxAllowedTime) { return errors.New("timestamp too far in future") } return nil } // Size returns size of the block in bytes. func (d *Data) Size() int { return proto.Size(d.ToProto()) } // TxsByteSize returns total byte size of all transactions without proto marshalling. // This is much cheaper than Size() and useful for metrics/reporting. func (d *Data) TxsByteSize() int { n := 0 for _, tx := range d.Txs { n += len(tx) } return n }