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.

419 lines
12 KiB

lite2: light client with weak subjectivity (#3989) Refs #1771 ADR: https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md ## Commits: * add Verifier and VerifyCommitTrusting * add two more checks make trustLevel an option * float32 for trustLevel * check newHeader time * started writing lite Client * unify Verify methods * ensure h2.Header.bfttime < h1.Header.bfttime + tp * move trust checks into Verify function * add more comments * more docs * started writing tests * unbonding period failures * tests are green * export ErrNewHeaderTooFarIntoFuture * make golangci happy * test for non-adjusted headers * more precision * providers and stores * VerifyHeader and VerifyHeaderAtHeight funcs * fix compile errors * remove lastVerifiedHeight, persist new trusted header * sequential verification * remove TrustedStore option * started writing tests for light client * cover basic cases for linear verification * bisection tests PASS * rename BisectingVerification to SkippingVerification * refactor the code * add TrustedHeader method * consolidate sequential verification tests * consolidate skipping verification tests * rename trustedVals to trustedNextVals * start writing docs * ValidateTrustLevel func and ErrOldHeaderExpired error * AutoClient and example tests * fix errors * update doc * remove ErrNewHeaderTooFarIntoFuture This check is unnecessary given existing a) ErrOldHeaderExpired b) h2.Time > now checks. * return an error if we're at more recent height * add comments * add LastSignedHeaderHeight method to Store I think it's fine if Store tracks last height * copy over proxy from old lite package * make TrustedHeader return latest if height=0 * modify LastSignedHeaderHeight to return an error if no headers exist * copy over proxy impl * refactor proxy and start http lite client * Tx and BlockchainInfo methods * Block method * commit method * code compiles again * lite client compiles * extract updateLiteClientIfNeededTo func * move final parts * add placeholder for tests * force usage of lite http client in proxy * comment out query tests for now * explicitly mention tp: trusting period * verify nextVals in VerifyHeader * refactor bisection * move the NextValidatorsHash check into updateTrustedHeaderAndVals + update the comment * add ConsensusParams method to RPC client * add ConsensusParams to rpc/mock/client * change trustLevel type to a new cmn.Fraction type + update SkippingVerification comment * stress out trustLevel is only used for non-adjusted headers * fixes after Fede's review Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * compare newHeader with a header from an alternative provider * save pivot header Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349122824 * check header can still be trusted in TrustedHeader Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349101424 * lite: update Validators and Block endpoints - Block no longer contains BlockMeta - Validators now accept two additional params: page and perPage * make linter happy
5 years ago
lite2: light client with weak subjectivity (#3989) Refs #1771 ADR: https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md ## Commits: * add Verifier and VerifyCommitTrusting * add two more checks make trustLevel an option * float32 for trustLevel * check newHeader time * started writing lite Client * unify Verify methods * ensure h2.Header.bfttime < h1.Header.bfttime + tp * move trust checks into Verify function * add more comments * more docs * started writing tests * unbonding period failures * tests are green * export ErrNewHeaderTooFarIntoFuture * make golangci happy * test for non-adjusted headers * more precision * providers and stores * VerifyHeader and VerifyHeaderAtHeight funcs * fix compile errors * remove lastVerifiedHeight, persist new trusted header * sequential verification * remove TrustedStore option * started writing tests for light client * cover basic cases for linear verification * bisection tests PASS * rename BisectingVerification to SkippingVerification * refactor the code * add TrustedHeader method * consolidate sequential verification tests * consolidate skipping verification tests * rename trustedVals to trustedNextVals * start writing docs * ValidateTrustLevel func and ErrOldHeaderExpired error * AutoClient and example tests * fix errors * update doc * remove ErrNewHeaderTooFarIntoFuture This check is unnecessary given existing a) ErrOldHeaderExpired b) h2.Time > now checks. * return an error if we're at more recent height * add comments * add LastSignedHeaderHeight method to Store I think it's fine if Store tracks last height * copy over proxy from old lite package * make TrustedHeader return latest if height=0 * modify LastSignedHeaderHeight to return an error if no headers exist * copy over proxy impl * refactor proxy and start http lite client * Tx and BlockchainInfo methods * Block method * commit method * code compiles again * lite client compiles * extract updateLiteClientIfNeededTo func * move final parts * add placeholder for tests * force usage of lite http client in proxy * comment out query tests for now * explicitly mention tp: trusting period * verify nextVals in VerifyHeader * refactor bisection * move the NextValidatorsHash check into updateTrustedHeaderAndVals + update the comment * add ConsensusParams method to RPC client * add ConsensusParams to rpc/mock/client * change trustLevel type to a new cmn.Fraction type + update SkippingVerification comment * stress out trustLevel is only used for non-adjusted headers * fixes after Fede's review Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * compare newHeader with a header from an alternative provider * save pivot header Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349122824 * check header can still be trusted in TrustedHeader Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349101424 * lite: update Validators and Block endpoints - Block no longer contains BlockMeta - Validators now accept two additional params: page and perPage * make linter happy
5 years ago
lite2: light client with weak subjectivity (#3989) Refs #1771 ADR: https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md ## Commits: * add Verifier and VerifyCommitTrusting * add two more checks make trustLevel an option * float32 for trustLevel * check newHeader time * started writing lite Client * unify Verify methods * ensure h2.Header.bfttime < h1.Header.bfttime + tp * move trust checks into Verify function * add more comments * more docs * started writing tests * unbonding period failures * tests are green * export ErrNewHeaderTooFarIntoFuture * make golangci happy * test for non-adjusted headers * more precision * providers and stores * VerifyHeader and VerifyHeaderAtHeight funcs * fix compile errors * remove lastVerifiedHeight, persist new trusted header * sequential verification * remove TrustedStore option * started writing tests for light client * cover basic cases for linear verification * bisection tests PASS * rename BisectingVerification to SkippingVerification * refactor the code * add TrustedHeader method * consolidate sequential verification tests * consolidate skipping verification tests * rename trustedVals to trustedNextVals * start writing docs * ValidateTrustLevel func and ErrOldHeaderExpired error * AutoClient and example tests * fix errors * update doc * remove ErrNewHeaderTooFarIntoFuture This check is unnecessary given existing a) ErrOldHeaderExpired b) h2.Time > now checks. * return an error if we're at more recent height * add comments * add LastSignedHeaderHeight method to Store I think it's fine if Store tracks last height * copy over proxy from old lite package * make TrustedHeader return latest if height=0 * modify LastSignedHeaderHeight to return an error if no headers exist * copy over proxy impl * refactor proxy and start http lite client * Tx and BlockchainInfo methods * Block method * commit method * code compiles again * lite client compiles * extract updateLiteClientIfNeededTo func * move final parts * add placeholder for tests * force usage of lite http client in proxy * comment out query tests for now * explicitly mention tp: trusting period * verify nextVals in VerifyHeader * refactor bisection * move the NextValidatorsHash check into updateTrustedHeaderAndVals + update the comment * add ConsensusParams method to RPC client * add ConsensusParams to rpc/mock/client * change trustLevel type to a new cmn.Fraction type + update SkippingVerification comment * stress out trustLevel is only used for non-adjusted headers * fixes after Fede's review Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * compare newHeader with a header from an alternative provider * save pivot header Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349122824 * check header can still be trusted in TrustedHeader Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349101424 * lite: update Validators and Block endpoints - Block no longer contains BlockMeta - Validators now accept two additional params: page and perPage * make linter happy
5 years ago
lite2: light client with weak subjectivity (#3989) Refs #1771 ADR: https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md ## Commits: * add Verifier and VerifyCommitTrusting * add two more checks make trustLevel an option * float32 for trustLevel * check newHeader time * started writing lite Client * unify Verify methods * ensure h2.Header.bfttime < h1.Header.bfttime + tp * move trust checks into Verify function * add more comments * more docs * started writing tests * unbonding period failures * tests are green * export ErrNewHeaderTooFarIntoFuture * make golangci happy * test for non-adjusted headers * more precision * providers and stores * VerifyHeader and VerifyHeaderAtHeight funcs * fix compile errors * remove lastVerifiedHeight, persist new trusted header * sequential verification * remove TrustedStore option * started writing tests for light client * cover basic cases for linear verification * bisection tests PASS * rename BisectingVerification to SkippingVerification * refactor the code * add TrustedHeader method * consolidate sequential verification tests * consolidate skipping verification tests * rename trustedVals to trustedNextVals * start writing docs * ValidateTrustLevel func and ErrOldHeaderExpired error * AutoClient and example tests * fix errors * update doc * remove ErrNewHeaderTooFarIntoFuture This check is unnecessary given existing a) ErrOldHeaderExpired b) h2.Time > now checks. * return an error if we're at more recent height * add comments * add LastSignedHeaderHeight method to Store I think it's fine if Store tracks last height * copy over proxy from old lite package * make TrustedHeader return latest if height=0 * modify LastSignedHeaderHeight to return an error if no headers exist * copy over proxy impl * refactor proxy and start http lite client * Tx and BlockchainInfo methods * Block method * commit method * code compiles again * lite client compiles * extract updateLiteClientIfNeededTo func * move final parts * add placeholder for tests * force usage of lite http client in proxy * comment out query tests for now * explicitly mention tp: trusting period * verify nextVals in VerifyHeader * refactor bisection * move the NextValidatorsHash check into updateTrustedHeaderAndVals + update the comment * add ConsensusParams method to RPC client * add ConsensusParams to rpc/mock/client * change trustLevel type to a new cmn.Fraction type + update SkippingVerification comment * stress out trustLevel is only used for non-adjusted headers * fixes after Fede's review Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * compare newHeader with a header from an alternative provider * save pivot header Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349122824 * check header can still be trusted in TrustedHeader Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349101424 * lite: update Validators and Block endpoints - Block no longer contains BlockMeta - Validators now accept two additional params: page and perPage * make linter happy
5 years ago
lite2: light client with weak subjectivity (#3989) Refs #1771 ADR: https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md ## Commits: * add Verifier and VerifyCommitTrusting * add two more checks make trustLevel an option * float32 for trustLevel * check newHeader time * started writing lite Client * unify Verify methods * ensure h2.Header.bfttime < h1.Header.bfttime + tp * move trust checks into Verify function * add more comments * more docs * started writing tests * unbonding period failures * tests are green * export ErrNewHeaderTooFarIntoFuture * make golangci happy * test for non-adjusted headers * more precision * providers and stores * VerifyHeader and VerifyHeaderAtHeight funcs * fix compile errors * remove lastVerifiedHeight, persist new trusted header * sequential verification * remove TrustedStore option * started writing tests for light client * cover basic cases for linear verification * bisection tests PASS * rename BisectingVerification to SkippingVerification * refactor the code * add TrustedHeader method * consolidate sequential verification tests * consolidate skipping verification tests * rename trustedVals to trustedNextVals * start writing docs * ValidateTrustLevel func and ErrOldHeaderExpired error * AutoClient and example tests * fix errors * update doc * remove ErrNewHeaderTooFarIntoFuture This check is unnecessary given existing a) ErrOldHeaderExpired b) h2.Time > now checks. * return an error if we're at more recent height * add comments * add LastSignedHeaderHeight method to Store I think it's fine if Store tracks last height * copy over proxy from old lite package * make TrustedHeader return latest if height=0 * modify LastSignedHeaderHeight to return an error if no headers exist * copy over proxy impl * refactor proxy and start http lite client * Tx and BlockchainInfo methods * Block method * commit method * code compiles again * lite client compiles * extract updateLiteClientIfNeededTo func * move final parts * add placeholder for tests * force usage of lite http client in proxy * comment out query tests for now * explicitly mention tp: trusting period * verify nextVals in VerifyHeader * refactor bisection * move the NextValidatorsHash check into updateTrustedHeaderAndVals + update the comment * add ConsensusParams method to RPC client * add ConsensusParams to rpc/mock/client * change trustLevel type to a new cmn.Fraction type + update SkippingVerification comment * stress out trustLevel is only used for non-adjusted headers * fixes after Fede's review Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * compare newHeader with a header from an alternative provider * save pivot header Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349122824 * check header can still be trusted in TrustedHeader Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349101424 * lite: update Validators and Block endpoints - Block no longer contains BlockMeta - Validators now accept two additional params: page and perPage * make linter happy
5 years ago
lite2: light client with weak subjectivity (#3989) Refs #1771 ADR: https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md ## Commits: * add Verifier and VerifyCommitTrusting * add two more checks make trustLevel an option * float32 for trustLevel * check newHeader time * started writing lite Client * unify Verify methods * ensure h2.Header.bfttime < h1.Header.bfttime + tp * move trust checks into Verify function * add more comments * more docs * started writing tests * unbonding period failures * tests are green * export ErrNewHeaderTooFarIntoFuture * make golangci happy * test for non-adjusted headers * more precision * providers and stores * VerifyHeader and VerifyHeaderAtHeight funcs * fix compile errors * remove lastVerifiedHeight, persist new trusted header * sequential verification * remove TrustedStore option * started writing tests for light client * cover basic cases for linear verification * bisection tests PASS * rename BisectingVerification to SkippingVerification * refactor the code * add TrustedHeader method * consolidate sequential verification tests * consolidate skipping verification tests * rename trustedVals to trustedNextVals * start writing docs * ValidateTrustLevel func and ErrOldHeaderExpired error * AutoClient and example tests * fix errors * update doc * remove ErrNewHeaderTooFarIntoFuture This check is unnecessary given existing a) ErrOldHeaderExpired b) h2.Time > now checks. * return an error if we're at more recent height * add comments * add LastSignedHeaderHeight method to Store I think it's fine if Store tracks last height * copy over proxy from old lite package * make TrustedHeader return latest if height=0 * modify LastSignedHeaderHeight to return an error if no headers exist * copy over proxy impl * refactor proxy and start http lite client * Tx and BlockchainInfo methods * Block method * commit method * code compiles again * lite client compiles * extract updateLiteClientIfNeededTo func * move final parts * add placeholder for tests * force usage of lite http client in proxy * comment out query tests for now * explicitly mention tp: trusting period * verify nextVals in VerifyHeader * refactor bisection * move the NextValidatorsHash check into updateTrustedHeaderAndVals + update the comment * add ConsensusParams method to RPC client * add ConsensusParams to rpc/mock/client * change trustLevel type to a new cmn.Fraction type + update SkippingVerification comment * stress out trustLevel is only used for non-adjusted headers * fixes after Fede's review Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * compare newHeader with a header from an alternative provider * save pivot header Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349122824 * check header can still be trusted in TrustedHeader Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349101424 * lite: update Validators and Block endpoints - Block no longer contains BlockMeta - Validators now accept two additional params: page and perPage * make linter happy
5 years ago
  1. package rpc
  2. import (
  3. "bytes"
  4. "context"
  5. "fmt"
  6. "strings"
  7. "time"
  8. "github.com/pkg/errors"
  9. "github.com/tendermint/tendermint/crypto/merkle"
  10. tmbytes "github.com/tendermint/tendermint/libs/bytes"
  11. service "github.com/tendermint/tendermint/libs/service"
  12. lite "github.com/tendermint/tendermint/lite2"
  13. rpcclient "github.com/tendermint/tendermint/rpc/client"
  14. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  15. rpctypes "github.com/tendermint/tendermint/rpc/lib/types"
  16. "github.com/tendermint/tendermint/types"
  17. )
  18. // Client is an RPC client, which uses lite#Client to verify data (if it can be
  19. // proved!).
  20. type Client struct {
  21. service.BaseService
  22. next rpcclient.Client
  23. lc *lite.Client
  24. prt *merkle.ProofRuntime
  25. }
  26. var _ rpcclient.Client = (*Client)(nil)
  27. // NewClient returns a new client.
  28. func NewClient(next rpcclient.Client, lc *lite.Client) *Client {
  29. c := &Client{
  30. next: next,
  31. lc: lc,
  32. prt: defaultProofRuntime(),
  33. }
  34. c.BaseService = *service.NewBaseService(nil, "Client", c)
  35. return c
  36. }
  37. func (c *Client) OnStart() error {
  38. if !c.next.IsRunning() {
  39. return c.next.Start()
  40. }
  41. return nil
  42. }
  43. func (c *Client) OnStop() {
  44. if c.next.IsRunning() {
  45. c.next.Stop()
  46. }
  47. }
  48. func (c *Client) Status() (*ctypes.ResultStatus, error) {
  49. return c.next.Status()
  50. }
  51. func (c *Client) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
  52. return c.next.ABCIInfo()
  53. }
  54. func (c *Client) ABCIQuery(path string, data tmbytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
  55. return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
  56. }
  57. // GetWithProofOptions is useful if you want full access to the ABCIQueryOptions.
  58. // XXX Usage of path? It's not used, and sometimes it's /, sometimes /key, sometimes /store.
  59. func (c *Client) ABCIQueryWithOptions(path string, data tmbytes.HexBytes,
  60. opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
  61. res, err := c.next.ABCIQueryWithOptions(path, data, opts)
  62. if err != nil {
  63. return nil, err
  64. }
  65. resp := res.Response
  66. // Validate the response.
  67. if resp.IsErr() {
  68. return nil, errors.Errorf("err response code: %v", resp.Code)
  69. }
  70. if len(resp.Key) == 0 || resp.Proof == nil {
  71. return nil, errors.New("empty tree")
  72. }
  73. if resp.Height <= 0 {
  74. return nil, errors.New("negative or zero height")
  75. }
  76. // Update the light client if we're behind.
  77. if err := c.updateLiteClientIfNeededTo(resp.Height + 1); err != nil {
  78. return nil, err
  79. }
  80. // AppHash for height H is in header H+1.
  81. h, err := c.lc.TrustedHeader(resp.Height+1, time.Now())
  82. if err != nil {
  83. return nil, errors.Wrapf(err, "TrustedHeader(%d)", resp.Height+1)
  84. }
  85. // Validate the value proof against the trusted header.
  86. if resp.Value != nil {
  87. // Value exists
  88. // XXX How do we encode the key into a string...
  89. storeName, err := parseQueryStorePath(path)
  90. if err != nil {
  91. return nil, err
  92. }
  93. kp := merkle.KeyPath{}
  94. kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL)
  95. kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL)
  96. err = c.prt.VerifyValue(resp.Proof, h.AppHash, kp.String(), resp.Value)
  97. if err != nil {
  98. return nil, errors.Wrap(err, "verify value proof")
  99. }
  100. return &ctypes.ResultABCIQuery{Response: resp}, nil
  101. }
  102. // OR validate the ansence proof against the trusted header.
  103. // XXX How do we encode the key into a string...
  104. err = c.prt.VerifyAbsence(resp.Proof, h.AppHash, string(resp.Key))
  105. if err != nil {
  106. return nil, errors.Wrap(err, "verify absence proof")
  107. }
  108. return &ctypes.ResultABCIQuery{Response: resp}, nil
  109. }
  110. func (c *Client) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
  111. return c.next.BroadcastTxCommit(tx)
  112. }
  113. func (c *Client) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  114. return c.next.BroadcastTxAsync(tx)
  115. }
  116. func (c *Client) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  117. return c.next.BroadcastTxSync(tx)
  118. }
  119. func (c *Client) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) {
  120. return c.next.UnconfirmedTxs(limit)
  121. }
  122. func (c *Client) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
  123. return c.next.NumUnconfirmedTxs()
  124. }
  125. func (c *Client) NetInfo() (*ctypes.ResultNetInfo, error) {
  126. return c.next.NetInfo()
  127. }
  128. func (c *Client) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
  129. return c.next.DumpConsensusState()
  130. }
  131. func (c *Client) ConsensusState() (*ctypes.ResultConsensusState, error) {
  132. return c.next.ConsensusState()
  133. }
  134. func (c *Client) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) {
  135. return c.next.ConsensusParams(height)
  136. }
  137. func (c *Client) Health() (*ctypes.ResultHealth, error) {
  138. return c.next.Health()
  139. }
  140. // BlockchainInfo calls rpcclient#BlockchainInfo and then verifies every header
  141. // returned.
  142. func (c *Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
  143. res, err := c.next.BlockchainInfo(minHeight, maxHeight)
  144. if err != nil {
  145. return nil, err
  146. }
  147. // Validate res.
  148. for _, meta := range res.BlockMetas {
  149. if meta == nil {
  150. return nil, errors.New("nil BlockMeta")
  151. }
  152. if err := meta.ValidateBasic(); err != nil {
  153. return nil, errors.Wrap(err, "invalid BlockMeta")
  154. }
  155. }
  156. // Update the light client if we're behind.
  157. if len(res.BlockMetas) > 0 {
  158. lastHeight := res.BlockMetas[len(res.BlockMetas)-1].Header.Height
  159. if err := c.updateLiteClientIfNeededTo(lastHeight); err != nil {
  160. return nil, err
  161. }
  162. }
  163. // Verify each of the BlockMetas.
  164. for _, meta := range res.BlockMetas {
  165. h, err := c.lc.TrustedHeader(meta.Header.Height, time.Now())
  166. if err != nil {
  167. return nil, errors.Wrapf(err, "TrustedHeader(%d)", meta.Header.Height)
  168. }
  169. if bmH, tH := meta.Header.Hash(), h.Hash(); !bytes.Equal(bmH, tH) {
  170. return nil, errors.Errorf("BlockMeta#Header %X does not match with trusted header %X",
  171. bmH, tH)
  172. }
  173. }
  174. return res, nil
  175. }
  176. func (c *Client) Genesis() (*ctypes.ResultGenesis, error) {
  177. return c.next.Genesis()
  178. }
  179. // Block calls rpcclient#Block and then verifies the result.
  180. func (c *Client) Block(height *int64) (*ctypes.ResultBlock, error) {
  181. res, err := c.next.Block(height)
  182. if err != nil {
  183. return nil, err
  184. }
  185. // Validate res.
  186. if err := res.BlockID.ValidateBasic(); err != nil {
  187. return nil, err
  188. }
  189. if err := res.Block.ValidateBasic(); err != nil {
  190. return nil, err
  191. }
  192. if bmH, bH := res.BlockID.Hash, res.Block.Hash(); !bytes.Equal(bmH, bH) {
  193. return nil, errors.Errorf("BlockID %X does not match with Block %X",
  194. bmH, bH)
  195. }
  196. // Update the light client if we're behind.
  197. if err := c.updateLiteClientIfNeededTo(res.Block.Height); err != nil {
  198. return nil, err
  199. }
  200. // Verify block.
  201. h, err := c.lc.TrustedHeader(res.Block.Height, time.Now())
  202. if err != nil {
  203. return nil, errors.Wrapf(err, "TrustedHeader(%d)", res.Block.Height)
  204. }
  205. if bH, tH := res.Block.Hash(), h.Hash(); !bytes.Equal(bH, tH) {
  206. return nil, errors.Errorf("Block#Header %X does not match with trusted header %X",
  207. bH, tH)
  208. }
  209. return res, nil
  210. }
  211. func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
  212. return c.next.BlockResults(height)
  213. }
  214. func (c *Client) Commit(height *int64) (*ctypes.ResultCommit, error) {
  215. res, err := c.next.Commit(height)
  216. if err != nil {
  217. return nil, err
  218. }
  219. // Validate res.
  220. if err := res.SignedHeader.ValidateBasic(c.lc.ChainID()); err != nil {
  221. return nil, err
  222. }
  223. // Update the light client if we're behind.
  224. if err := c.updateLiteClientIfNeededTo(res.Height); err != nil {
  225. return nil, err
  226. }
  227. // Verify commit.
  228. h, err := c.lc.TrustedHeader(res.Height, time.Now())
  229. if err != nil {
  230. return nil, errors.Wrapf(err, "TrustedHeader(%d)", res.Height)
  231. }
  232. if rH, tH := res.Hash(), h.Hash(); !bytes.Equal(rH, tH) {
  233. return nil, errors.Errorf("header %X does not match with trusted header %X",
  234. rH, tH)
  235. }
  236. return res, nil
  237. }
  238. // Tx calls rpcclient#Tx method and then verifies the proof if such was
  239. // requested.
  240. func (c *Client) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
  241. res, err := c.next.Tx(hash, prove)
  242. if err != nil || !prove {
  243. return res, err
  244. }
  245. // Validate res.
  246. if res.Height <= 0 {
  247. return nil, errors.Errorf("invalid ResultTx: %v", res)
  248. }
  249. // Update the light client if we're behind.
  250. if err := c.updateLiteClientIfNeededTo(res.Height); err != nil {
  251. return nil, err
  252. }
  253. // Validate the proof.
  254. h, err := c.lc.TrustedHeader(res.Height, time.Now())
  255. if err != nil {
  256. return res, errors.Wrapf(err, "TrustedHeader(%d)", res.Height)
  257. }
  258. return res, res.Proof.Validate(h.DataHash)
  259. }
  260. func (c *Client) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
  261. return c.next.TxSearch(query, prove, page, perPage)
  262. }
  263. func (c *Client) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) {
  264. return c.next.Validators(height, page, perPage)
  265. }
  266. func (c *Client) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
  267. return c.next.BroadcastEvidence(ev)
  268. }
  269. func (c *Client) Subscribe(ctx context.Context, subscriber, query string,
  270. outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) {
  271. return c.next.Subscribe(ctx, subscriber, query, outCapacity...)
  272. }
  273. func (c *Client) Unsubscribe(ctx context.Context, subscriber, query string) error {
  274. return c.next.Unsubscribe(ctx, subscriber, query)
  275. }
  276. func (c *Client) UnsubscribeAll(ctx context.Context, subscriber string) error {
  277. return c.next.UnsubscribeAll(ctx, subscriber)
  278. }
  279. func (c *Client) updateLiteClientIfNeededTo(height int64) error {
  280. lastTrustedHeight, err := c.lc.LastTrustedHeight()
  281. if err != nil {
  282. return errors.Wrap(err, "LastTrustedHeight")
  283. }
  284. if lastTrustedHeight < height {
  285. if err := c.lc.VerifyHeaderAtHeight(height, time.Now()); err != nil {
  286. return errors.Wrapf(err, "VerifyHeaderAtHeight(%d)", height)
  287. }
  288. }
  289. return nil
  290. }
  291. func (c *Client) RegisterOpDecoder(typ string, dec merkle.OpDecoder) {
  292. c.prt.RegisterOpDecoder(typ, dec)
  293. }
  294. // SubscribeWS subscribes for events using the given query and remote address as
  295. // a subscriber, but does not verify responses (UNSAFE)!
  296. // TODO: verify data
  297. func (c *Client) SubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) {
  298. out, err := c.next.Subscribe(context.Background(), ctx.RemoteAddr(), query)
  299. if err != nil {
  300. return nil, err
  301. }
  302. go func() {
  303. for {
  304. select {
  305. case resultEvent := <-out:
  306. // We should have a switch here that performs a validation
  307. // depending on the event's type.
  308. ctx.WSConn.TryWriteRPCResponse(
  309. rpctypes.NewRPCSuccessResponse(
  310. ctx.WSConn.Codec(),
  311. rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", ctx.JSONReq.ID)),
  312. resultEvent,
  313. ))
  314. case <-c.Quit():
  315. return
  316. }
  317. }
  318. }()
  319. return &ctypes.ResultSubscribe{}, nil
  320. }
  321. // UnsubscribeWS calls original client's Unsubscribe using remote address as a
  322. // subscriber.
  323. func (c *Client) UnsubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) {
  324. err := c.next.Unsubscribe(context.Background(), ctx.RemoteAddr(), query)
  325. if err != nil {
  326. return nil, err
  327. }
  328. return &ctypes.ResultUnsubscribe{}, nil
  329. }
  330. // UnsubscribeAllWS calls original client's UnsubscribeAll using remote address
  331. // as a subscriber.
  332. func (c *Client) UnsubscribeAllWS(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) {
  333. err := c.next.UnsubscribeAll(context.Background(), ctx.RemoteAddr())
  334. if err != nil {
  335. return nil, err
  336. }
  337. return &ctypes.ResultUnsubscribe{}, nil
  338. }
  339. func parseQueryStorePath(path string) (storeName string, err error) {
  340. if !strings.HasPrefix(path, "/") {
  341. return "", fmt.Errorf("expected path to start with /")
  342. }
  343. paths := strings.SplitN(path[1:], "/", 3)
  344. switch {
  345. case len(paths) != 3:
  346. return "", errors.New("expected format like /store/<storeName>/key")
  347. case paths[0] != "store":
  348. return "", errors.New("expected format like /store/<storeName>/key")
  349. case paths[2] != "key":
  350. return "", errors.New("expected format like /store/<storeName>/key")
  351. }
  352. return paths[1], nil
  353. }