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.

187 lines
5.5 KiB

  1. package proxy
  2. import (
  3. "github.com/tendermint/go-wire/data"
  4. "github.com/tendermint/tendermint/lite"
  5. certclient "github.com/tendermint/tendermint/lite/client"
  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 Certifier 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.InquiringCertifier
  15. }
  16. // SecureClient uses a given certifier to wrap an connection to an untrusted
  17. // host and return a cryptographically secure rpc client.
  18. //
  19. // If it is wrapping an HTTP rpcclient, it will also wrap the websocket interface
  20. func SecureClient(c rpcclient.Client, cert *lite.InquiringCertifier) Wrapper {
  21. wrap := Wrapper{c, cert}
  22. // TODO: no longer possible as no more such interface exposed....
  23. // if we wrap http client, then we can swap out the event switch to filter
  24. // if hc, ok := c.(*rpcclient.HTTP); ok {
  25. // evt := hc.WSEvents.EventSwitch
  26. // hc.WSEvents.EventSwitch = WrappedSwitch{evt, wrap}
  27. // }
  28. return wrap
  29. }
  30. // ABCIQueryWithOptions exposes all options for the ABCI query and verifies the returned proof
  31. func (w Wrapper) ABCIQueryWithOptions(path string, data data.Bytes,
  32. opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
  33. res, _, err := GetWithProofOptions(path, data, opts, w.Client, w.cert)
  34. return res, err
  35. }
  36. // ABCIQuery uses default options for the ABCI query and verifies the returned proof
  37. func (w Wrapper) ABCIQuery(path string, data data.Bytes) (*ctypes.ResultABCIQuery, error) {
  38. return w.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
  39. }
  40. // Tx queries for a given tx and verifies the proof if it was requested
  41. func (w Wrapper) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
  42. res, err := w.Client.Tx(hash, prove)
  43. if !prove || err != nil {
  44. return res, err
  45. }
  46. h := int64(res.Height)
  47. check, err := GetCertifiedCommit(h, w.Client, w.cert)
  48. if err != nil {
  49. return res, err
  50. }
  51. err = res.Proof.Validate(check.Header.DataHash)
  52. return res, err
  53. }
  54. // BlockchainInfo requests a list of headers and verifies them all...
  55. // Rather expensive.
  56. //
  57. // TODO: optimize this if used for anything needing performance
  58. func (w Wrapper) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
  59. r, err := w.Client.BlockchainInfo(minHeight, maxHeight)
  60. if err != nil {
  61. return nil, err
  62. }
  63. // go and verify every blockmeta in the result....
  64. for _, meta := range r.BlockMetas {
  65. // get a checkpoint to verify from
  66. c, err := w.Commit(&meta.Header.Height)
  67. if err != nil {
  68. return nil, err
  69. }
  70. check := certclient.CommitFromResult(c)
  71. err = ValidateBlockMeta(meta, check)
  72. if err != nil {
  73. return nil, err
  74. }
  75. }
  76. return r, nil
  77. }
  78. // Block returns an entire block and verifies all signatures
  79. func (w Wrapper) Block(height *int64) (*ctypes.ResultBlock, error) {
  80. r, err := w.Client.Block(height)
  81. if err != nil {
  82. return nil, err
  83. }
  84. // get a checkpoint to verify from
  85. c, err := w.Commit(height)
  86. if err != nil {
  87. return nil, err
  88. }
  89. check := certclient.CommitFromResult(c)
  90. // now verify
  91. err = ValidateBlockMeta(r.BlockMeta, check)
  92. if err != nil {
  93. return nil, err
  94. }
  95. err = ValidateBlock(r.Block, check)
  96. if err != nil {
  97. return nil, err
  98. }
  99. return r, nil
  100. }
  101. // Commit downloads the Commit and certifies it with the lite.
  102. //
  103. // This is the foundation for all other verification in this module
  104. func (w Wrapper) Commit(height *int64) (*ctypes.ResultCommit, error) {
  105. rpcclient.WaitForHeight(w.Client, *height, nil)
  106. r, err := w.Client.Commit(height)
  107. // if we got it, then certify it
  108. if err == nil {
  109. check := certclient.CommitFromResult(r)
  110. err = w.cert.Certify(check)
  111. }
  112. return r, err
  113. }
  114. // // WrappedSwitch creates a websocket connection that auto-verifies any info
  115. // // coming through before passing it along.
  116. // //
  117. // // Since the verification takes 1-2 rpc calls, this is obviously only for
  118. // // relatively low-throughput situations that can tolerate a bit extra latency
  119. // type WrappedSwitch struct {
  120. // types.EventSwitch
  121. // client rpcclient.Client
  122. // }
  123. // // FireEvent verifies any block or header returned from the eventswitch
  124. // func (s WrappedSwitch) FireEvent(event string, data events.EventData) {
  125. // tm, ok := data.(types.TMEventData)
  126. // if !ok {
  127. // fmt.Printf("bad type %#v\n", data)
  128. // return
  129. // }
  130. // // check to validate it if possible, and drop if not valid
  131. // switch t := tm.Unwrap().(type) {
  132. // case types.EventDataNewBlockHeader:
  133. // err := verifyHeader(s.client, t.Header)
  134. // if err != nil {
  135. // fmt.Printf("Invalid header: %#v\n", err)
  136. // return
  137. // }
  138. // case types.EventDataNewBlock:
  139. // err := verifyBlock(s.client, t.Block)
  140. // if err != nil {
  141. // fmt.Printf("Invalid block: %#v\n", err)
  142. // return
  143. // }
  144. // // TODO: can we verify tx as well? anything else
  145. // }
  146. // // looks good, we fire it
  147. // s.EventSwitch.FireEvent(event, data)
  148. // }
  149. // func verifyHeader(c rpcclient.Client, head *types.Header) error {
  150. // // get a checkpoint to verify from
  151. // commit, err := c.Commit(&head.Height)
  152. // if err != nil {
  153. // return err
  154. // }
  155. // check := certclient.CommitFromResult(commit)
  156. // return ValidateHeader(head, check)
  157. // }
  158. //
  159. // func verifyBlock(c rpcclient.Client, block *types.Block) error {
  160. // // get a checkpoint to verify from
  161. // commit, err := c.Commit(&block.Height)
  162. // if err != nil {
  163. // return err
  164. // }
  165. // check := certclient.CommitFromResult(commit)
  166. // return ValidateBlock(block, check)
  167. // }