- package core
-
- import (
- "fmt"
-
- tmmath "github.com/tendermint/tendermint/libs/math"
- ctypes "github.com/tendermint/tendermint/rpc/core/types"
- rpctypes "github.com/tendermint/tendermint/rpc/lib/types"
- sm "github.com/tendermint/tendermint/state"
- "github.com/tendermint/tendermint/types"
- )
-
- // BlockchainInfo gets block headers for minHeight <= height <= maxHeight.
- // Block headers are returned in descending order (highest first).
- // More: https://docs.tendermint.com/master/rpc/#/Info/blockchain
- func BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
- // maximum 20 block metas
- const limit int64 = 20
- var err error
- minHeight, maxHeight, err = filterMinMax(blockStore.Base(), blockStore.Height(), minHeight, maxHeight, limit)
- if err != nil {
- return nil, err
- }
- logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight)
-
- blockMetas := []*types.BlockMeta{}
- for height := maxHeight; height >= minHeight; height-- {
- blockMeta := blockStore.LoadBlockMeta(height)
- blockMetas = append(blockMetas, blockMeta)
- }
-
- return &ctypes.ResultBlockchainInfo{
- LastHeight: blockStore.Height(),
- BlockMetas: blockMetas}, nil
- }
-
- // error if either min or max are negative or min > max
- // if 0, use blockstore base for min, latest block height for max
- // enforce limit.
- func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
- // filter negatives
- if min < 0 || max < 0 {
- return min, max, fmt.Errorf("heights must be non-negative")
- }
-
- // adjust for default values
- if min == 0 {
- min = 1
- }
- if max == 0 {
- max = height
- }
-
- // limit max to the height
- max = tmmath.MinInt64(height, max)
-
- // limit min to the base
- min = tmmath.MaxInt64(base, min)
-
- // limit min to within `limit` of max
- // so the total number of blocks returned will be `limit`
- min = tmmath.MaxInt64(min, max-limit+1)
-
- if min > max {
- return min, max, fmt.Errorf("min height %d can't be greater than max height %d", min, max)
- }
- return min, max, nil
- }
-
- // Block gets block at a given height.
- // If no height is provided, it will fetch the latest block.
- // More: https://docs.tendermint.com/master/rpc/#/Info/block
- func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) {
- height, err := getHeight(blockStore.Base(), blockStore.Height(), heightPtr)
- if err != nil {
- return nil, err
- }
-
- block := blockStore.LoadBlock(height)
- blockMeta := blockStore.LoadBlockMeta(height)
- if blockMeta == nil {
- return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: block}, nil
- }
- return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
- }
-
- // BlockByHash gets block by hash.
- // More: https://docs.tendermint.com/master/rpc/#/Info/block_by_hash
- func BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
- block := blockStore.LoadBlockByHash(hash)
- if block == nil {
- return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
- }
- // If block is not nil, then blockMeta can't be nil.
- blockMeta := blockStore.LoadBlockMeta(block.Height)
- return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
- }
-
- // Commit gets block commit at a given height.
- // If no height is provided, it will fetch the commit for the latest block.
- // More: https://docs.tendermint.com/master/rpc/#/Info/commit
- func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) {
- height, err := getHeight(blockStore.Base(), blockStore.Height(), heightPtr)
- if err != nil {
- return nil, err
- }
-
- blockMeta := blockStore.LoadBlockMeta(height)
- if blockMeta == nil {
- return nil, nil
- }
- header := blockMeta.Header
-
- // If the next block has not been committed yet,
- // use a non-canonical commit
- if height == blockStore.Height() {
- commit := blockStore.LoadSeenCommit(height)
- return ctypes.NewResultCommit(&header, commit, false), nil
- }
-
- // Return the canonical commit (comes from the block at height+1)
- commit := blockStore.LoadBlockCommit(height)
- return ctypes.NewResultCommit(&header, commit, true), nil
- }
-
- // BlockResults gets ABCIResults at a given height.
- // If no height is provided, it will fetch results for the latest block.
- //
- // Results are for the height of the block containing the txs.
- // Thus response.results.deliver_tx[5] is the results of executing
- // getBlock(h).Txs[5]
- // More: https://docs.tendermint.com/master/rpc/#/Info/block_results
- func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) {
- height, err := getHeight(blockStore.Base(), blockStore.Height(), heightPtr)
- if err != nil {
- return nil, err
- }
-
- results, err := sm.LoadABCIResponses(stateDB, height)
- if err != nil {
- return nil, err
- }
-
- return &ctypes.ResultBlockResults{
- Height: height,
- TxsResults: results.DeliverTxs,
- BeginBlockEvents: results.BeginBlock.Events,
- EndBlockEvents: results.EndBlock.Events,
- ValidatorUpdates: results.EndBlock.ValidatorUpdates,
- ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
- }, nil
- }
-
- func getHeight(currentBase int64, currentHeight int64, heightPtr *int64) (int64, error) {
- if heightPtr != nil {
- height := *heightPtr
- if height <= 0 {
- return 0, fmt.Errorf("height must be greater than 0")
- }
- if height > currentHeight {
- return 0, fmt.Errorf("height must be less than or equal to the current blockchain height")
- }
- if height < currentBase {
- return 0, fmt.Errorf("height %v is not available, blocks pruned at height %v",
- height, currentBase)
- }
- return height, nil
- }
- return currentHeight, nil
- }
|