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