github.com/Oyster-zx/tendermint@v0.34.24-fork/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/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 }