github.com/vipernet-xyz/tm@v0.34.24/rpc/core/blocks.go (about)

     1  package core
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  
     8  	tmmath "github.com/vipernet-xyz/tm/libs/math"
     9  	tmquery "github.com/vipernet-xyz/tm/libs/pubsub/query"
    10  	ctypes "github.com/vipernet-xyz/tm/rpc/core/types"
    11  	rpctypes "github.com/vipernet-xyz/tm/rpc/jsonrpc/types"
    12  	blockidxnull "github.com/vipernet-xyz/tm/state/indexer/block/null"
    13  	"github.com/vipernet-xyz/tm/types"
    14  )
    15  
    16  // BlockchainInfo gets block headers for minHeight <= height <= maxHeight.
    17  // Block headers are returned in descending order (highest first).
    18  // More: https://docs.tendermint.com/v0.34/rpc/#/Info/blockchain
    19  func BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
    20  	// maximum 20 block metas
    21  	const limit int64 = 20
    22  	var err error
    23  	minHeight, maxHeight, err = filterMinMax(
    24  		env.BlockStore.Base(),
    25  		env.BlockStore.Height(),
    26  		minHeight,
    27  		maxHeight,
    28  		limit)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	env.Logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight)
    33  
    34  	blockMetas := []*types.BlockMeta{}
    35  	for height := maxHeight; height >= minHeight; height-- {
    36  		blockMeta := env.BlockStore.LoadBlockMeta(height)
    37  		blockMetas = append(blockMetas, blockMeta)
    38  	}
    39  
    40  	return &ctypes.ResultBlockchainInfo{
    41  		LastHeight: env.BlockStore.Height(),
    42  		BlockMetas: blockMetas}, nil
    43  }
    44  
    45  // error if either min or max are negative or min > max
    46  // if 0, use blockstore base for min, latest block height for max
    47  // enforce limit.
    48  func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
    49  	// filter negatives
    50  	if min < 0 || max < 0 {
    51  		return min, max, fmt.Errorf("heights must be non-negative")
    52  	}
    53  
    54  	// adjust for default values
    55  	if min == 0 {
    56  		min = 1
    57  	}
    58  	if max == 0 {
    59  		max = height
    60  	}
    61  
    62  	// limit max to the height
    63  	max = tmmath.MinInt64(height, max)
    64  
    65  	// limit min to the base
    66  	min = tmmath.MaxInt64(base, min)
    67  
    68  	// limit min to within `limit` of max
    69  	// so the total number of blocks returned will be `limit`
    70  	min = tmmath.MaxInt64(min, max-limit+1)
    71  
    72  	if min > max {
    73  		return min, max, fmt.Errorf("min height %d can't be greater than max height %d", min, max)
    74  	}
    75  	return min, max, nil
    76  }
    77  
    78  // Block gets block at a given height.
    79  // If no height is provided, it will fetch the latest block.
    80  // More: https://docs.tendermint.com/v0.34/rpc/#/Info/block
    81  func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) {
    82  	height, err := getHeight(env.BlockStore.Height(), heightPtr)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	block := env.BlockStore.LoadBlock(height)
    88  	blockMeta := env.BlockStore.LoadBlockMeta(height)
    89  	if blockMeta == nil {
    90  		return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: block}, nil
    91  	}
    92  	return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
    93  }
    94  
    95  // BlockByHash gets block by hash.
    96  // More: https://docs.tendermint.com/v0.34/rpc/#/Info/block_by_hash
    97  func BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
    98  	block := env.BlockStore.LoadBlockByHash(hash)
    99  	if block == nil {
   100  		return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
   101  	}
   102  	// If block is not nil, then blockMeta can't be nil.
   103  	blockMeta := env.BlockStore.LoadBlockMeta(block.Height)
   104  	return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
   105  }
   106  
   107  // Commit gets block commit at a given height.
   108  // If no height is provided, it will fetch the commit for the latest block.
   109  // More: https://docs.tendermint.com/v0.34/rpc/#/Info/commit
   110  func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) {
   111  	height, err := getHeight(env.BlockStore.Height(), heightPtr)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	blockMeta := env.BlockStore.LoadBlockMeta(height)
   117  	if blockMeta == nil {
   118  		return nil, nil
   119  	}
   120  	header := blockMeta.Header
   121  
   122  	// If the next block has not been committed yet,
   123  	// use a non-canonical commit
   124  	if height == env.BlockStore.Height() {
   125  		commit := env.BlockStore.LoadSeenCommit(height)
   126  		return ctypes.NewResultCommit(&header, commit, false), nil
   127  	}
   128  
   129  	// Return the canonical commit (comes from the block at height+1)
   130  	commit := env.BlockStore.LoadBlockCommit(height)
   131  	return ctypes.NewResultCommit(&header, commit, true), nil
   132  }
   133  
   134  // BlockResults gets ABCIResults at a given height.
   135  // If no height is provided, it will fetch results for the latest block.
   136  // When DiscardABCIResponses is enabled, an error will be returned.
   137  //
   138  // Results are for the height of the block containing the txs.
   139  // Thus response.results.deliver_tx[5] is the results of executing
   140  // getBlock(h).Txs[5]
   141  // More: https://docs.tendermint.com/v0.34/rpc/#/Info/block_results
   142  func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) {
   143  	height, err := getHeight(env.BlockStore.Height(), heightPtr)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	results, err := env.StateStore.LoadABCIResponses(height)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	return &ctypes.ResultBlockResults{
   154  		Height:                height,
   155  		TxsResults:            results.DeliverTxs,
   156  		BeginBlockEvents:      results.BeginBlock.Events,
   157  		EndBlockEvents:        results.EndBlock.Events,
   158  		ValidatorUpdates:      results.EndBlock.ValidatorUpdates,
   159  		ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
   160  	}, nil
   161  }
   162  
   163  // BlockSearch searches for a paginated set of blocks matching BeginBlock and
   164  // EndBlock event search criteria.
   165  func BlockSearch(
   166  	ctx *rpctypes.Context,
   167  	query string,
   168  	pagePtr, perPagePtr *int,
   169  	orderBy string,
   170  ) (*ctypes.ResultBlockSearch, error) {
   171  
   172  	// skip if block indexing is disabled
   173  	if _, ok := env.BlockIndexer.(*blockidxnull.BlockerIndexer); ok {
   174  		return nil, errors.New("block indexing is disabled")
   175  	}
   176  
   177  	q, err := tmquery.New(query)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  
   182  	results, err := env.BlockIndexer.Search(ctx.Context(), q)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  
   187  	// sort results (must be done before pagination)
   188  	switch orderBy {
   189  	case "desc", "":
   190  		sort.Slice(results, func(i, j int) bool { return results[i] > results[j] })
   191  
   192  	case "asc":
   193  		sort.Slice(results, func(i, j int) bool { return results[i] < results[j] })
   194  
   195  	default:
   196  		return nil, errors.New("expected order_by to be either `asc` or `desc` or empty")
   197  	}
   198  
   199  	// paginate results
   200  	totalCount := len(results)
   201  	perPage := validatePerPage(perPagePtr)
   202  
   203  	page, err := validatePage(pagePtr, perPage, totalCount)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	skipCount := validateSkipCount(page, perPage)
   209  	pageSize := tmmath.MinInt(perPage, totalCount-skipCount)
   210  
   211  	apiResults := make([]*ctypes.ResultBlock, 0, pageSize)
   212  	for i := skipCount; i < skipCount+pageSize; i++ {
   213  		block := env.BlockStore.LoadBlock(results[i])
   214  		if block != nil {
   215  			blockMeta := env.BlockStore.LoadBlockMeta(block.Height)
   216  			if blockMeta != nil {
   217  				apiResults = append(apiResults, &ctypes.ResultBlock{
   218  					Block:   block,
   219  					BlockID: blockMeta.BlockID,
   220  				})
   221  			}
   222  		}
   223  	}
   224  
   225  	return &ctypes.ResultBlockSearch{Blocks: apiResults, TotalCount: totalCount}, nil
   226  }