|
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
|
|
}
|