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.

221 lines
5.4 KiB

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