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  }