github.com/DFWallet/tendermint-cosmos@v0.0.2/light/rpc/client.go (about) 1 package rpc 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "regexp" 9 "time" 10 11 "github.com/gogo/protobuf/proto" 12 13 abci "github.com/DFWallet/tendermint-cosmos/abci/types" 14 "github.com/DFWallet/tendermint-cosmos/crypto/merkle" 15 tmbytes "github.com/DFWallet/tendermint-cosmos/libs/bytes" 16 tmmath "github.com/DFWallet/tendermint-cosmos/libs/math" 17 service "github.com/DFWallet/tendermint-cosmos/libs/service" 18 rpcclient "github.com/DFWallet/tendermint-cosmos/rpc/client" 19 ctypes "github.com/DFWallet/tendermint-cosmos/rpc/core/types" 20 rpctypes "github.com/DFWallet/tendermint-cosmos/rpc/jsonrpc/types" 21 "github.com/DFWallet/tendermint-cosmos/types" 22 ) 23 24 var errNegOrZeroHeight = errors.New("negative or zero height") 25 26 // KeyPathFunc builds a merkle path out of the given path and key. 27 type KeyPathFunc func(path string, key []byte) (merkle.KeyPath, error) 28 29 // LightClient is an interface that contains functionality needed by Client from the light client. 30 //go:generate mockery --case underscore --name LightClient 31 type LightClient interface { 32 ChainID() string 33 Update(ctx context.Context, now time.Time) (*types.LightBlock, error) 34 VerifyLightBlockAtHeight(ctx context.Context, height int64, now time.Time) (*types.LightBlock, error) 35 TrustedLightBlock(height int64) (*types.LightBlock, error) 36 } 37 38 var _ rpcclient.Client = (*Client)(nil) 39 40 // Client is an RPC client, which uses light#Client to verify data (if it can 41 // be proved). Note, merkle.DefaultProofRuntime is used to verify values 42 // returned by ABCI#Query. 43 type Client struct { 44 service.BaseService 45 46 next rpcclient.Client 47 lc LightClient 48 49 // proof runtime used to verify values returned by ABCIQuery 50 prt *merkle.ProofRuntime 51 keyPathFn KeyPathFunc 52 } 53 54 var _ rpcclient.Client = (*Client)(nil) 55 56 // Option allow you to tweak Client. 57 type Option func(*Client) 58 59 // KeyPathFn option can be used to set a function, which parses a given path 60 // and builds the merkle path for the prover. It must be provided if you want 61 // to call ABCIQuery or ABCIQueryWithOptions. 62 func KeyPathFn(fn KeyPathFunc) Option { 63 return func(c *Client) { 64 c.keyPathFn = fn 65 } 66 } 67 68 // DefaultMerkleKeyPathFn creates a function used to generate merkle key paths 69 // from a path string and a key. This is the default used by the cosmos SDK. 70 // This merkle key paths are required when verifying /abci_query calls 71 func DefaultMerkleKeyPathFn() KeyPathFunc { 72 // regexp for extracting store name from /abci_query path 73 storeNameRegexp := regexp.MustCompile(`\/store\/(.+)\/key`) 74 75 return func(path string, key []byte) (merkle.KeyPath, error) { 76 matches := storeNameRegexp.FindStringSubmatch(path) 77 if len(matches) != 2 { 78 return nil, fmt.Errorf("can't find store name in %s using %s", path, storeNameRegexp) 79 } 80 storeName := matches[1] 81 82 kp := merkle.KeyPath{} 83 kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL) 84 kp = kp.AppendKey(key, merkle.KeyEncodingURL) 85 return kp, nil 86 } 87 } 88 89 // NewClient returns a new client. 90 func NewClient(next rpcclient.Client, lc LightClient, opts ...Option) *Client { 91 c := &Client{ 92 next: next, 93 lc: lc, 94 prt: merkle.DefaultProofRuntime(), 95 } 96 c.BaseService = *service.NewBaseService(nil, "Client", c) 97 for _, o := range opts { 98 o(c) 99 } 100 return c 101 } 102 103 func (c *Client) OnStart() error { 104 if !c.next.IsRunning() { 105 return c.next.Start() 106 } 107 return nil 108 } 109 110 func (c *Client) OnStop() { 111 if c.next.IsRunning() { 112 if err := c.next.Stop(); err != nil { 113 c.Logger.Error("Error stopping on next", "err", err) 114 } 115 } 116 } 117 118 func (c *Client) Status(ctx context.Context) (*ctypes.ResultStatus, error) { 119 return c.next.Status(ctx) 120 } 121 122 func (c *Client) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) { 123 return c.next.ABCIInfo(ctx) 124 } 125 126 // ABCIQuery requests proof by default. 127 func (c *Client) ABCIQuery(ctx context.Context, path string, data tmbytes.HexBytes) (*ctypes.ResultABCIQuery, error) { 128 return c.ABCIQueryWithOptions(ctx, path, data, rpcclient.DefaultABCIQueryOptions) 129 } 130 131 // ABCIQueryWithOptions returns an error if opts.Prove is false. 132 func (c *Client) ABCIQueryWithOptions(ctx context.Context, path string, data tmbytes.HexBytes, 133 opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { 134 135 // always request the proof 136 opts.Prove = true 137 138 res, err := c.next.ABCIQueryWithOptions(ctx, path, data, opts) 139 if err != nil { 140 return nil, err 141 } 142 resp := res.Response 143 144 // Validate the response. 145 if resp.IsErr() { 146 return nil, fmt.Errorf("err response code: %v", resp.Code) 147 } 148 if len(resp.Key) == 0 { 149 return nil, errors.New("empty key") 150 } 151 if resp.ProofOps == nil || len(resp.ProofOps.Ops) == 0 { 152 return nil, errors.New("no proof ops") 153 } 154 if resp.Height <= 0 { 155 return nil, errNegOrZeroHeight 156 } 157 158 // Update the light client if we're behind. 159 // NOTE: AppHash for height H is in header H+1. 160 nextHeight := resp.Height + 1 161 l, err := c.updateLightClientIfNeededTo(ctx, &nextHeight) 162 if err != nil { 163 return nil, err 164 } 165 166 // Validate the value proof against the trusted header. 167 if resp.Value != nil { 168 // 1) build a Merkle key path from path and resp.Key 169 if c.keyPathFn == nil { 170 return nil, errors.New("please configure Client with KeyPathFn option") 171 } 172 173 kp, err := c.keyPathFn(path, resp.Key) 174 if err != nil { 175 return nil, fmt.Errorf("can't build merkle key path: %w", err) 176 } 177 178 // 2) verify value 179 err = c.prt.VerifyValue(resp.ProofOps, l.AppHash, kp.String(), resp.Value) 180 if err != nil { 181 return nil, fmt.Errorf("verify value proof: %w", err) 182 } 183 } else { // OR validate the absence proof against the trusted header. 184 err = c.prt.VerifyAbsence(resp.ProofOps, l.AppHash, string(resp.Key)) 185 if err != nil { 186 return nil, fmt.Errorf("verify absence proof: %w", err) 187 } 188 } 189 190 return &ctypes.ResultABCIQuery{Response: resp}, nil 191 } 192 193 func (c *Client) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { 194 return c.next.BroadcastTxCommit(ctx, tx) 195 } 196 197 func (c *Client) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 198 return c.next.BroadcastTxAsync(ctx, tx) 199 } 200 201 func (c *Client) BroadcastTxSync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 202 return c.next.BroadcastTxSync(ctx, tx) 203 } 204 205 func (c *Client) UnconfirmedTxs(ctx context.Context, limit *int) (*ctypes.ResultUnconfirmedTxs, error) { 206 return c.next.UnconfirmedTxs(ctx, limit) 207 } 208 209 func (c *Client) NumUnconfirmedTxs(ctx context.Context) (*ctypes.ResultUnconfirmedTxs, error) { 210 return c.next.NumUnconfirmedTxs(ctx) 211 } 212 213 func (c *Client) CheckTx(ctx context.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) { 214 return c.next.CheckTx(ctx, tx) 215 } 216 217 func (c *Client) NetInfo(ctx context.Context) (*ctypes.ResultNetInfo, error) { 218 return c.next.NetInfo(ctx) 219 } 220 221 func (c *Client) DumpConsensusState(ctx context.Context) (*ctypes.ResultDumpConsensusState, error) { 222 return c.next.DumpConsensusState(ctx) 223 } 224 225 func (c *Client) ConsensusState(ctx context.Context) (*ctypes.ResultConsensusState, error) { 226 return c.next.ConsensusState(ctx) 227 } 228 229 func (c *Client) ConsensusParams(ctx context.Context, height *int64) (*ctypes.ResultConsensusParams, error) { 230 res, err := c.next.ConsensusParams(ctx, height) 231 if err != nil { 232 return nil, err 233 } 234 235 // Validate res. 236 if err := types.ValidateConsensusParams(res.ConsensusParams); err != nil { 237 return nil, err 238 } 239 if res.BlockHeight <= 0 { 240 return nil, errNegOrZeroHeight 241 } 242 243 // Update the light client if we're behind. 244 l, err := c.updateLightClientIfNeededTo(ctx, &res.BlockHeight) 245 if err != nil { 246 return nil, err 247 } 248 249 // Verify hash. 250 if cH, tH := types.HashConsensusParams(res.ConsensusParams), l.ConsensusHash; !bytes.Equal(cH, tH) { 251 return nil, fmt.Errorf("params hash %X does not match trusted hash %X", 252 cH, tH) 253 } 254 255 return res, nil 256 } 257 258 func (c *Client) Health(ctx context.Context) (*ctypes.ResultHealth, error) { 259 return c.next.Health(ctx) 260 } 261 262 // BlockchainInfo calls rpcclient#BlockchainInfo and then verifies every header 263 // returned. 264 func (c *Client) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { 265 res, err := c.next.BlockchainInfo(ctx, minHeight, maxHeight) 266 if err != nil { 267 return nil, err 268 } 269 270 // Validate res. 271 for i, meta := range res.BlockMetas { 272 if meta == nil { 273 return nil, fmt.Errorf("nil block meta %d", i) 274 } 275 if err := meta.ValidateBasic(); err != nil { 276 return nil, fmt.Errorf("invalid block meta %d: %w", i, err) 277 } 278 } 279 280 // Update the light client if we're behind. 281 if len(res.BlockMetas) > 0 { 282 lastHeight := res.BlockMetas[len(res.BlockMetas)-1].Header.Height 283 if _, err := c.updateLightClientIfNeededTo(ctx, &lastHeight); err != nil { 284 return nil, err 285 } 286 } 287 288 // Verify each of the BlockMetas. 289 for _, meta := range res.BlockMetas { 290 h, err := c.lc.TrustedLightBlock(meta.Header.Height) 291 if err != nil { 292 return nil, fmt.Errorf("trusted header %d: %w", meta.Header.Height, err) 293 } 294 if bmH, tH := meta.Header.Hash(), h.Hash(); !bytes.Equal(bmH, tH) { 295 return nil, fmt.Errorf("block meta header %X does not match with trusted header %X", 296 bmH, tH) 297 } 298 } 299 300 return res, nil 301 } 302 303 func (c *Client) Genesis(ctx context.Context) (*ctypes.ResultGenesis, error) { 304 return c.next.Genesis(ctx) 305 } 306 307 func (c *Client) GenesisChunked(ctx context.Context, id uint) (*ctypes.ResultGenesisChunk, error) { 308 return c.next.GenesisChunked(ctx, id) 309 } 310 311 // Block calls rpcclient#Block and then verifies the result. 312 func (c *Client) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) { 313 res, err := c.next.Block(ctx, height) 314 if err != nil { 315 return nil, err 316 } 317 318 // Validate res. 319 if err := res.BlockID.ValidateBasic(); err != nil { 320 return nil, err 321 } 322 if err := res.Block.ValidateBasic(); err != nil { 323 return nil, err 324 } 325 if bmH, bH := res.BlockID.Hash, res.Block.Hash(); !bytes.Equal(bmH, bH) { 326 return nil, fmt.Errorf("blockID %X does not match with block %X", 327 bmH, bH) 328 } 329 330 // Update the light client if we're behind. 331 l, err := c.updateLightClientIfNeededTo(ctx, &res.Block.Height) 332 if err != nil { 333 return nil, err 334 } 335 336 // Verify block. 337 if bH, tH := res.Block.Hash(), l.Hash(); !bytes.Equal(bH, tH) { 338 return nil, fmt.Errorf("block header %X does not match with trusted header %X", 339 bH, tH) 340 } 341 342 return res, nil 343 } 344 345 // BlockByHash calls rpcclient#BlockByHash and then verifies the result. 346 func (c *Client) BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error) { 347 res, err := c.next.BlockByHash(ctx, hash) 348 if err != nil { 349 return nil, err 350 } 351 352 // Validate res. 353 if err := res.BlockID.ValidateBasic(); err != nil { 354 return nil, err 355 } 356 if err := res.Block.ValidateBasic(); err != nil { 357 return nil, err 358 } 359 if bmH, bH := res.BlockID.Hash, res.Block.Hash(); !bytes.Equal(bmH, bH) { 360 return nil, fmt.Errorf("blockID %X does not match with block %X", 361 bmH, bH) 362 } 363 364 // Update the light client if we're behind. 365 l, err := c.updateLightClientIfNeededTo(ctx, &res.Block.Height) 366 if err != nil { 367 return nil, err 368 } 369 370 // Verify block. 371 if bH, tH := res.Block.Hash(), l.Hash(); !bytes.Equal(bH, tH) { 372 return nil, fmt.Errorf("block header %X does not match with trusted header %X", 373 bH, tH) 374 } 375 376 return res, nil 377 } 378 379 // BlockResults returns the block results for the given height. If no height is 380 // provided, the results of the block preceding the latest are returned. 381 func (c *Client) BlockResults(ctx context.Context, height *int64) (*ctypes.ResultBlockResults, error) { 382 var h int64 383 if height == nil { 384 res, err := c.next.Status(ctx) 385 if err != nil { 386 return nil, fmt.Errorf("can't get latest height: %w", err) 387 } 388 // Can't return the latest block results here because we won't be able to 389 // prove them. Return the results for the previous block instead. 390 h = res.SyncInfo.LatestBlockHeight - 1 391 } else { 392 h = *height 393 } 394 395 res, err := c.next.BlockResults(ctx, &h) 396 if err != nil { 397 return nil, err 398 } 399 400 // Validate res. 401 if res.Height <= 0 { 402 return nil, errNegOrZeroHeight 403 } 404 405 // Update the light client if we're behind. 406 nextHeight := h + 1 407 trustedBlock, err := c.updateLightClientIfNeededTo(ctx, &nextHeight) 408 if err != nil { 409 return nil, err 410 } 411 412 // proto-encode BeginBlock events 413 bbeBytes, err := proto.Marshal(&abci.ResponseBeginBlock{ 414 Events: res.BeginBlockEvents, 415 }) 416 if err != nil { 417 return nil, err 418 } 419 420 // Build a Merkle tree of proto-encoded DeliverTx results and get a hash. 421 results := types.NewResults(res.TxsResults) 422 423 // proto-encode EndBlock events. 424 ebeBytes, err := proto.Marshal(&abci.ResponseEndBlock{ 425 Events: res.EndBlockEvents, 426 }) 427 if err != nil { 428 return nil, err 429 } 430 431 // Build a Merkle tree out of the above 3 binary slices. 432 rH := merkle.HashFromByteSlices([][]byte{bbeBytes, results.Hash(), ebeBytes}) 433 434 // Verify block results. 435 if !bytes.Equal(rH, trustedBlock.LastResultsHash) { 436 return nil, fmt.Errorf("last results %X does not match with trusted last results %X", 437 rH, trustedBlock.LastResultsHash) 438 } 439 440 return res, nil 441 } 442 443 func (c *Client) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) { 444 // Update the light client if we're behind and retrieve the light block at the requested height 445 // or at the latest height if no height is provided. 446 l, err := c.updateLightClientIfNeededTo(ctx, height) 447 if err != nil { 448 return nil, err 449 } 450 451 return &ctypes.ResultCommit{ 452 SignedHeader: *l.SignedHeader, 453 CanonicalCommit: true, 454 }, nil 455 } 456 457 // Tx calls rpcclient#Tx method and then verifies the proof if such was 458 // requested. 459 func (c *Client) Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) { 460 res, err := c.next.Tx(ctx, hash, prove) 461 if err != nil || !prove { 462 return res, err 463 } 464 465 // Validate res. 466 if res.Height <= 0 { 467 return nil, errNegOrZeroHeight 468 } 469 470 // Update the light client if we're behind. 471 l, err := c.updateLightClientIfNeededTo(ctx, &res.Height) 472 if err != nil { 473 return nil, err 474 } 475 476 // Validate the proof. 477 return res, res.Proof.Validate(l.DataHash) 478 } 479 480 func (c *Client) TxSearch( 481 ctx context.Context, 482 query string, 483 prove bool, 484 page, perPage *int, 485 orderBy string, 486 ) (*ctypes.ResultTxSearch, error) { 487 return c.next.TxSearch(ctx, query, prove, page, perPage, orderBy) 488 } 489 490 func (c *Client) BlockSearch( 491 ctx context.Context, 492 query string, 493 page, perPage *int, 494 orderBy string, 495 ) (*ctypes.ResultBlockSearch, error) { 496 return c.next.BlockSearch(ctx, query, page, perPage, orderBy) 497 } 498 499 // Validators fetches and verifies validators. 500 func (c *Client) Validators( 501 ctx context.Context, 502 height *int64, 503 pagePtr, perPagePtr *int, 504 ) (*ctypes.ResultValidators, error) { 505 506 // Update the light client if we're behind and retrieve the light block at the 507 // requested height or at the latest height if no height is provided. 508 l, err := c.updateLightClientIfNeededTo(ctx, height) 509 if err != nil { 510 return nil, err 511 } 512 513 totalCount := len(l.ValidatorSet.Validators) 514 perPage := validatePerPage(perPagePtr) 515 page, err := validatePage(pagePtr, perPage, totalCount) 516 if err != nil { 517 return nil, err 518 } 519 520 skipCount := validateSkipCount(page, perPage) 521 v := l.ValidatorSet.Validators[skipCount : skipCount+tmmath.MinInt(perPage, totalCount-skipCount)] 522 523 return &ctypes.ResultValidators{ 524 BlockHeight: l.Height, 525 Validators: v, 526 Count: len(v), 527 Total: totalCount}, nil 528 } 529 530 func (c *Client) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { 531 return c.next.BroadcastEvidence(ctx, ev) 532 } 533 534 func (c *Client) Subscribe(ctx context.Context, subscriber, query string, 535 outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) { 536 return c.next.Subscribe(ctx, subscriber, query, outCapacity...) 537 } 538 539 func (c *Client) Unsubscribe(ctx context.Context, subscriber, query string) error { 540 return c.next.Unsubscribe(ctx, subscriber, query) 541 } 542 543 func (c *Client) UnsubscribeAll(ctx context.Context, subscriber string) error { 544 return c.next.UnsubscribeAll(ctx, subscriber) 545 } 546 547 func (c *Client) updateLightClientIfNeededTo(ctx context.Context, height *int64) (*types.LightBlock, error) { 548 var ( 549 l *types.LightBlock 550 err error 551 ) 552 if height == nil { 553 l, err = c.lc.Update(ctx, time.Now()) 554 } else { 555 l, err = c.lc.VerifyLightBlockAtHeight(ctx, *height, time.Now()) 556 } 557 if err != nil { 558 return nil, fmt.Errorf("failed to update light client to %d: %w", height, err) 559 } 560 return l, nil 561 } 562 563 func (c *Client) RegisterOpDecoder(typ string, dec merkle.OpDecoder) { 564 c.prt.RegisterOpDecoder(typ, dec) 565 } 566 567 // SubscribeWS subscribes for events using the given query and remote address as 568 // a subscriber, but does not verify responses (UNSAFE)! 569 // TODO: verify data 570 func (c *Client) SubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) { 571 out, err := c.next.Subscribe(context.Background(), ctx.RemoteAddr(), query) 572 if err != nil { 573 return nil, err 574 } 575 576 go func() { 577 for { 578 select { 579 case resultEvent := <-out: 580 // We should have a switch here that performs a validation 581 // depending on the event's type. 582 ctx.WSConn.TryWriteRPCResponse( 583 rpctypes.NewRPCSuccessResponse( 584 rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", ctx.JSONReq.ID)), 585 resultEvent, 586 )) 587 case <-c.Quit(): 588 return 589 } 590 } 591 }() 592 593 return &ctypes.ResultSubscribe{}, nil 594 } 595 596 // UnsubscribeWS calls original client's Unsubscribe using remote address as a 597 // subscriber. 598 func (c *Client) UnsubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) { 599 err := c.next.Unsubscribe(context.Background(), ctx.RemoteAddr(), query) 600 if err != nil { 601 return nil, err 602 } 603 return &ctypes.ResultUnsubscribe{}, nil 604 } 605 606 // UnsubscribeAllWS calls original client's UnsubscribeAll using remote address 607 // as a subscriber. 608 func (c *Client) UnsubscribeAllWS(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) { 609 err := c.next.UnsubscribeAll(context.Background(), ctx.RemoteAddr()) 610 if err != nil { 611 return nil, err 612 } 613 return &ctypes.ResultUnsubscribe{}, nil 614 } 615 616 // XXX: Copied from rpc/core/env.go 617 const ( 618 // see README 619 defaultPerPage = 30 620 maxPerPage = 100 621 ) 622 623 func validatePage(pagePtr *int, perPage, totalCount int) (int, error) { 624 if perPage < 1 { 625 panic(fmt.Sprintf("zero or negative perPage: %d", perPage)) 626 } 627 628 if pagePtr == nil { // no page parameter 629 return 1, nil 630 } 631 632 pages := ((totalCount - 1) / perPage) + 1 633 if pages == 0 { 634 pages = 1 // one page (even if it's empty) 635 } 636 page := *pagePtr 637 if page <= 0 || page > pages { 638 return 1, fmt.Errorf("page should be within [1, %d] range, given %d", pages, page) 639 } 640 641 return page, nil 642 } 643 644 func validatePerPage(perPagePtr *int) int { 645 if perPagePtr == nil { // no per_page parameter 646 return defaultPerPage 647 } 648 649 perPage := *perPagePtr 650 if perPage < 1 { 651 return defaultPerPage 652 } else if perPage > maxPerPage { 653 return maxPerPage 654 } 655 return perPage 656 } 657 658 func validateSkipCount(page, perPage int) int { 659 skipCount := (page - 1) * perPage 660 if skipCount < 0 { 661 return 0 662 } 663 664 return skipCount 665 }