package types
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
tbytes "github.com/tendermint/tendermint/libs/bytes"
|
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
)
|
|
|
|
// Info about the status of the light client
|
|
type LightClientInfo struct {
|
|
PrimaryID string `json:"primaryID"`
|
|
WitnessesID []string `json:"witnessesID"`
|
|
NumPeers int `json:"number_of_peers,string"`
|
|
LastTrustedHeight int64 `json:"last_trusted_height,string"`
|
|
LastTrustedHash tbytes.HexBytes `json:"last_trusted_hash"`
|
|
LatestBlockTime time.Time `json:"latest_block_time"`
|
|
TrustingPeriod string `json:"trusting_period"`
|
|
// Boolean that reflects whether LatestBlockTime + trusting period is before
|
|
// time.Now() (time when /status is called)
|
|
TrustedBlockExpired bool `json:"trusted_block_expired"`
|
|
}
|
|
|
|
// LightBlock is a SignedHeader and a ValidatorSet.
|
|
// It is the basis of the light client
|
|
type LightBlock struct {
|
|
*SignedHeader `json:"signed_header"`
|
|
ValidatorSet *ValidatorSet `json:"validator_set"`
|
|
}
|
|
|
|
// ValidateBasic checks that the data is correct and consistent
|
|
//
|
|
// This does no verification of the signatures
|
|
func (lb LightBlock) ValidateBasic(chainID string) error {
|
|
if lb.SignedHeader == nil {
|
|
return errors.New("missing signed header")
|
|
}
|
|
if lb.ValidatorSet == nil {
|
|
return errors.New("missing validator set")
|
|
}
|
|
|
|
if err := lb.SignedHeader.ValidateBasic(chainID); err != nil {
|
|
return fmt.Errorf("invalid signed header: %w", err)
|
|
}
|
|
if err := lb.ValidatorSet.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("invalid validator set: %w", err)
|
|
}
|
|
|
|
// make sure the validator set is consistent with the header
|
|
if valSetHash := lb.ValidatorSet.Hash(); !bytes.Equal(lb.SignedHeader.ValidatorsHash, valSetHash) {
|
|
return fmt.Errorf("expected validator hash of header to match validator set hash (%X != %X)",
|
|
lb.SignedHeader.ValidatorsHash, valSetHash,
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// String returns a string representation of the LightBlock
|
|
func (lb LightBlock) String() string {
|
|
return lb.StringIndented("")
|
|
}
|
|
|
|
// StringIndented returns an indented string representation of the LightBlock
|
|
//
|
|
// SignedHeader
|
|
// ValidatorSet
|
|
func (lb LightBlock) StringIndented(indent string) string {
|
|
return fmt.Sprintf(`LightBlock{
|
|
%s %v
|
|
%s %v
|
|
%s}`,
|
|
indent, lb.SignedHeader.StringIndented(indent+" "),
|
|
indent, lb.ValidatorSet.StringIndented(indent+" "),
|
|
indent)
|
|
}
|
|
|
|
// ToProto converts the LightBlock to protobuf
|
|
func (lb *LightBlock) ToProto() (*tmproto.LightBlock, error) {
|
|
if lb == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
lbp := new(tmproto.LightBlock)
|
|
var err error
|
|
if lb.SignedHeader != nil {
|
|
lbp.SignedHeader = lb.SignedHeader.ToProto()
|
|
}
|
|
if lb.ValidatorSet != nil {
|
|
lbp.ValidatorSet, err = lb.ValidatorSet.ToProto()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return lbp, nil
|
|
}
|
|
|
|
// LightBlockFromProto converts from protobuf back into the Lightblock.
|
|
// An error is returned if either the validator set or signed header are invalid
|
|
func LightBlockFromProto(pb *tmproto.LightBlock) (*LightBlock, error) {
|
|
if pb == nil {
|
|
return nil, errors.New("nil light block")
|
|
}
|
|
|
|
lb := new(LightBlock)
|
|
|
|
if pb.SignedHeader != nil {
|
|
sh, err := SignedHeaderFromProto(pb.SignedHeader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lb.SignedHeader = sh
|
|
}
|
|
|
|
if pb.ValidatorSet != nil {
|
|
vals, err := ValidatorSetFromProto(pb.ValidatorSet)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lb.ValidatorSet = vals
|
|
}
|
|
|
|
return lb, nil
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// SignedHeader is a header along with the commits that prove it.
|
|
type SignedHeader struct {
|
|
*Header `json:"header"`
|
|
|
|
Commit *Commit `json:"commit"`
|
|
}
|
|
|
|
// ValidateBasic does basic consistency checks and makes sure the header
|
|
// and commit are consistent.
|
|
//
|
|
// NOTE: This does not actually check the cryptographic signatures. Make sure
|
|
// to use a Verifier to validate the signatures actually provide a
|
|
// significantly strong proof for this header's validity.
|
|
func (sh SignedHeader) ValidateBasic(chainID string) error {
|
|
if sh.Header == nil {
|
|
return errors.New("missing header")
|
|
}
|
|
if sh.Commit == nil {
|
|
return errors.New("missing commit")
|
|
}
|
|
|
|
if err := sh.Header.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("invalid header: %w", err)
|
|
}
|
|
if err := sh.Commit.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("invalid commit: %w", err)
|
|
}
|
|
|
|
if sh.ChainID != chainID {
|
|
return fmt.Errorf("header belongs to another chain %q, not %q", sh.ChainID, chainID)
|
|
}
|
|
|
|
// Make sure the header is consistent with the commit.
|
|
if sh.Commit.Height != sh.Height {
|
|
return fmt.Errorf("header and commit height mismatch: %d vs %d", sh.Height, sh.Commit.Height)
|
|
}
|
|
if hhash, chash := sh.Header.Hash(), sh.Commit.BlockID.Hash; !bytes.Equal(hhash, chash) {
|
|
return fmt.Errorf("commit signs block %X, header is block %X", chash, hhash)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// String returns a string representation of SignedHeader.
|
|
func (sh SignedHeader) String() string {
|
|
return sh.StringIndented("")
|
|
}
|
|
|
|
// StringIndented returns an indented string representation of SignedHeader.
|
|
//
|
|
// Header
|
|
// Commit
|
|
func (sh SignedHeader) StringIndented(indent string) string {
|
|
return fmt.Sprintf(`SignedHeader{
|
|
%s %v
|
|
%s %v
|
|
%s}`,
|
|
indent, sh.Header.StringIndented(indent+" "),
|
|
indent, sh.Commit.StringIndented(indent+" "),
|
|
indent)
|
|
}
|
|
|
|
// ToProto converts SignedHeader to protobuf
|
|
func (sh *SignedHeader) ToProto() *tmproto.SignedHeader {
|
|
if sh == nil {
|
|
return nil
|
|
}
|
|
|
|
psh := new(tmproto.SignedHeader)
|
|
if sh.Header != nil {
|
|
psh.Header = sh.Header.ToProto()
|
|
}
|
|
if sh.Commit != nil {
|
|
psh.Commit = sh.Commit.ToProto()
|
|
}
|
|
|
|
return psh
|
|
}
|
|
|
|
// FromProto sets a protobuf SignedHeader to the given pointer.
|
|
// It returns an error if the header or the commit is invalid.
|
|
func SignedHeaderFromProto(shp *tmproto.SignedHeader) (*SignedHeader, error) {
|
|
if shp == nil {
|
|
return nil, errors.New("nil SignedHeader")
|
|
}
|
|
|
|
sh := new(SignedHeader)
|
|
|
|
if shp.Header != nil {
|
|
h, err := HeaderFromProto(shp.Header)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sh.Header = &h
|
|
}
|
|
|
|
if shp.Commit != nil {
|
|
c, err := CommitFromProto(shp.Commit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sh.Commit = c
|
|
}
|
|
|
|
return sh, nil
|
|
}
|