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 }