github.com/evdatsion/aphelion-dpos-bft@v0.32.1/rpc/core/blocks.go (about) 1 package core 2 3 import ( 4 "fmt" 5 6 cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common" 7 ctypes "github.com/evdatsion/aphelion-dpos-bft/rpc/core/types" 8 rpctypes "github.com/evdatsion/aphelion-dpos-bft/rpc/lib/types" 9 sm "github.com/evdatsion/aphelion-dpos-bft/state" 10 "github.com/evdatsion/aphelion-dpos-bft/types" 11 ) 12 13 // Get block headers for minHeight <= height <= maxHeight. 14 // Block headers are returned in descending order (highest first). 15 // 16 // ```shell 17 // curl 'localhost:26657/blockchain?minHeight=10&maxHeight=10' 18 // ``` 19 // 20 // ```go 21 // client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket") 22 // err := client.Start() 23 // if err != nil { 24 // // handle error 25 // } 26 // defer client.Stop() 27 // info, err := client.BlockchainInfo(10, 10) 28 // ``` 29 // 30 // > The above command returns JSON structured like this: 31 // 32 // ```json 33 // { 34 // "error": "", 35 // "result": { 36 // "block_metas": [ 37 // { 38 // "header": { 39 // "app_hash": "", 40 // "chain_id": "test-chain-6UTNIN", 41 // "height": "10", 42 // "time": "2017-05-29T15:05:53.877Z", 43 // "num_txs": "0", 44 // "last_block_id": { 45 // "parts": { 46 // "hash": "3C78F00658E06744A88F24FF97A0A5011139F34A", 47 // "total": "1" 48 // }, 49 // "hash": "F70588DAB36BDA5A953D548A16F7D48C6C2DFD78" 50 // }, 51 // "last_commit_hash": "F31CC4282E50B3F2A58D763D233D76F26D26CABE", 52 // "data_hash": "", 53 // "validators_hash": "9365FC80F234C967BD233F5A3E2AB2F1E4B0E5AA" 54 // }, 55 // "block_id": { 56 // "parts": { 57 // "hash": "277A4DBEF91483A18B85F2F5677ABF9694DFA40F", 58 // "total": "1" 59 // }, 60 // "hash": "96B1D2F2D201BA4BC383EB8224139DB1294944E5" 61 // } 62 // } 63 // ], 64 // "last_height": "5493" 65 // }, 66 // "id": "", 67 // "jsonrpc": "2.0" 68 // } 69 // ``` 70 // 71 // <aside class="notice">Returns at most 20 items.</aside> 72 func BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { 73 74 // maximum 20 block metas 75 const limit int64 = 20 76 var err error 77 minHeight, maxHeight, err = filterMinMax(blockStore.Height(), minHeight, maxHeight, limit) 78 if err != nil { 79 return nil, err 80 } 81 logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight) 82 83 blockMetas := []*types.BlockMeta{} 84 for height := maxHeight; height >= minHeight; height-- { 85 blockMeta := blockStore.LoadBlockMeta(height) 86 blockMetas = append(blockMetas, blockMeta) 87 } 88 89 return &ctypes.ResultBlockchainInfo{ 90 LastHeight: blockStore.Height(), 91 BlockMetas: blockMetas}, nil 92 } 93 94 // error if either min or max are negative or min < max 95 // if 0, use 1 for min, latest block height for max 96 // enforce limit. 97 // error if min > max 98 func filterMinMax(height, min, max, limit int64) (int64, int64, error) { 99 // filter negatives 100 if min < 0 || max < 0 { 101 return min, max, fmt.Errorf("heights must be non-negative") 102 } 103 104 // adjust for default values 105 if min == 0 { 106 min = 1 107 } 108 if max == 0 { 109 max = height 110 } 111 112 // limit max to the height 113 max = cmn.MinInt64(height, max) 114 115 // limit min to within `limit` of max 116 // so the total number of blocks returned will be `limit` 117 min = cmn.MaxInt64(min, max-limit+1) 118 119 if min > max { 120 return min, max, fmt.Errorf("min height %d can't be greater than max height %d", min, max) 121 } 122 return min, max, nil 123 } 124 125 // Get block at a given height. 126 // If no height is provided, it will fetch the latest block. 127 // 128 // ```shell 129 // curl 'localhost:26657/block?height=10' 130 // ``` 131 // 132 // ```go 133 // client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket") 134 // err := client.Start() 135 // if err != nil { 136 // // handle error 137 // } 138 // defer client.Stop() 139 // info, err := client.Block(10) 140 // ``` 141 // 142 // > The above command returns JSON structured like this: 143 // 144 // ```json 145 // { 146 // "error": "", 147 // "result": { 148 // "block": { 149 // "last_commit": { 150 // "precommits": [ 151 // { 152 // "signature": { 153 // "data": "12C0D8893B8A38224488DC1DE6270DF76BB1A5E9DB1C68577706A6A97C6EC34FFD12339183D5CA8BC2F46148773823DE905B7F6F5862FD564038BB7AE03BF50D", 154 // "type": "ed25519" 155 // }, 156 // "block_id": { 157 // "parts": { 158 // "hash": "3C78F00658E06744A88F24FF97A0A5011139F34A", 159 // "total": "1" 160 // }, 161 // "hash": "F70588DAB36BDA5A953D548A16F7D48C6C2DFD78" 162 // }, 163 // "type": "2", 164 // "round": "0", 165 // "height": "9", 166 // "validator_index": "0", 167 // "validator_address": "E89A51D60F68385E09E716D353373B11F8FACD62" 168 // } 169 // ], 170 // "blockID": { 171 // "parts": { 172 // "hash": "3C78F00658E06744A88F24FF97A0A5011139F34A", 173 // "total": "1" 174 // }, 175 // "hash": "F70588DAB36BDA5A953D548A16F7D48C6C2DFD78" 176 // } 177 // }, 178 // "data": { 179 // "txs": [] 180 // }, 181 // "header": { 182 // "app_hash": "", 183 // "chain_id": "test-chain-6UTNIN", 184 // "height": "10", 185 // "time": "2017-05-29T15:05:53.877Z", 186 // "num_txs": "0", 187 // "last_block_id": { 188 // "parts": { 189 // "hash": "3C78F00658E06744A88F24FF97A0A5011139F34A", 190 // "total": "1" 191 // }, 192 // "hash": "F70588DAB36BDA5A953D548A16F7D48C6C2DFD78" 193 // }, 194 // "last_commit_hash": "F31CC4282E50B3F2A58D763D233D76F26D26CABE", 195 // "data_hash": "", 196 // "validators_hash": "9365FC80F234C967BD233F5A3E2AB2F1E4B0E5AA" 197 // } 198 // }, 199 // "block_meta": { 200 // "header": { 201 // "app_hash": "", 202 // "chain_id": "test-chain-6UTNIN", 203 // "height": "10", 204 // "time": "2017-05-29T15:05:53.877Z", 205 // "num_txs": "0", 206 // "last_block_id": { 207 // "parts": { 208 // "hash": "3C78F00658E06744A88F24FF97A0A5011139F34A", 209 // "total": "1" 210 // }, 211 // "hash": "F70588DAB36BDA5A953D548A16F7D48C6C2DFD78" 212 // }, 213 // "last_commit_hash": "F31CC4282E50B3F2A58D763D233D76F26D26CABE", 214 // "data_hash": "", 215 // "validators_hash": "9365FC80F234C967BD233F5A3E2AB2F1E4B0E5AA" 216 // }, 217 // "block_id": { 218 // "parts": { 219 // "hash": "277A4DBEF91483A18B85F2F5677ABF9694DFA40F", 220 // "total": "1" 221 // }, 222 // "hash": "96B1D2F2D201BA4BC383EB8224139DB1294944E5" 223 // } 224 // } 225 // }, 226 // "id": "", 227 // "jsonrpc": "2.0" 228 // } 229 // ``` 230 func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) { 231 storeHeight := blockStore.Height() 232 height, err := getHeight(storeHeight, heightPtr) 233 if err != nil { 234 return nil, err 235 } 236 237 blockMeta := blockStore.LoadBlockMeta(height) 238 block := blockStore.LoadBlock(height) 239 return &ctypes.ResultBlock{BlockMeta: blockMeta, Block: block}, nil 240 } 241 242 // Get block commit at a given height. 243 // If no height is provided, it will fetch the commit for the latest block. 244 // 245 // ```shell 246 // curl 'localhost:26657/commit?height=11' 247 // ``` 248 // 249 // ```go 250 // client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket") 251 // err := client.Start() 252 // if err != nil { 253 // // handle error 254 // } 255 // defer client.Stop() 256 // info, err := client.Commit(11) 257 // ``` 258 // 259 // > The above command returns JSON structured like this: 260 // 261 // ```json 262 // { 263 // "error": "", 264 // "result": { 265 // "canonical": true, 266 // "commit": { 267 // "precommits": [ 268 // { 269 // "signature": { 270 // "data": "00970429FEC652E9E21D106A90AE8C5413759A7488775CEF4A3F44DC46C7F9D941070E4FBE9ED54DF247FA3983359A0C3A238D61DE55C75C9116D72ABC9CF50F", 271 // "type": "ed25519" 272 // }, 273 // "block_id": { 274 // "parts": { 275 // "hash": "9E37CBF266BC044A779E09D81C456E653B89E006", 276 // "total": "1" 277 // }, 278 // "hash": "CC6E861E31CA4334E9888381B4A9137D1458AB6A" 279 // }, 280 // "type": "2", 281 // "round": "0", 282 // "height": "11", 283 // "validator_index": "0", 284 // "validator_address": "E89A51D60F68385E09E716D353373B11F8FACD62" 285 // } 286 // ], 287 // "blockID": { 288 // "parts": { 289 // "hash": "9E37CBF266BC044A779E09D81C456E653B89E006", 290 // "total": "1" 291 // }, 292 // "hash": "CC6E861E31CA4334E9888381B4A9137D1458AB6A" 293 // } 294 // }, 295 // "header": { 296 // "app_hash": "", 297 // "chain_id": "test-chain-6UTNIN", 298 // "height": "11", 299 // "time": "2017-05-29T15:05:54.893Z", 300 // "num_txs": "0", 301 // "last_block_id": { 302 // "parts": { 303 // "hash": "277A4DBEF91483A18B85F2F5677ABF9694DFA40F", 304 // "total": "1" 305 // }, 306 // "hash": "96B1D2F2D201BA4BC383EB8224139DB1294944E5" 307 // }, 308 // "last_commit_hash": "3CE0C9727CE524BA9CB7C91E28F08E2B94001087", 309 // "data_hash": "", 310 // "validators_hash": "9365FC80F234C967BD233F5A3E2AB2F1E4B0E5AA" 311 // } 312 // }, 313 // "id": "", 314 // "jsonrpc": "2.0" 315 // } 316 // ``` 317 func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) { 318 storeHeight := blockStore.Height() 319 height, err := getHeight(storeHeight, heightPtr) 320 if err != nil { 321 return nil, err 322 } 323 324 header := blockStore.LoadBlockMeta(height).Header 325 326 // If the next block has not been committed yet, 327 // use a non-canonical commit 328 if height == storeHeight { 329 commit := blockStore.LoadSeenCommit(height) 330 return ctypes.NewResultCommit(&header, commit, false), nil 331 } 332 333 // Return the canonical commit (comes from the block at height+1) 334 commit := blockStore.LoadBlockCommit(height) 335 return ctypes.NewResultCommit(&header, commit, true), nil 336 } 337 338 // BlockResults gets ABCIResults at a given height. 339 // If no height is provided, it will fetch results for the latest block. 340 // 341 // Results are for the height of the block containing the txs. 342 // Thus response.results.deliver_tx[5] is the results of executing 343 // getBlock(h).Txs[5] 344 // 345 // ```shell 346 // curl 'localhost:26657/block_results?height=10' 347 // ``` 348 // 349 // ```go 350 // client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket") 351 // err := client.Start() 352 // if err != nil { 353 // // handle error 354 // } 355 // defer client.Stop() 356 // info, err := client.BlockResults(10) 357 // ``` 358 // 359 // 360 // > The above command returns JSON structured like this: 361 // 362 // ```json 363 // { 364 // "jsonrpc": "2.0", 365 // "id": "", 366 // "result": { 367 // "height": "39", 368 // "results": { 369 // "deliver_tx": [ 370 // { 371 // "tags": [ 372 // { 373 // "key": "YXBwLmNyZWF0b3I=", 374 // "value": "Q29zbW9zaGkgTmV0b3dva28=" 375 // } 376 // ] 377 // } 378 // ], 379 // "end_block": { 380 // "validator_updates": null 381 // }, 382 // "begin_block": {} 383 // } 384 // } 385 // } 386 // ``` 387 func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) { 388 storeHeight := blockStore.Height() 389 height, err := getHeight(storeHeight, heightPtr) 390 if err != nil { 391 return nil, err 392 } 393 394 results, err := sm.LoadABCIResponses(stateDB, height) 395 if err != nil { 396 return nil, err 397 } 398 399 res := &ctypes.ResultBlockResults{ 400 Height: height, 401 Results: results, 402 } 403 return res, nil 404 } 405 406 func getHeight(currentHeight int64, heightPtr *int64) (int64, error) { 407 if heightPtr != nil { 408 height := *heightPtr 409 if height <= 0 { 410 return 0, fmt.Errorf("Height must be greater than 0") 411 } 412 if height > currentHeight { 413 return 0, fmt.Errorf("Height must be less than or equal to the current blockchain height") 414 } 415 return height, nil 416 } 417 return currentHeight, nil 418 }