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 }