func (env *Environment) BlockSearch(
	ctx *rpctypes.Context,
	query string,
	pagePtr, perPagePtr *int,
	orderBy string,
) (*coretypes.ResultBlockSearch, error) {
	if !indexer.KVSinkEnabled(env.EventSinks) {
		return nil, fmt.Errorf("block searching is disabled due to no kvEventSink")
	}
	q, err := tmquery.New(query)
	if err != nil {
		return nil, err
	}
	var kvsink indexer.EventSink
	for _, sink := range env.EventSinks {
		if sink.Type() == indexer.KV {
			kvsink = sink
		}
	}
	results, err := kvsink.SearchBlockEvents(ctx.Context(), q)
	if err != nil {
		return nil, err
	}
	switch orderBy {
	case "desc", "":
		sort.Slice(results, func(i, j int) bool { return results[i] > results[j] })
	case "asc":
		sort.Slice(results, func(i, j int) bool { return results[i] < results[j] })
	default:
		return nil, fmt.Errorf("expected order_by to be either `asc` or `desc` or empty: %w", coretypes.ErrInvalidRequest)
	}
	totalCount := len(results)
	perPage := env.validatePerPage(perPagePtr)
	page, err := validatePage(pagePtr, perPage, totalCount)
	if err != nil {
		return nil, err
	}
	skipCount := validateSkipCount(page, perPage)
	pageSize := tmmath.MinInt(perPage, totalCount-skipCount)
	apiResults := make([]*coretypes.ResultBlock, 0, pageSize)
	for i := skipCount; i < skipCount+pageSize; i++ {
		block := env.BlockStore.LoadBlock(results[i])
		if block != nil {
			blockMeta := env.BlockStore.LoadBlockMeta(block.Height)
			if blockMeta != nil {
				apiResults = append(apiResults, &coretypes.ResultBlock{
					Block:   block,
					BlockID: blockMeta.BlockID,
				})
			}
		}
	}
	return &coretypes.ResultBlockSearch{Blocks: apiResults, TotalCount: totalCount}, nil
}
BlockSearch searches for a paginated set of blocks matching BeginBlock and
EndBlock event search criteria.
package core
import (
	"fmt"
	"sort"
	"github.com/tendermint/tendermint/internal/state/indexer"
	"github.com/tendermint/tendermint/libs/bytes"
	tmmath "github.com/tendermint/tendermint/libs/math"
	tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
	"github.com/tendermint/tendermint/rpc/coretypes"
	rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
	"github.com/tendermint/tendermint/types"
)
func (env *Environment) BlockchainInfo(
	ctx *rpctypes.Context,
	minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) {
	const limit int64 = 20
	var err error
	minHeight, maxHeight, err = filterMinMax(
		env.BlockStore.Base(),
		env.BlockStore.Height(),
		minHeight,
		maxHeight,
		limit)
	if err != nil {
		return nil, err
	}
	env.Logger.Debug("BlockchainInfo", "maxHeight", maxHeight, "minHeight", minHeight)
	blockMetas := make([]*types.BlockMeta, 0, maxHeight-minHeight+1)
	for height := maxHeight; height >= minHeight; height-- {
		blockMeta := env.BlockStore.LoadBlockMeta(height)
		if blockMeta != nil {
			blockMetas = append(blockMetas, blockMeta)
		}
	}
	return &coretypes.ResultBlockchainInfo{
		LastHeight: env.BlockStore.Height(),
		BlockMetas: blockMetas}, nil
}
func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
	if min < 0 || max < 0 {
		return min, max, coretypes.ErrZeroOrNegativeHeight
	}
	if min == 0 {
		min = 1
	}
	if max == 0 {
		max = height
	}
	max = tmmath.MinInt64(height, max)
	min = tmmath.MaxInt64(base, min)
	min = tmmath.MaxInt64(min, max-limit+1)
	if min > max {
		return min, max, fmt.Errorf("%w: min height %d can't be greater than max height %d",
			coretypes.ErrInvalidRequest, min, max)
	}
	return min, max, nil
}
func (env *Environment) Block(ctx *rpctypes.Context, heightPtr *int64) (*coretypes.ResultBlock, error) {
	height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
	if err != nil {
		return nil, err
	}
	blockMeta := env.BlockStore.LoadBlockMeta(height)
	if blockMeta == nil {
		return &coretypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
	}
	block := env.BlockStore.LoadBlock(height)
	return &coretypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
}
func (env *Environment) BlockByHash(ctx *rpctypes.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error) {
	block := env.BlockStore.LoadBlockByHash(hash)
	if block == nil {
		return &coretypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
	}
	blockMeta := env.BlockStore.LoadBlockMeta(block.Height)
	return &coretypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
}
func (env *Environment) Commit(ctx *rpctypes.Context, heightPtr *int64) (*coretypes.ResultCommit, error) {
	height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
	if err != nil {
		return nil, err
	}
	blockMeta := env.BlockStore.LoadBlockMeta(height)
	if blockMeta == nil {
		return nil, nil
	}
	header := blockMeta.Header
	if height == env.BlockStore.Height() {
		commit := env.BlockStore.LoadSeenCommit()
		if commit != nil && commit.Height == height {
			return coretypes.NewResultCommit(&header, commit, false), nil
		}
	}
	commit := env.BlockStore.LoadBlockCommit(height)
	if commit == nil {
		return nil, nil
	}
	return coretypes.NewResultCommit(&header, commit, true), nil
}
func (env *Environment) BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*coretypes.ResultBlockResults, error) {
	height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
	if err != nil {
		return nil, err
	}
	results, err := env.StateStore.LoadABCIResponses(height)
	if err != nil {
		return nil, err
	}
	var totalGasUsed int64
	for _, tx := range results.GetDeliverTxs() {
		totalGasUsed += tx.GetGasUsed()
	}
	return &coretypes.ResultBlockResults{
		Height:                height,
		TxsResults:            results.DeliverTxs,
		TotalGasUsed:          totalGasUsed,
		BeginBlockEvents:      results.BeginBlock.Events,
		EndBlockEvents:        results.EndBlock.Events,
		ValidatorUpdates:      results.EndBlock.ValidatorUpdates,
		ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
	}, nil
}