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.

418 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
  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. cmn "github.com/tendermint/tendermint/libs/common"
  11. lite "github.com/tendermint/tendermint/lite2"
  12. rpcclient "github.com/tendermint/tendermint/rpc/client"
  13. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  14. rpctypes "github.com/tendermint/tendermint/rpc/lib/types"
  15. "github.com/tendermint/tendermint/types"
  16. )
  17. // Client is an RPC client, which uses lite#Client to verify data (if it can be
  18. // proved!).
  19. type Client struct {
  20. cmn.BaseService
  21. next rpcclient.Client
  22. lc *lite.Client
  23. prt *merkle.ProofRuntime
  24. }
  25. var _ rpcclient.Client = (*Client)(nil)
  26. // NewClient returns a new client.
  27. func NewClient(next rpcclient.Client, lc *lite.Client) *Client {
  28. c := &Client{
  29. next: next,
  30. lc: lc,
  31. prt: defaultProofRuntime(),
  32. }
  33. c.BaseService = *cmn.NewBaseService(nil, "Client", c)
  34. return c
  35. }
  36. func (c *Client) OnStart() error {
  37. if !c.next.IsRunning() {
  38. return c.next.Start()
  39. }
  40. return nil
  41. }
  42. func (c *Client) OnStop() {
  43. if c.next.IsRunning() {
  44. c.next.Stop()
  45. }
  46. }
  47. func (c *Client) Status() (*ctypes.ResultStatus, error) {
  48. return c.next.Status()
  49. }
  50. func (c *Client) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
  51. return c.next.ABCIInfo()
  52. }
  53. func (c *Client) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) {
  54. return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
  55. }
  56. // GetWithProofOptions is useful if you want full access to the ABCIQueryOptions.
  57. // XXX Usage of path? It's not used, and sometimes it's /, sometimes /key, sometimes /store.
  58. func (c *Client) ABCIQueryWithOptions(path string, data cmn.HexBytes,
  59. opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
  60. res, err := c.next.ABCIQueryWithOptions(path, data, opts)
  61. if err != nil {
  62. return nil, err
  63. }
  64. resp := res.Response
  65. // Validate the response.
  66. if resp.IsErr() {
  67. return nil, errors.Errorf("err response code: %v", resp.Code)
  68. }
  69. if len(resp.Key) == 0 || resp.Proof == nil {
  70. return nil, errors.New("empty tree")
  71. }
  72. if resp.Height <= 0 {
  73. return nil, errors.New("negative or zero height")
  74. }
  75. // Update the light client if we're behind.
  76. if err := c.updateLiteClientIfNeededTo(resp.Height + 1); err != nil {
  77. return nil, err
  78. }
  79. // AppHash for height H is in header H+1.
  80. h, err := c.lc.TrustedHeader(resp.Height+1, time.Now())
  81. if err != nil {
  82. return nil, errors.Wrapf(err, "TrustedHeader(%d)", resp.Height+1)
  83. }
  84. // Validate the value proof against the trusted header.
  85. if resp.Value != nil {
  86. // Value exists
  87. // XXX How do we encode the key into a string...
  88. storeName, err := parseQueryStorePath(path)
  89. if err != nil {
  90. return nil, err
  91. }
  92. kp := merkle.KeyPath{}
  93. kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL)
  94. kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL)
  95. err = c.prt.VerifyValue(resp.Proof, h.AppHash, kp.String(), resp.Value)
  96. if err != nil {
  97. return nil, errors.Wrap(err, "verify value proof")
  98. }
  99. return &ctypes.ResultABCIQuery{Response: resp}, nil
  100. }
  101. // OR validate the ansence proof against the trusted header.
  102. // XXX How do we encode the key into a string...
  103. err = c.prt.VerifyAbsence(resp.Proof, h.AppHash, string(resp.Key))
  104. if err != nil {
  105. return nil, errors.Wrap(err, "verify absence proof")
  106. }
  107. return &ctypes.ResultABCIQuery{Response: resp}, nil
  108. }
  109. func (c *Client) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
  110. return c.next.BroadcastTxCommit(tx)
  111. }
  112. func (c *Client) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  113. return c.next.BroadcastTxAsync(tx)
  114. }
  115. func (c *Client) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  116. return c.next.BroadcastTxSync(tx)
  117. }
  118. func (c *Client) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) {
  119. return c.next.UnconfirmedTxs(limit)
  120. }
  121. func (c *Client) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
  122. return c.next.NumUnconfirmedTxs()
  123. }
  124. func (c *Client) NetInfo() (*ctypes.ResultNetInfo, error) {
  125. return c.next.NetInfo()
  126. }
  127. func (c *Client) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
  128. return c.next.DumpConsensusState()
  129. }
  130. func (c *Client) ConsensusState() (*ctypes.ResultConsensusState, error) {
  131. return c.next.ConsensusState()
  132. }
  133. func (c *Client) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) {
  134. return c.next.ConsensusParams(height)
  135. }
  136. func (c *Client) Health() (*ctypes.ResultHealth, error) {
  137. return c.next.Health()
  138. }
  139. // BlockchainInfo calls rpcclient#BlockchainInfo and then verifies every header
  140. // returned.
  141. func (c *Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
  142. res, err := c.next.BlockchainInfo(minHeight, maxHeight)
  143. if err != nil {
  144. return nil, err
  145. }
  146. // Validate res.
  147. for _, meta := range res.BlockMetas {
  148. if meta == nil {
  149. return nil, errors.New("nil BlockMeta")
  150. }
  151. if err := meta.ValidateBasic(); err != nil {
  152. return nil, errors.Wrap(err, "invalid BlockMeta")
  153. }
  154. }
  155. // Update the light client if we're behind.
  156. if len(res.BlockMetas) > 0 {
  157. lastHeight := res.BlockMetas[len(res.BlockMetas)-1].Header.Height
  158. if err := c.updateLiteClientIfNeededTo(lastHeight); err != nil {
  159. return nil, err
  160. }
  161. }
  162. // Verify each of the BlockMetas.
  163. for _, meta := range res.BlockMetas {
  164. h, err := c.lc.TrustedHeader(meta.Header.Height, time.Now())
  165. if err != nil {
  166. return nil, errors.Wrapf(err, "TrustedHeader(%d)", meta.Header.Height)
  167. }
  168. if bmH, tH := meta.Header.Hash(), h.Hash(); !bytes.Equal(bmH, tH) {
  169. return nil, errors.Errorf("BlockMeta#Header %X does not match with trusted header %X",
  170. bmH, tH)
  171. }
  172. }
  173. return res, nil
  174. }
  175. func (c *Client) Genesis() (*ctypes.ResultGenesis, error) {
  176. return c.next.Genesis()
  177. }
  178. // Block calls rpcclient#Block and then verifies the result.
  179. func (c *Client) Block(height *int64) (*ctypes.ResultBlock, error) {
  180. res, err := c.next.Block(height)
  181. if err != nil {
  182. return nil, err
  183. }
  184. // Validate res.
  185. if err := res.BlockID.ValidateBasic(); err != nil {
  186. return nil, err
  187. }
  188. if err := res.Block.ValidateBasic(); err != nil {
  189. return nil, err
  190. }
  191. if bmH, bH := res.BlockID.Hash, res.Block.Hash(); !bytes.Equal(bmH, bH) {
  192. return nil, errors.Errorf("BlockID %X does not match with Block %X",
  193. bmH, bH)
  194. }
  195. // Update the light client if we're behind.
  196. if err := c.updateLiteClientIfNeededTo(res.Block.Height); err != nil {
  197. return nil, err
  198. }
  199. // Verify block.
  200. h, err := c.lc.TrustedHeader(res.Block.Height, time.Now())
  201. if err != nil {
  202. return nil, errors.Wrapf(err, "TrustedHeader(%d)", res.Block.Height)
  203. }
  204. if bH, tH := res.Block.Hash(), h.Hash(); !bytes.Equal(bH, tH) {
  205. return nil, errors.Errorf("Block#Header %X does not match with trusted header %X",
  206. bH, tH)
  207. }
  208. return res, nil
  209. }
  210. func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
  211. return c.next.BlockResults(height)
  212. }
  213. func (c *Client) Commit(height *int64) (*ctypes.ResultCommit, error) {
  214. res, err := c.next.Commit(height)
  215. if err != nil {
  216. return nil, err
  217. }
  218. // Validate res.
  219. if err := res.SignedHeader.ValidateBasic(c.lc.ChainID()); err != nil {
  220. return nil, err
  221. }
  222. // Update the light client if we're behind.
  223. if err := c.updateLiteClientIfNeededTo(res.Height); err != nil {
  224. return nil, err
  225. }
  226. // Verify commit.
  227. h, err := c.lc.TrustedHeader(res.Height, time.Now())
  228. if err != nil {
  229. return nil, errors.Wrapf(err, "TrustedHeader(%d)", res.Height)
  230. }
  231. if rH, tH := res.Hash(), h.Hash(); !bytes.Equal(rH, tH) {
  232. return nil, errors.Errorf("header %X does not match with trusted header %X",
  233. rH, tH)
  234. }
  235. return res, nil
  236. }
  237. // Tx calls rpcclient#Tx method and then verifies the proof if such was
  238. // requested.
  239. func (c *Client) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
  240. res, err := c.next.Tx(hash, prove)
  241. if err != nil || !prove {
  242. return res, err
  243. }
  244. // Validate res.
  245. if res.Height <= 0 {
  246. return nil, errors.Errorf("invalid ResultTx: %v", res)
  247. }
  248. // Update the light client if we're behind.
  249. if err := c.updateLiteClientIfNeededTo(res.Height); err != nil {
  250. return nil, err
  251. }
  252. // Validate the proof.
  253. h, err := c.lc.TrustedHeader(res.Height, time.Now())
  254. if err != nil {
  255. return res, errors.Wrapf(err, "TrustedHeader(%d)", res.Height)
  256. }
  257. return res, res.Proof.Validate(h.DataHash)
  258. }
  259. func (c *Client) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
  260. return c.next.TxSearch(query, prove, page, perPage)
  261. }
  262. func (c *Client) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) {
  263. return c.next.Validators(height, page, perPage)
  264. }
  265. func (c *Client) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
  266. return c.next.BroadcastEvidence(ev)
  267. }
  268. func (c *Client) Subscribe(ctx context.Context, subscriber, query string,
  269. outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) {
  270. return c.next.Subscribe(ctx, subscriber, query, outCapacity...)
  271. }
  272. func (c *Client) Unsubscribe(ctx context.Context, subscriber, query string) error {
  273. return c.next.Unsubscribe(ctx, subscriber, query)
  274. }
  275. func (c *Client) UnsubscribeAll(ctx context.Context, subscriber string) error {
  276. return c.next.UnsubscribeAll(ctx, subscriber)
  277. }
  278. func (c *Client) updateLiteClientIfNeededTo(height int64) error {
  279. lastTrustedHeight, err := c.lc.LastTrustedHeight()
  280. if err != nil {
  281. return errors.Wrap(err, "LastTrustedHeight")
  282. }
  283. if lastTrustedHeight < height {
  284. if err := c.lc.VerifyHeaderAtHeight(height, time.Now()); err != nil {
  285. return errors.Wrapf(err, "VerifyHeaderAtHeight(%d)", height)
  286. }
  287. }
  288. return nil
  289. }
  290. func (c *Client) RegisterOpDecoder(typ string, dec merkle.OpDecoder) {
  291. c.prt.RegisterOpDecoder(typ, dec)
  292. }
  293. // SubscribeWS subscribes for events using the given query and remote address as
  294. // a subscriber, but does not verify responses (UNSAFE)!
  295. // TODO: verify data
  296. func (c *Client) SubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) {
  297. out, err := c.next.Subscribe(context.Background(), ctx.RemoteAddr(), query)
  298. if err != nil {
  299. return nil, err
  300. }
  301. go func() {
  302. for {
  303. select {
  304. case resultEvent := <-out:
  305. // We should have a switch here that performs a validation
  306. // depending on the event's type.
  307. ctx.WSConn.TryWriteRPCResponse(
  308. rpctypes.NewRPCSuccessResponse(
  309. ctx.WSConn.Codec(),
  310. rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", ctx.JSONReq.ID)),
  311. resultEvent,
  312. ))
  313. case <-c.Quit():
  314. return
  315. }
  316. }
  317. }()
  318. return &ctypes.ResultSubscribe{}, nil
  319. }
  320. // UnsubscribeWS calls original client's Unsubscribe using remote address as a
  321. // subscriber.
  322. func (c *Client) UnsubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) {
  323. err := c.next.Unsubscribe(context.Background(), ctx.RemoteAddr(), query)
  324. if err != nil {
  325. return nil, err
  326. }
  327. return &ctypes.ResultUnsubscribe{}, nil
  328. }
  329. // UnsubscribeAllWS calls original client's UnsubscribeAll using remote address
  330. // as a subscriber.
  331. func (c *Client) UnsubscribeAllWS(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) {
  332. err := c.next.UnsubscribeAll(context.Background(), ctx.RemoteAddr())
  333. if err != nil {
  334. return nil, err
  335. }
  336. return &ctypes.ResultUnsubscribe{}, nil
  337. }
  338. func parseQueryStorePath(path string) (storeName string, err error) {
  339. if !strings.HasPrefix(path, "/") {
  340. return "", fmt.Errorf("expected path to start with /")
  341. }
  342. paths := strings.SplitN(path[1:], "/", 3)
  343. switch {
  344. case len(paths) != 3:
  345. return "", errors.New("expected format like /store/<storeName>/key")
  346. case paths[0] != "store":
  347. return "", errors.New("expected format like /store/<storeName>/key")
  348. case paths[2] != "key":
  349. return "", errors.New("expected format like /store/<storeName>/key")
  350. }
  351. return paths[1], nil
  352. }