func (env *Environment) getHeight(latestHeight int64, heightPtr *int64) (int64, error) {
	if heightPtr != nil {
		height := *heightPtr
		if height <= 0 {
			return 0, fmt.Errorf("%w (requested height: %d)", coretypes.ErrZeroOrNegativeHeight, height)
		}
		if height > latestHeight {
			return 0, fmt.Errorf("%w (requested height: %d, blockchain height: %d)",
				coretypes.ErrHeightExceedsChainHead, height, latestHeight)
		}
		base := env.BlockStore.Base()
		if height < base {
			return 0, fmt.Errorf("%w (requested height: %d, base height: %d)", coretypes.ErrHeightNotAvailable, height, base)
		}
		return height, nil
	}
	return latestHeight, nil
}
latestHeight can be either latest committed or uncommitted (+1) height.
package core
import (
	"encoding/base64"
	"fmt"
	"time"
	"github.com/tendermint/tendermint/config"
	"github.com/tendermint/tendermint/crypto"
	"github.com/tendermint/tendermint/internal/consensus"
	"github.com/tendermint/tendermint/internal/mempool"
	"github.com/tendermint/tendermint/internal/p2p"
	"github.com/tendermint/tendermint/internal/proxy"
	sm "github.com/tendermint/tendermint/internal/state"
	"github.com/tendermint/tendermint/internal/state/indexer"
	"github.com/tendermint/tendermint/internal/statesync"
	tmjson "github.com/tendermint/tendermint/libs/json"
	"github.com/tendermint/tendermint/libs/log"
	"github.com/tendermint/tendermint/rpc/coretypes"
	"github.com/tendermint/tendermint/types"
)
const (
	defaultPerPage = 30
	maxPerPage     = 100
	SubscribeTimeout = 5 * time.Second
	genesisChunkSize = 16 * 1024 * 1024  
)
type consensusState interface {
	GetState() sm.State
	GetValidators() (int64, []*types.Validator)
	GetLastHeight() int64
	GetRoundStateJSON() ([]byte, error)
	GetRoundStateSimpleJSON() ([]byte, error)
}
type transport interface {
	Listeners() []string
	IsListening() bool
	NodeInfo() types.NodeInfo
}
type consensusReactor interface {
	WaitSync() bool
	GetPeerState(peerID types.NodeID) (*consensus.PeerState, bool)
}
type peerManager interface {
	Peers() []types.NodeID
	Addresses(types.NodeID) []p2p.NodeAddress
}
type Environment struct {
	ProxyAppQuery   proxy.AppConnQuery
	ProxyAppMempool proxy.AppConnMempool
	StateStore       sm.Store
	BlockStore       sm.BlockStore
	EvidencePool     sm.EvidencePool
	ConsensusState   consensusState
	ConsensusReactor consensusReactor
	P2PTransport transport
	PeerManager peerManager
	PubKey            crypto.PubKey
	GenDoc            *types.GenesisDoc  
	EventSinks        []indexer.EventSink
	EventBus          *types.EventBus  
	Mempool           mempool.Mempool
	BlockSyncReactor  consensus.BlockSyncReactor
	StateSyncMetricer statesync.Metricer
	Logger log.Logger
	Config config.RPCConfig
	genChunks []string
}
func validatePage(pagePtr *int, perPage, totalCount int) (int, error) {
	if perPage < 1 {
		panic(fmt.Errorf("%w (%d)", coretypes.ErrZeroOrNegativePerPage, perPage))
	}
	if pagePtr == nil {  
		return 1, nil
	}
	pages := ((totalCount - 1) / perPage) + 1
	if pages == 0 {
		pages = 1  
	}
	page := *pagePtr
	if page <= 0 || page > pages {
		return 1, fmt.Errorf("%w expected range: [1, %d], given %d", coretypes.ErrPageOutOfRange, pages, page)
	}
	return page, nil
}
func (env *Environment) validatePerPage(perPagePtr *int) int {
	if perPagePtr == nil {  
		return defaultPerPage
	}
	perPage := *perPagePtr
	if perPage < 1 {
		return defaultPerPage
	} else if perPage > maxPerPage && !env.Config.Unsafe {
		return maxPerPage
	}
	return perPage
}
func (env *Environment) InitGenesisChunks() error {
	if env.genChunks != nil {
		return nil
	}
	if env.GenDoc == nil {
		return nil
	}
	data, err := tmjson.Marshal(env.GenDoc)
	if err != nil {
		return err
	}
	for i := 0; i < len(data); i += genesisChunkSize {
		end := i + genesisChunkSize
		if end > len(data) {
			end = len(data)
		}
		env.genChunks = append(env.genChunks, base64.StdEncoding.EncodeToString(data[i:end]))
	}
	return nil
}
func validateSkipCount(page, perPage int) int {
	skipCount := (page - 1) * perPage
	if skipCount < 0 {
		return 0
	}
	return skipCount
}