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