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.

205 lines
6.1 KiB

7 years ago
7 years ago
  1. package proxy
  2. import (
  3. cmn "github.com/tendermint/tendermint/libs/common"
  4. "github.com/tendermint/tendermint/crypto/merkle"
  5. "github.com/tendermint/tendermint/lite"
  6. rpcclient "github.com/tendermint/tendermint/rpc/client"
  7. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  8. )
  9. var _ rpcclient.Client = Wrapper{}
  10. // Wrapper wraps a rpcclient with a Verifier and double-checks any input that is
  11. // provable before passing it along. Allows you to make any rpcclient fully secure.
  12. type Wrapper struct {
  13. rpcclient.Client
  14. cert *lite.DynamicVerifier
  15. prt *merkle.ProofRuntime
  16. }
  17. // SecureClient uses a given Verifier to wrap an connection to an untrusted
  18. // host and return a cryptographically secure rpc client.
  19. //
  20. // If it is wrapping an HTTP rpcclient, it will also wrap the websocket interface
  21. func SecureClient(c rpcclient.Client, cert *lite.DynamicVerifier) Wrapper {
  22. prt := defaultProofRuntime()
  23. wrap := Wrapper{c, cert, prt}
  24. // TODO: no longer possible as no more such interface exposed....
  25. // if we wrap http client, then we can swap out the event switch to filter
  26. // if hc, ok := c.(*rpcclient.HTTP); ok {
  27. // evt := hc.WSEvents.EventSwitch
  28. // hc.WSEvents.EventSwitch = WrappedSwitch{evt, wrap}
  29. // }
  30. return wrap
  31. }
  32. // ABCIQueryWithOptions exposes all options for the ABCI query and verifies the returned proof
  33. func (w Wrapper) ABCIQueryWithOptions(path string, data cmn.HexBytes,
  34. opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
  35. res, err := GetWithProofOptions(w.prt, path, data, opts, w.Client, w.cert)
  36. return res, err
  37. }
  38. // ABCIQuery uses default options for the ABCI query and verifies the returned proof
  39. func (w Wrapper) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) {
  40. return w.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
  41. }
  42. // Tx queries for a given tx and verifies the proof if it was requested
  43. func (w Wrapper) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
  44. res, err := w.Client.Tx(hash, prove)
  45. if !prove || err != nil {
  46. return res, err
  47. }
  48. h := int64(res.Height)
  49. sh, err := GetCertifiedCommit(h, w.Client, w.cert)
  50. if err != nil {
  51. return res, err
  52. }
  53. err = res.Proof.Validate(sh.DataHash)
  54. return res, err
  55. }
  56. // BlockchainInfo requests a list of headers and verifies them all...
  57. // Rather expensive.
  58. //
  59. // TODO: optimize this if used for anything needing performance
  60. func (w Wrapper) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
  61. r, err := w.Client.BlockchainInfo(minHeight, maxHeight)
  62. if err != nil {
  63. return nil, err
  64. }
  65. // go and verify every blockmeta in the result....
  66. for _, meta := range r.BlockMetas {
  67. // get a checkpoint to verify from
  68. res, err := w.Commit(&meta.Header.Height)
  69. if err != nil {
  70. return nil, err
  71. }
  72. sh := res.SignedHeader
  73. err = ValidateBlockMeta(meta, sh)
  74. if err != nil {
  75. return nil, err
  76. }
  77. }
  78. return r, nil
  79. }
  80. // Block returns an entire block and verifies all signatures
  81. func (w Wrapper) Block(height *int64) (*ctypes.ResultBlock, error) {
  82. resBlock, err := w.Client.Block(height)
  83. if err != nil {
  84. return nil, err
  85. }
  86. // get a checkpoint to verify from
  87. resCommit, err := w.Commit(height)
  88. if err != nil {
  89. return nil, err
  90. }
  91. sh := resCommit.SignedHeader
  92. // now verify
  93. err = ValidateBlockMeta(resBlock.BlockMeta, sh)
  94. if err != nil {
  95. return nil, err
  96. }
  97. err = ValidateBlock(resBlock.Block, sh)
  98. if err != nil {
  99. return nil, err
  100. }
  101. return resBlock, nil
  102. }
  103. // Commit downloads the Commit and certifies it with the lite.
  104. //
  105. // This is the foundation for all other verification in this module
  106. func (w Wrapper) Commit(height *int64) (*ctypes.ResultCommit, error) {
  107. if height == nil {
  108. resStatus, err := w.Client.Status()
  109. if err != nil {
  110. return nil, err
  111. }
  112. // NOTE: If resStatus.CatchingUp, there is a race
  113. // condition where the validator set for the next height
  114. // isn't available until some time after the blockstore
  115. // has height h on the remote node. This isn't an issue
  116. // once the node has caught up, and a syncing node likely
  117. // won't have this issue esp with the implementation we
  118. // have here, but we may have to address this at some
  119. // point.
  120. height = new(int64)
  121. *height = resStatus.SyncInfo.LatestBlockHeight
  122. }
  123. rpcclient.WaitForHeight(w.Client, *height, nil)
  124. res, err := w.Client.Commit(height)
  125. // if we got it, then verify it
  126. if err == nil {
  127. sh := res.SignedHeader
  128. err = w.cert.Verify(sh)
  129. }
  130. return res, err
  131. }
  132. // // WrappedSwitch creates a websocket connection that auto-verifies any info
  133. // // coming through before passing it along.
  134. // //
  135. // // Since the verification takes 1-2 rpc calls, this is obviously only for
  136. // // relatively low-throughput situations that can tolerate a bit extra latency
  137. // type WrappedSwitch struct {
  138. // types.EventSwitch
  139. // client rpcclient.Client
  140. // }
  141. // // FireEvent verifies any block or header returned from the eventswitch
  142. // func (s WrappedSwitch) FireEvent(event string, data events.EventData) {
  143. // tm, ok := data.(types.TMEventData)
  144. // if !ok {
  145. // fmt.Printf("bad type %#v\n", data)
  146. // return
  147. // }
  148. // // check to validate it if possible, and drop if not valid
  149. // switch t := tm.(type) {
  150. // case types.EventDataNewBlockHeader:
  151. // err := verifyHeader(s.client, t.Header)
  152. // if err != nil {
  153. // fmt.Printf("Invalid header: %#v\n", err)
  154. // return
  155. // }
  156. // case types.EventDataNewBlock:
  157. // err := verifyBlock(s.client, t.Block)
  158. // if err != nil {
  159. // fmt.Printf("Invalid block: %#v\n", err)
  160. // return
  161. // }
  162. // // TODO: can we verify tx as well? anything else
  163. // }
  164. // // looks good, we fire it
  165. // s.EventSwitch.FireEvent(event, data)
  166. // }
  167. // func verifyHeader(c rpcclient.Client, head *types.Header) error {
  168. // // get a checkpoint to verify from
  169. // commit, err := c.Commit(&head.Height)
  170. // if err != nil {
  171. // return err
  172. // }
  173. // check := certclient.CommitFromResult(commit)
  174. // return ValidateHeader(head, check)
  175. // }
  176. //
  177. // func verifyBlock(c rpcclient.Client, block *types.Block) error {
  178. // // get a checkpoint to verify from
  179. // commit, err := c.Commit(&block.Height)
  180. // if err != nil {
  181. // return err
  182. // }
  183. // check := certclient.CommitFromResult(commit)
  184. // return ValidateBlock(block, check)
  185. // }