github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/rpc/core/blocks.go (about)

     1  package core
     2  
     3  import (
     4  	"fmt"
     5  
     6  	tmmath "github.com/lazyledger/lazyledger-core/libs/math"
     7  	ctypes "github.com/lazyledger/lazyledger-core/rpc/core/types"
     8  	rpctypes "github.com/lazyledger/lazyledger-core/rpc/jsonrpc/types"
     9  	"github.com/lazyledger/lazyledger-core/types"
    10  )
    11  
    12  // BlockchainInfo gets block headers for minHeight <= height <= maxHeight.
    13  //
    14  // If maxHeight does not yet exist, blocks up to the current height will be
    15  // returned. If minHeight does not exist (due to pruning), earliest existing
    16  // height will be used.
    17  //
    18  // At most 20 items will be returned. Block headers are returned in descending
    19  // order (highest first).
    20  //
    21  // More: https://docs.tendermint.com/master/rpc/#/Info/blockchain
    22  func BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
    23  	const limit int64 = 20
    24  
    25  	var err error
    26  	minHeight, maxHeight, err = filterMinMax(
    27  		env.BlockStore.Base(),
    28  		env.BlockStore.Height(),
    29  		minHeight,
    30  		maxHeight,
    31  		limit)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	env.Logger.Debug("BlockchainInfo", "maxHeight", maxHeight, "minHeight", minHeight)
    36  
    37  	blockMetas := make([]*types.BlockMeta, 0, maxHeight-minHeight+1)
    38  	for height := maxHeight; height >= minHeight; height-- {
    39  		blockMeta := env.BlockStore.LoadBlockMeta(height)
    40  		if blockMeta != nil {
    41  			blockMetas = append(blockMetas, blockMeta)
    42  		}
    43  	}
    44  
    45  	return &ctypes.ResultBlockchainInfo{
    46  		LastHeight: env.BlockStore.Height(),
    47  		BlockMetas: blockMetas}, nil
    48  }
    49  
    50  // error if either min or max are negative or min > max
    51  // if 0, use blockstore base for min, latest block height for max
    52  // enforce limit.
    53  func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
    54  	// filter negatives
    55  	if min < 0 || max < 0 {
    56  		return min, max, fmt.Errorf("heights must be non-negative")
    57  	}
    58  
    59  	// adjust for default values
    60  	if min == 0 {
    61  		min = 1
    62  	}
    63  	if max == 0 {
    64  		max = height
    65  	}
    66  
    67  	// limit max to the height
    68  	max = tmmath.MinInt64(height, max)
    69  
    70  	// limit min to the base
    71  	min = tmmath.MaxInt64(base, min)
    72  
    73  	// limit min to within `limit` of max
    74  	// so the total number of blocks returned will be `limit`
    75  	min = tmmath.MaxInt64(min, max-limit+1)
    76  
    77  	if min > max {
    78  		return min, max, fmt.Errorf("min height %d can't be greater than max height %d", min, max)
    79  	}
    80  	return min, max, nil
    81  }
    82  
    83  // Block gets block at a given height.
    84  // If no height is provided, it will fetch the latest block.
    85  // More: https://docs.tendermint.com/master/rpc/#/Info/block
    86  func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) {
    87  	height, err := getHeight(env.BlockStore.Height(), heightPtr)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	block := env.BlockStore.LoadBlock(height)
    93  	blockMeta := env.BlockStore.LoadBlockMeta(height)
    94  	if blockMeta == nil {
    95  		return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: block}, nil
    96  	}
    97  	return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
    98  }
    99  
   100  // BlockByHash gets block by hash.
   101  // More: https://docs.tendermint.com/master/rpc/#/Info/block_by_hash
   102  func BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
   103  	block := env.BlockStore.LoadBlockByHash(hash)
   104  	if block == nil {
   105  		return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
   106  	}
   107  	// If block is not nil, then blockMeta can't be nil.
   108  	blockMeta := env.BlockStore.LoadBlockMeta(block.Height)
   109  	return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
   110  }
   111  
   112  // Commit gets block commit at a given height.
   113  // If no height is provided, it will fetch the commit for the latest block.
   114  // More: https://docs.tendermint.com/master/rpc/#/Info/commit
   115  func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) {
   116  	height, err := getHeight(env.BlockStore.Height(), heightPtr)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	blockMeta := env.BlockStore.LoadBlockMeta(height)
   122  	if blockMeta == nil {
   123  		return nil, nil
   124  	}
   125  	header := blockMeta.Header
   126  
   127  	// If the next block has not been committed yet,
   128  	// use a non-canonical commit
   129  	if height == env.BlockStore.Height() {
   130  		commit := env.BlockStore.LoadSeenCommit(height)
   131  		return ctypes.NewResultCommit(&header, commit, false), nil
   132  	}
   133  
   134  	// Return the canonical commit (comes from the block at height+1)
   135  	commit := env.BlockStore.LoadBlockCommit(height)
   136  	return ctypes.NewResultCommit(&header, commit, true), nil
   137  }
   138  
   139  // DataAvailabilityHeader TODO
   140  func DataAvailabilityHeader(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultDataAvailabilityHeader, error) {
   141  	height, err := getHeight(env.BlockStore.Height(), heightPtr)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	// TODO: store DAHeader to avoid loading the full block each time
   147  	// depends on either:
   148  	// - https://github.com/lazyledger/lazyledger-core/pull/312, or
   149  	// - https://github.com/lazyledger/lazyledger-core/pull/218
   150  	block := env.BlockStore.LoadBlock(height)
   151  	_ = block.Hash()
   152  	dah := block.DataAvailabilityHeader
   153  	return &ctypes.ResultDataAvailabilityHeader{
   154  		DataAvailabilityHeader: dah,
   155  	}, nil
   156  }
   157  
   158  // BlockResults gets ABCIResults at a given height.
   159  // If no height is provided, it will fetch results for the latest block.
   160  //
   161  // Results are for the height of the block containing the txs.
   162  // Thus response.results.deliver_tx[5] is the results of executing
   163  // getBlock(h).Txs[5]
   164  // More: https://docs.tendermint.com/master/rpc/#/Info/block_results
   165  func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) {
   166  	height, err := getHeight(env.BlockStore.Height(), heightPtr)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  
   171  	results, err := env.StateStore.LoadABCIResponses(height)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	return &ctypes.ResultBlockResults{
   177  		Height:                height,
   178  		TxsResults:            results.DeliverTxs,
   179  		BeginBlockEvents:      results.BeginBlock.Events,
   180  		EndBlockEvents:        results.EndBlock.Events,
   181  		ValidatorUpdates:      results.EndBlock.ValidatorUpdates,
   182  		ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
   183  	}, nil
   184  }