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.

237 lines
6.1 KiB

  1. package types
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "time"
  7. tbytes "github.com/tendermint/tendermint/libs/bytes"
  8. tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
  9. )
  10. // Info about the status of the light client
  11. type LightClientInfo struct {
  12. PrimaryID string `json:"primaryID"`
  13. WitnessesID []string `json:"witnessesID"`
  14. NumPeers int `json:"number_of_peers,string"`
  15. LastTrustedHeight int64 `json:"last_trusted_height,string"`
  16. LastTrustedHash tbytes.HexBytes `json:"last_trusted_hash"`
  17. LatestBlockTime time.Time `json:"latest_block_time"`
  18. TrustingPeriod string `json:"trusting_period"`
  19. // Boolean that reflects whether LatestBlockTime + trusting period is before
  20. // time.Now() (time when /status is called)
  21. TrustedBlockExpired bool `json:"trusted_block_expired"`
  22. }
  23. // LightBlock is a SignedHeader and a ValidatorSet.
  24. // It is the basis of the light client
  25. type LightBlock struct {
  26. *SignedHeader `json:"signed_header"`
  27. ValidatorSet *ValidatorSet `json:"validator_set"`
  28. }
  29. // ValidateBasic checks that the data is correct and consistent
  30. //
  31. // This does no verification of the signatures
  32. func (lb LightBlock) ValidateBasic(chainID string) error {
  33. if lb.SignedHeader == nil {
  34. return errors.New("missing signed header")
  35. }
  36. if lb.ValidatorSet == nil {
  37. return errors.New("missing validator set")
  38. }
  39. if err := lb.SignedHeader.ValidateBasic(chainID); err != nil {
  40. return fmt.Errorf("invalid signed header: %w", err)
  41. }
  42. if err := lb.ValidatorSet.ValidateBasic(); err != nil {
  43. return fmt.Errorf("invalid validator set: %w", err)
  44. }
  45. // make sure the validator set is consistent with the header
  46. if valSetHash := lb.ValidatorSet.Hash(); !bytes.Equal(lb.SignedHeader.ValidatorsHash, valSetHash) {
  47. return fmt.Errorf("expected validator hash of header to match validator set hash (%X != %X)",
  48. lb.SignedHeader.ValidatorsHash, valSetHash,
  49. )
  50. }
  51. return nil
  52. }
  53. // String returns a string representation of the LightBlock
  54. func (lb LightBlock) String() string {
  55. return lb.StringIndented("")
  56. }
  57. // StringIndented returns an indented string representation of the LightBlock
  58. //
  59. // SignedHeader
  60. // ValidatorSet
  61. func (lb LightBlock) StringIndented(indent string) string {
  62. return fmt.Sprintf(`LightBlock{
  63. %s %v
  64. %s %v
  65. %s}`,
  66. indent, lb.SignedHeader.StringIndented(indent+" "),
  67. indent, lb.ValidatorSet.StringIndented(indent+" "),
  68. indent)
  69. }
  70. // ToProto converts the LightBlock to protobuf
  71. func (lb *LightBlock) ToProto() (*tmproto.LightBlock, error) {
  72. if lb == nil {
  73. return nil, nil
  74. }
  75. lbp := new(tmproto.LightBlock)
  76. var err error
  77. if lb.SignedHeader != nil {
  78. lbp.SignedHeader = lb.SignedHeader.ToProto()
  79. }
  80. if lb.ValidatorSet != nil {
  81. lbp.ValidatorSet, err = lb.ValidatorSet.ToProto()
  82. if err != nil {
  83. return nil, err
  84. }
  85. }
  86. return lbp, nil
  87. }
  88. // LightBlockFromProto converts from protobuf back into the Lightblock.
  89. // An error is returned if either the validator set or signed header are invalid
  90. func LightBlockFromProto(pb *tmproto.LightBlock) (*LightBlock, error) {
  91. if pb == nil {
  92. return nil, errors.New("nil light block")
  93. }
  94. lb := new(LightBlock)
  95. if pb.SignedHeader != nil {
  96. sh, err := SignedHeaderFromProto(pb.SignedHeader)
  97. if err != nil {
  98. return nil, err
  99. }
  100. lb.SignedHeader = sh
  101. }
  102. if pb.ValidatorSet != nil {
  103. vals, err := ValidatorSetFromProto(pb.ValidatorSet)
  104. if err != nil {
  105. return nil, err
  106. }
  107. lb.ValidatorSet = vals
  108. }
  109. return lb, nil
  110. }
  111. //-----------------------------------------------------------------------------
  112. // SignedHeader is a header along with the commits that prove it.
  113. type SignedHeader struct {
  114. *Header `json:"header"`
  115. Commit *Commit `json:"commit"`
  116. }
  117. // ValidateBasic does basic consistency checks and makes sure the header
  118. // and commit are consistent.
  119. //
  120. // NOTE: This does not actually check the cryptographic signatures. Make sure
  121. // to use a Verifier to validate the signatures actually provide a
  122. // significantly strong proof for this header's validity.
  123. func (sh SignedHeader) ValidateBasic(chainID string) error {
  124. if sh.Header == nil {
  125. return errors.New("missing header")
  126. }
  127. if sh.Commit == nil {
  128. return errors.New("missing commit")
  129. }
  130. if err := sh.Header.ValidateBasic(); err != nil {
  131. return fmt.Errorf("invalid header: %w", err)
  132. }
  133. if err := sh.Commit.ValidateBasic(); err != nil {
  134. return fmt.Errorf("invalid commit: %w", err)
  135. }
  136. if sh.ChainID != chainID {
  137. return fmt.Errorf("header belongs to another chain %q, not %q", sh.ChainID, chainID)
  138. }
  139. // Make sure the header is consistent with the commit.
  140. if sh.Commit.Height != sh.Height {
  141. return fmt.Errorf("header and commit height mismatch: %d vs %d", sh.Height, sh.Commit.Height)
  142. }
  143. if hhash, chash := sh.Header.Hash(), sh.Commit.BlockID.Hash; !bytes.Equal(hhash, chash) {
  144. return fmt.Errorf("commit signs block %X, header is block %X", chash, hhash)
  145. }
  146. return nil
  147. }
  148. // String returns a string representation of SignedHeader.
  149. func (sh SignedHeader) String() string {
  150. return sh.StringIndented("")
  151. }
  152. // StringIndented returns an indented string representation of SignedHeader.
  153. //
  154. // Header
  155. // Commit
  156. func (sh SignedHeader) StringIndented(indent string) string {
  157. return fmt.Sprintf(`SignedHeader{
  158. %s %v
  159. %s %v
  160. %s}`,
  161. indent, sh.Header.StringIndented(indent+" "),
  162. indent, sh.Commit.StringIndented(indent+" "),
  163. indent)
  164. }
  165. // ToProto converts SignedHeader to protobuf
  166. func (sh *SignedHeader) ToProto() *tmproto.SignedHeader {
  167. if sh == nil {
  168. return nil
  169. }
  170. psh := new(tmproto.SignedHeader)
  171. if sh.Header != nil {
  172. psh.Header = sh.Header.ToProto()
  173. }
  174. if sh.Commit != nil {
  175. psh.Commit = sh.Commit.ToProto()
  176. }
  177. return psh
  178. }
  179. // FromProto sets a protobuf SignedHeader to the given pointer.
  180. // It returns an error if the header or the commit is invalid.
  181. func SignedHeaderFromProto(shp *tmproto.SignedHeader) (*SignedHeader, error) {
  182. if shp == nil {
  183. return nil, errors.New("nil SignedHeader")
  184. }
  185. sh := new(SignedHeader)
  186. if shp.Header != nil {
  187. h, err := HeaderFromProto(shp.Header)
  188. if err != nil {
  189. return nil, err
  190. }
  191. sh.Header = &h
  192. }
  193. if shp.Commit != nil {
  194. c, err := CommitFromProto(shp.Commit)
  195. if err != nil {
  196. return nil, err
  197. }
  198. sh.Commit = c
  199. }
  200. return sh, nil
  201. }