github.com/okex/exchain@v1.8.0/libs/tendermint/rpc/core/blocks.go (about)

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