github.com/number571/tendermint@v0.34.11-gost/rpc/core/blocks.go (about)

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