You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

385 lines
10 KiB

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