func (c *Client) Validators(
	ctx context.Context,
	height *int64,
	pagePtr, perPagePtr *int,
) (*coretypes.ResultValidators, error) {
	l, err := c.updateLightClientIfNeededTo(ctx, height)
	if err != nil {
		return nil, err
	}
	totalCount := len(l.ValidatorSet.Validators)
	perPage := validatePerPage(perPagePtr)
	page, err := validatePage(pagePtr, perPage, totalCount)
	if err != nil {
		return nil, err
	}
	skipCount := validateSkipCount(page, perPage)
	v := l.ValidatorSet.Validators[skipCount : skipCount+tmmath.MinInt(perPage, totalCount-skipCount)]
	return &coretypes.ResultValidators{
		BlockHeight: l.Height,
		Validators:  v,
		Count:       len(v),
		Total:       totalCount}, nil
}
Validators fetches and verifies validators.
package rpc
import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"regexp"
	"time"
	"github.com/gogo/protobuf/proto"
	abci "github.com/tendermint/tendermint/abci/types"
	"github.com/tendermint/tendermint/crypto/merkle"
	tmbytes "github.com/tendermint/tendermint/libs/bytes"
	tmmath "github.com/tendermint/tendermint/libs/math"
	service "github.com/tendermint/tendermint/libs/service"
	rpcclient "github.com/tendermint/tendermint/rpc/client"
	"github.com/tendermint/tendermint/rpc/coretypes"
	rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
	"github.com/tendermint/tendermint/types"
)
type KeyPathFunc func(path string, key []byte) (merkle.KeyPath, error)
type LightClient interface {
	ChainID() string
	Update(ctx context.Context, now time.Time) (*types.LightBlock, error)
	VerifyLightBlockAtHeight(ctx context.Context, height int64, now time.Time) (*types.LightBlock, error)
	TrustedLightBlock(height int64) (*types.LightBlock, error)
}
var _ rpcclient.Client = (*Client)(nil)
type Client struct {
	service.BaseService
	next rpcclient.Client
	lc   LightClient
	prt       *merkle.ProofRuntime
	keyPathFn KeyPathFunc
}
var _ rpcclient.Client = (*Client)(nil)
type Option func(*Client)
func KeyPathFn(fn KeyPathFunc) Option {
	return func(c *Client) {
		c.keyPathFn = fn
	}
}
func DefaultMerkleKeyPathFn() KeyPathFunc {
	storeNameRegexp := regexp.MustCompile(`\/store\/(.+)\/key`)
	return func(path string, key []byte) (merkle.KeyPath, error) {
		matches := storeNameRegexp.FindStringSubmatch(path)
		if len(matches) != 2 {
			return nil, fmt.Errorf("can't find store name in %s using %s", path, storeNameRegexp)
		}
		storeName := matches[1]
		kp := merkle.KeyPath{}
		kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL)
		kp = kp.AppendKey(key, merkle.KeyEncodingURL)
		return kp, nil
	}
}
func NewClient(next rpcclient.Client, lc LightClient, opts ...Option) *Client {
	c := &Client{
		next: next,
		lc:   lc,
		prt:  merkle.DefaultProofRuntime(),
	}
	c.BaseService = *service.NewBaseService(nil, "Client", c)
	for _, o := range opts {
		o(c)
	}
	return c
}
func (c *Client) OnStart() error {
	if !c.next.IsRunning() {
		return c.next.Start()
	}
	return nil
}
func (c *Client) OnStop() {
	if c.next.IsRunning() {
		if err := c.next.Stop(); err != nil {
			c.Logger.Error("Error stopping on next", "err", err)
		}
	}
}
func (c *Client) Status(ctx context.Context) (*coretypes.ResultStatus, error) {
	return c.next.Status(ctx)
}
func (c *Client) ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo, error) {
	return c.next.ABCIInfo(ctx)
}
func (c *Client) ABCIQuery(ctx context.Context, path string, data tmbytes.HexBytes) (*coretypes.ResultABCIQuery, error) {  
	return c.ABCIQueryWithOptions(ctx, path, data, rpcclient.DefaultABCIQueryOptions)
}
func (c *Client) ABCIQueryWithOptions(ctx context.Context, path string, data tmbytes.HexBytes,
	opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) {
	opts.Prove = true
	res, err := c.next.ABCIQueryWithOptions(ctx, path, data, opts)
	if err != nil {
		return nil, err
	}
	resp := res.Response
	if resp.IsErr() {
		return nil, fmt.Errorf("err response code: %v", resp.Code)
	}
	if len(resp.Key) == 0 {
		return nil, errors.New("empty key")
	}
	if resp.ProofOps == nil || len(resp.ProofOps.Ops) == 0 {
		return nil, errors.New("no proof ops")
	}
	if resp.Height <= 0 {
		return nil, coretypes.ErrZeroOrNegativeHeight
	}
	nextHeight := resp.Height + 1
	l, err := c.updateLightClientIfNeededTo(ctx, &nextHeight)
	if err != nil {
		return nil, err
	}
	if resp.Value != nil {
		if c.keyPathFn == nil {
			return nil, errors.New("please configure Client with KeyPathFn option")
		}
		kp, err := c.keyPathFn(path, resp.Key)
		if err != nil {
			return nil, fmt.Errorf("can't build merkle key path: %w", err)
		}
		err = c.prt.VerifyValue(resp.ProofOps, l.AppHash, kp.String(), resp.Value)
		if err != nil {
			return nil, fmt.Errorf("verify value proof: %w", err)
		}
	} else {  
		err = c.prt.VerifyAbsence(resp.ProofOps, l.AppHash, string(resp.Key))
		if err != nil {
			return nil, fmt.Errorf("verify absence proof: %w", err)
		}
	}
	return &coretypes.ResultABCIQuery{Response: resp}, nil
}
func (c *Client) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) {
	return c.next.BroadcastTxCommit(ctx, tx)
}
func (c *Client) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
	return c.next.BroadcastTxAsync(ctx, tx)
}
func (c *Client) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
	return c.next.BroadcastTxSync(ctx, tx)
}
func (c *Client) UnconfirmedTxs(ctx context.Context, limit *int) (*coretypes.ResultUnconfirmedTxs, error) {
	return c.next.UnconfirmedTxs(ctx, limit)
}
func (c *Client) NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error) {
	return c.next.NumUnconfirmedTxs(ctx)
}
func (c *Client) CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultCheckTx, error) {
	return c.next.CheckTx(ctx, tx)
}
func (c *Client) RemoveTx(ctx context.Context, txKey types.TxKey) error {
	return c.next.RemoveTx(ctx, txKey)
}
func (c *Client) NetInfo(ctx context.Context) (*coretypes.ResultNetInfo, error) {
	return c.next.NetInfo(ctx)
}
func (c *Client) DumpConsensusState(ctx context.Context) (*coretypes.ResultDumpConsensusState, error) {
	return c.next.DumpConsensusState(ctx)
}
func (c *Client) ConsensusState(ctx context.Context) (*coretypes.ResultConsensusState, error) {
	return c.next.ConsensusState(ctx)
}
func (c *Client) ConsensusParams(ctx context.Context, height *int64) (*coretypes.ResultConsensusParams, error) {
	res, err := c.next.ConsensusParams(ctx, height)
	if err != nil {
		return nil, err
	}
	if err := res.ConsensusParams.ValidateConsensusParams(); err != nil {
		return nil, err
	}
	if res.BlockHeight <= 0 {
		return nil, coretypes.ErrZeroOrNegativeHeight
	}
	l, err := c.updateLightClientIfNeededTo(ctx, &res.BlockHeight)
	if err != nil {
		return nil, err
	}
	if cH, tH := res.ConsensusParams.HashConsensusParams(), l.ConsensusHash; !bytes.Equal(cH, tH) {
		return nil, fmt.Errorf("params hash %X does not match trusted hash %X",
			cH, tH)
	}
	return res, nil
}
func (c *Client) Health(ctx context.Context) (*coretypes.ResultHealth, error) {
	return c.next.Health(ctx)
}
func (c *Client) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) {  
	res, err := c.next.BlockchainInfo(ctx, minHeight, maxHeight)
	if err != nil {
		return nil, err
	}
	for i, meta := range res.BlockMetas {
		if meta == nil {
			return nil, fmt.Errorf("nil block meta %d", i)
		}
		if err := meta.ValidateBasic(); err != nil {
			return nil, fmt.Errorf("invalid block meta %d: %w", i, err)
		}
	}
	if len(res.BlockMetas) > 0 {
		lastHeight := res.BlockMetas[len(res.BlockMetas)-1].Header.Height
		if _, err := c.updateLightClientIfNeededTo(ctx, &lastHeight); err != nil {
			return nil, err
		}
	}
	for _, meta := range res.BlockMetas {
		h, err := c.lc.TrustedLightBlock(meta.Header.Height)
		if err != nil {
			return nil, fmt.Errorf("trusted header %d: %w", meta.Header.Height, err)
		}
		if bmH, tH := meta.Header.Hash(), h.Hash(); !bytes.Equal(bmH, tH) {
			return nil, fmt.Errorf("block meta header %X does not match with trusted header %X",
				bmH, tH)
		}
	}
	return res, nil
}
func (c *Client) Genesis(ctx context.Context) (*coretypes.ResultGenesis, error) {
	return c.next.Genesis(ctx)
}
func (c *Client) GenesisChunked(ctx context.Context, id uint) (*coretypes.ResultGenesisChunk, error) {
	return c.next.GenesisChunked(ctx, id)
}
func (c *Client) Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error) {
	res, err := c.next.Block(ctx, height)
	if err != nil {
		return nil, err
	}
	if err := res.BlockID.ValidateBasic(); err != nil {
		return nil, err
	}
	if err := res.Block.ValidateBasic(); err != nil {
		return nil, err
	}
	if bmH, bH := res.BlockID.Hash, res.Block.Hash(); !bytes.Equal(bmH, bH) {
		return nil, fmt.Errorf("blockID %X does not match with block %X",
			bmH, bH)
	}
	l, err := c.updateLightClientIfNeededTo(ctx, &res.Block.Height)
	if err != nil {
		return nil, err
	}
	if bH, tH := res.Block.Hash(), l.Hash(); !bytes.Equal(bH, tH) {
		return nil, fmt.Errorf("block header %X does not match with trusted header %X",
			bH, tH)
	}
	return res, nil
}
func (c *Client) BlockByHash(ctx context.Context, hash tmbytes.HexBytes) (*coretypes.ResultBlock, error) {
	res, err := c.next.BlockByHash(ctx, hash)
	if err != nil {
		return nil, err
	}
	if err := res.BlockID.ValidateBasic(); err != nil {
		return nil, err
	}
	if err := res.Block.ValidateBasic(); err != nil {
		return nil, err
	}
	if bmH, bH := res.BlockID.Hash, res.Block.Hash(); !bytes.Equal(bmH, bH) {
		return nil, fmt.Errorf("blockID %X does not match with block %X",
			bmH, bH)
	}
	l, err := c.updateLightClientIfNeededTo(ctx, &res.Block.Height)
	if err != nil {
		return nil, err
	}
	if bH, tH := res.Block.Hash(), l.Hash(); !bytes.Equal(bH, tH) {
		return nil, fmt.Errorf("block header %X does not match with trusted header %X",
			bH, tH)
	}
	return res, nil
}
func (c *Client) BlockResults(ctx context.Context, height *int64) (*coretypes.ResultBlockResults, error) {
	var h int64
	if height == nil {
		res, err := c.next.Status(ctx)
		if err != nil {
			return nil, fmt.Errorf("can't get latest height: %w", err)
		}
		h = res.SyncInfo.LatestBlockHeight - 1
	} else {
		h = *height
	}
	res, err := c.next.BlockResults(ctx, &h)
	if err != nil {
		return nil, err
	}
	if res.Height <= 0 {
		return nil, coretypes.ErrZeroOrNegativeHeight
	}
	nextHeight := h + 1
	trustedBlock, err := c.updateLightClientIfNeededTo(ctx, &nextHeight)
	if err != nil {
		return nil, err
	}
	bbeBytes, err := proto.Marshal(&abci.ResponseBeginBlock{
		Events: res.BeginBlockEvents,
	})
	if err != nil {
		return nil, err
	}
	results := types.NewResults(res.TxsResults)
	ebeBytes, err := proto.Marshal(&abci.ResponseEndBlock{
		Events: res.EndBlockEvents,
	})
	if err != nil {
		return nil, err
	}
	rH := merkle.HashFromByteSlices([][]byte{bbeBytes, results.Hash(), ebeBytes})
	if !bytes.Equal(rH, trustedBlock.LastResultsHash) {
		return nil, fmt.Errorf("last results %X does not match with trusted last results %X",
			rH, trustedBlock.LastResultsHash)
	}
	return res, nil
}
func (c *Client) Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) {
	l, err := c.updateLightClientIfNeededTo(ctx, height)
	if err != nil {
		return nil, err
	}
	return &coretypes.ResultCommit{
		SignedHeader:    *l.SignedHeader,
		CanonicalCommit: true,
	}, nil
}
func (c *Client) Tx(ctx context.Context, hash tmbytes.HexBytes, prove bool) (*coretypes.ResultTx, error) {
	res, err := c.next.Tx(ctx, hash, prove)
	if err != nil || !prove {
		return res, err
	}
	if res.Height <= 0 {
		return nil, coretypes.ErrZeroOrNegativeHeight
	}
	l, err := c.updateLightClientIfNeededTo(ctx, &res.Height)
	if err != nil {
		return nil, err
	}
	return res, res.Proof.Validate(l.DataHash)
}
func (c *Client) TxSearch(
	ctx context.Context,
	query string,
	prove bool,
	page, perPage *int,
	orderBy string,
) (*coretypes.ResultTxSearch, error) {
	return c.next.TxSearch(ctx, query, prove, page, perPage, orderBy)
}
func (c *Client) BlockSearch(
	ctx context.Context,
	query string,
	page, perPage *int,
	orderBy string,
) (*coretypes.ResultBlockSearch, error) {
	return c.next.BlockSearch(ctx, query, page, perPage, orderBy)
}