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