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.

231 lines
6.6 KiB

7 years ago
  1. package core
  2. import (
  3. "fmt"
  4. cmn "github.com/tendermint/tendermint/libs/common"
  5. tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
  6. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  7. "github.com/tendermint/tendermint/state/txindex/null"
  8. "github.com/tendermint/tendermint/types"
  9. )
  10. // Tx allows you to query the transaction results. `nil` could mean the
  11. // transaction is in the mempool, invalidated, or was not sent in the first
  12. // place.
  13. //
  14. // ```shell
  15. // curl "localhost:26657/tx?hash=0xF87370F68C82D9AC7201248ECA48CEC5F16FFEC99C461C1B2961341A2FE9C1C8"
  16. // ```
  17. //
  18. // ```go
  19. // client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
  20. // err := client.Start()
  21. // if err != nil {
  22. // // handle error
  23. // }
  24. // defer client.Stop()
  25. // hashBytes, err := hex.DecodeString("F87370F68C82D9AC7201248ECA48CEC5F16FFEC99C461C1B2961341A2FE9C1C8")
  26. // tx, err := client.Tx(hashBytes, true)
  27. // ```
  28. //
  29. // > The above command returns JSON structured like this:
  30. //
  31. // ```json
  32. // {
  33. // "error": "",
  34. // "result": {
  35. // "proof": {
  36. // "Proof": {
  37. // "aunts": []
  38. // },
  39. // "Data": "YWJjZA==",
  40. // "RootHash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF",
  41. // "Total": "1",
  42. // "Index": "0"
  43. // },
  44. // "tx": "YWJjZA==",
  45. // "tx_result": {
  46. // "log": "",
  47. // "data": "",
  48. // "code": "0"
  49. // },
  50. // "index": "0",
  51. // "height": "52",
  52. // "hash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF"
  53. // },
  54. // "id": "",
  55. // "jsonrpc": "2.0"
  56. // }
  57. // ```
  58. //
  59. // Returns a transaction matching the given transaction hash.
  60. //
  61. // ### Query Parameters
  62. //
  63. // | Parameter | Type | Default | Required | Description |
  64. // |-----------+--------+---------+----------+-----------------------------------------------------------|
  65. // | hash | []byte | nil | true | The transaction hash |
  66. // | prove | bool | false | false | Include a proof of the transaction inclusion in the block |
  67. //
  68. // ### Returns
  69. //
  70. // - `proof`: the `types.TxProof` object
  71. // - `tx`: `[]byte` - the transaction
  72. // - `tx_result`: the `abci.Result` object
  73. // - `index`: `int` - index of the transaction
  74. // - `height`: `int` - height of the block where this transaction was in
  75. // - `hash`: `[]byte` - hash of the transaction
  76. func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
  77. // if index is disabled, return error
  78. if _, ok := txIndexer.(*null.TxIndex); ok {
  79. return nil, fmt.Errorf("Transaction indexing is disabled")
  80. }
  81. r, err := txIndexer.Get(hash)
  82. if err != nil {
  83. return nil, err
  84. }
  85. if r == nil {
  86. return nil, fmt.Errorf("Tx (%X) not found", hash)
  87. }
  88. height := r.Height
  89. index := r.Index
  90. var proof types.TxProof
  91. if prove {
  92. block := blockStore.LoadBlock(height)
  93. proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines
  94. }
  95. return &ctypes.ResultTx{
  96. Hash: hash,
  97. Height: height,
  98. Index: uint32(index),
  99. TxResult: r.Result,
  100. Tx: r.Tx,
  101. Proof: proof,
  102. }, nil
  103. }
  104. // TxSearch allows you to query for multiple transactions results. It returns a
  105. // list of transactions (maximum ?per_page entries) and the total count.
  106. //
  107. // ```shell
  108. // curl "localhost:26657/tx_search?query=\"account.owner='Ivan'\"&prove=true"
  109. // ```
  110. //
  111. // ```go
  112. // client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
  113. // err := client.Start()
  114. // if err != nil {
  115. // // handle error
  116. // }
  117. // defer client.Stop()
  118. // q, err := tmquery.New("account.owner='Ivan'")
  119. // tx, err := client.TxSearch(q, true)
  120. // ```
  121. //
  122. // > The above command returns JSON structured like this:
  123. //
  124. // ```json
  125. // {
  126. // "jsonrpc": "2.0",
  127. // "id": "",
  128. // "result": {
  129. // "txs": [
  130. // {
  131. // "proof": {
  132. // "Proof": {
  133. // "aunts": [
  134. // "J3LHbizt806uKnABNLwG4l7gXCA=",
  135. // "iblMO/M1TnNtlAefJyNCeVhjAb0=",
  136. // "iVk3ryurVaEEhdeS0ohAJZ3wtB8=",
  137. // "5hqMkTeGqpct51ohX0lZLIdsn7Q=",
  138. // "afhsNxFnLlZgFDoyPpdQSe0bR8g="
  139. // ]
  140. // },
  141. // "Data": "mvZHHa7HhZ4aRT0xMDA=",
  142. // "RootHash": "F6541223AA46E428CB1070E9840D2C3DF3B6D776",
  143. // "Total": "32",
  144. // "Index": "31"
  145. // },
  146. // "tx": "mvZHHa7HhZ4aRT0xMDA=",
  147. // "tx_result": {},
  148. // "index": "31",
  149. // "height": "12",
  150. // "hash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF"
  151. // }
  152. // ],
  153. // "total_count": "1"
  154. // }
  155. // }
  156. // ```
  157. //
  158. // ### Query Parameters
  159. //
  160. // | Parameter | Type | Default | Required | Description |
  161. // |-----------+--------+---------+----------+-----------------------------------------------------------|
  162. // | query | string | "" | true | Query |
  163. // | prove | bool | false | false | Include proofs of the transactions inclusion in the block |
  164. // | page | int | 1 | false | Page number (1-based) |
  165. // | per_page | int | 30 | false | Number of entries per page (max: 100) |
  166. //
  167. // ### Returns
  168. //
  169. // - `proof`: the `types.TxProof` object
  170. // - `tx`: `[]byte` - the transaction
  171. // - `tx_result`: the `abci.Result` object
  172. // - `index`: `int` - index of the transaction
  173. // - `height`: `int` - height of the block where this transaction was in
  174. // - `hash`: `[]byte` - hash of the transaction
  175. func TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
  176. // if index is disabled, return error
  177. if _, ok := txIndexer.(*null.TxIndex); ok {
  178. return nil, fmt.Errorf("Transaction indexing is disabled")
  179. }
  180. q, err := tmquery.New(query)
  181. if err != nil {
  182. return nil, err
  183. }
  184. results, err := txIndexer.Search(q)
  185. if err != nil {
  186. return nil, err
  187. }
  188. totalCount := len(results)
  189. perPage = validatePerPage(perPage)
  190. page = validatePage(page, perPage, totalCount)
  191. skipCount := validateSkipCount(page, perPage)
  192. apiResults := make([]*ctypes.ResultTx, cmn.MinInt(perPage, totalCount-skipCount))
  193. var proof types.TxProof
  194. // if there's no tx in the results array, we don't need to loop through the apiResults array
  195. for i := 0; i < len(apiResults); i++ {
  196. r := results[skipCount+i]
  197. height := r.Height
  198. index := r.Index
  199. if prove {
  200. block := blockStore.LoadBlock(height)
  201. proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines
  202. }
  203. apiResults[i] = &ctypes.ResultTx{
  204. Hash: r.Tx.Hash(),
  205. Height: height,
  206. Index: index,
  207. TxResult: r.Result,
  208. Tx: r.Tx,
  209. Proof: proof,
  210. }
  211. }
  212. return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil
  213. }