github.com/project-88388/tendermint-v0.34.14-terra.2@v1.0.0/rpc/core/blocks.go (about) 1 package core 2 3 import ( 4 "errors" 5 "fmt" 6 "sort" 7 8 tmmath "github.com/tendermint/tendermint/libs/math" 9 tmquery "github.com/tendermint/tendermint/libs/pubsub/query" 10 ctypes "github.com/tendermint/tendermint/rpc/core/types" 11 rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" 12 blockidxnull "github.com/tendermint/tendermint/state/indexer/block/null" 13 "github.com/tendermint/tendermint/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/master/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/master/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/master/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/master/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 // 137 // Results are for the height of the block containing the txs. 138 // Thus response.results.deliver_tx[5] is the results of executing 139 // getBlock(h).Txs[5] 140 // More: https://docs.tendermint.com/master/rpc/#/Info/block_results 141 func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) { 142 height, err := getHeight(env.BlockStore.Height(), heightPtr) 143 if err != nil { 144 return nil, err 145 } 146 147 results, err := env.StateStore.LoadABCIResponses(height) 148 if err != nil { 149 return nil, err 150 } 151 152 return &ctypes.ResultBlockResults{ 153 Height: height, 154 TxsResults: results.DeliverTxs, 155 BeginBlockEvents: results.BeginBlock.Events, 156 EndBlockEvents: results.EndBlock.Events, 157 ValidatorUpdates: results.EndBlock.ValidatorUpdates, 158 ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates, 159 }, nil 160 } 161 162 // BlockSearch searches for a paginated set of blocks matching BeginBlock and 163 // EndBlock event search criteria. 164 func BlockSearch( 165 ctx *rpctypes.Context, 166 query string, 167 pagePtr, perPagePtr *int, 168 orderBy string, 169 ) (*ctypes.ResultBlockSearch, error) { 170 171 // skip if block indexing is disabled 172 if _, ok := env.BlockIndexer.(*blockidxnull.BlockerIndexer); ok { 173 return nil, errors.New("block indexing is disabled") 174 } 175 176 q, err := tmquery.New(query) 177 if err != nil { 178 return nil, err 179 } 180 181 results, err := env.BlockIndexer.Search(ctx.Context(), q) 182 if err != nil { 183 return nil, err 184 } 185 186 // sort results (must be done before pagination) 187 switch orderBy { 188 case "desc", "": 189 sort.Slice(results, func(i, j int) bool { return results[i] > results[j] }) 190 191 case "asc": 192 sort.Slice(results, func(i, j int) bool { return results[i] < results[j] }) 193 194 default: 195 return nil, errors.New("expected order_by to be either `asc` or `desc` or empty") 196 } 197 198 // paginate results 199 totalCount := len(results) 200 perPage := validatePerPage(perPagePtr) 201 202 page, err := validatePage(pagePtr, perPage, totalCount) 203 if err != nil { 204 return nil, err 205 } 206 207 skipCount := validateSkipCount(page, perPage) 208 pageSize := tmmath.MinInt(perPage, totalCount-skipCount) 209 210 apiResults := make([]*ctypes.ResultBlock, 0, pageSize) 211 for i := skipCount; i < skipCount+pageSize; i++ { 212 block := env.BlockStore.LoadBlock(results[i]) 213 if block != nil { 214 blockMeta := env.BlockStore.LoadBlockMeta(block.Height) 215 if blockMeta != nil { 216 apiResults = append(apiResults, &ctypes.ResultBlock{ 217 Block: block, 218 BlockID: blockMeta.BlockID, 219 }) 220 } 221 } 222 } 223 224 return &ctypes.ResultBlockSearch{Blocks: apiResults, TotalCount: totalCount}, nil 225 }