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.

154 lines
4.5 KiB

7 years ago
  1. package proxy
  2. import (
  3. "fmt"
  4. "github.com/pkg/errors"
  5. cmn "github.com/tendermint/tmlibs/common"
  6. "github.com/tendermint/tendermint/lite"
  7. rpcclient "github.com/tendermint/tendermint/rpc/client"
  8. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  9. "github.com/tendermint/tendermint/types"
  10. )
  11. // KeyProof represents a proof of existence or absence of a single key.
  12. // Copied from iavl repo. TODO
  13. type KeyProof interface {
  14. // Verify verfies the proof is valid. To verify absence,
  15. // the value should be nil.
  16. Verify(key, value, root []byte) error
  17. // Root returns the root hash of the proof.
  18. Root() []byte
  19. // Serialize itself
  20. Bytes() []byte
  21. }
  22. // GetWithProof will query the key on the given node, and verify it has
  23. // a valid proof, as defined by the certifier.
  24. //
  25. // If there is any error in checking, returns an error.
  26. // If val is non-empty, proof should be KeyExistsProof
  27. // If val is empty, proof should be KeyMissingProof
  28. func GetWithProof(key []byte, reqHeight int64, node rpcclient.Client,
  29. cert lite.Certifier) (
  30. val cmn.HexBytes, height int64, proof KeyProof, err error) {
  31. if reqHeight < 0 {
  32. err = errors.Errorf("Height cannot be negative")
  33. return
  34. }
  35. _resp, proof, err := GetWithProofOptions("/key", key,
  36. rpcclient.ABCIQueryOptions{Height: int64(reqHeight)},
  37. node, cert)
  38. if _resp != nil {
  39. resp := _resp.Response
  40. val, height = resp.Value, resp.Height
  41. }
  42. return val, height, proof, err
  43. }
  44. // GetWithProofOptions is useful if you want full access to the ABCIQueryOptions
  45. func GetWithProofOptions(path string, key []byte, opts rpcclient.ABCIQueryOptions,
  46. node rpcclient.Client, cert lite.Certifier) (
  47. *ctypes.ResultABCIQuery, KeyProof, error) {
  48. _resp, err := node.ABCIQueryWithOptions(path, key, opts)
  49. if err != nil {
  50. return nil, nil, err
  51. }
  52. resp := _resp.Response
  53. // make sure the proof is the proper height
  54. if resp.IsErr() {
  55. err = errors.Errorf("Query error for key %d: %d", key, resp.Code)
  56. return nil, nil, err
  57. }
  58. if len(resp.Key) == 0 || len(resp.Proof) == 0 {
  59. return nil, nil, ErrNoData()
  60. }
  61. if resp.Height == 0 {
  62. return nil, nil, errors.New("Height returned is zero")
  63. }
  64. // AppHash for height H is in header H+1
  65. signedHeader, err := GetCertifiedCommit(resp.Height+1, node, cert)
  66. if err != nil {
  67. return nil, nil, err
  68. }
  69. _ = signedHeader
  70. return &ctypes.ResultABCIQuery{Response: resp}, nil, nil
  71. /* // TODO refactor so iavl stuff is not in tendermint core
  72. // https://github.com/tendermint/tendermint/issues/1183
  73. if len(resp.Value) > 0 {
  74. // The key was found, construct a proof of existence.
  75. proof, err := iavl.ReadKeyProof(resp.Proof)
  76. if err != nil {
  77. return nil, nil, errors.Wrap(err, "Error reading proof")
  78. }
  79. eproof, ok := proof.(*iavl.KeyExistsProof)
  80. if !ok {
  81. return nil, nil, errors.New("Expected KeyExistsProof for non-empty value")
  82. }
  83. // Validate the proof against the certified header to ensure data integrity.
  84. err = eproof.Verify(resp.Key, resp.Value, signedHeader.AppHash)
  85. if err != nil {
  86. return nil, nil, errors.Wrap(err, "Couldn't verify proof")
  87. }
  88. return &ctypes.ResultABCIQuery{Response: resp}, eproof, nil
  89. }
  90. // The key wasn't found, construct a proof of non-existence.
  91. proof, err := iavl.ReadKeyProof(resp.Proof)
  92. if err != nil {
  93. return nil, nil, errors.Wrap(err, "Error reading proof")
  94. }
  95. aproof, ok := proof.(*iavl.KeyAbsentProof)
  96. if !ok {
  97. return nil, nil, errors.New("Expected KeyAbsentProof for empty Value")
  98. }
  99. // Validate the proof against the certified header to ensure data integrity.
  100. err = aproof.Verify(resp.Key, nil, signedHeader.AppHash)
  101. if err != nil {
  102. return nil, nil, errors.Wrap(err, "Couldn't verify proof")
  103. }
  104. return &ctypes.ResultABCIQuery{Response: resp}, aproof, ErrNoData()
  105. */
  106. }
  107. // GetCertifiedCommit gets the signed header for a given height and certifies
  108. // it. Returns error if unable to get a proven header.
  109. func GetCertifiedCommit(h int64, client rpcclient.Client, cert lite.Certifier) (types.SignedHeader, error) {
  110. // FIXME: cannot use cert.GetByHeight for now, as it also requires
  111. // Validators and will fail on querying tendermint for non-current height.
  112. // When this is supported, we should use it instead...
  113. rpcclient.WaitForHeight(client, h, nil)
  114. cresp, err := client.Commit(&h)
  115. if err != nil {
  116. return types.SignedHeader{}, err
  117. }
  118. // Validate downloaded checkpoint with our request and trust store.
  119. sh := cresp.SignedHeader
  120. if sh.Height != h {
  121. return types.SignedHeader{}, fmt.Errorf("height mismatch: want %v got %v",
  122. h, sh.Height)
  123. }
  124. if err = cert.Certify(sh); err != nil {
  125. return types.SignedHeader{}, err
  126. }
  127. return sh, nil
  128. }