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.

146 lines
4.2 KiB

7 years ago
  1. package core
  2. import (
  3. "errors"
  4. "fmt"
  5. "sort"
  6. "github.com/tendermint/tendermint/libs/bytes"
  7. tmmath "github.com/tendermint/tendermint/libs/math"
  8. tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
  9. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  10. rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
  11. "github.com/tendermint/tendermint/state/indexer"
  12. "github.com/tendermint/tendermint/types"
  13. )
  14. // Tx allows you to query the transaction results. `nil` could mean the
  15. // transaction is in the mempool, invalidated, or was not sent in the first
  16. // place.
  17. // More: https://docs.tendermint.com/master/rpc/#/Info/tx
  18. func (env *Environment) Tx(ctx *rpctypes.Context, hash bytes.HexBytes, prove bool) (*ctypes.ResultTx, error) {
  19. // if index is disabled, return error
  20. // N.B. The hash parameter is HexBytes so that the reflective parameter
  21. // decoding logic in the HTTP service will correctly translate from JSON.
  22. // See https://github.com/tendermint/tendermint/issues/6802 for context.
  23. if !indexer.KVSinkEnabled(env.EventSinks) {
  24. return nil, errors.New("transaction querying is disabled due to no kvEventSink")
  25. }
  26. for _, sink := range env.EventSinks {
  27. if sink.Type() == indexer.KV {
  28. r, err := sink.GetTxByHash(hash)
  29. if r == nil {
  30. return nil, fmt.Errorf("tx (%X) not found, err: %w", hash, err)
  31. }
  32. height := r.Height
  33. index := r.Index
  34. var proof types.TxProof
  35. if prove {
  36. block := env.BlockStore.LoadBlock(height)
  37. proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines
  38. }
  39. return &ctypes.ResultTx{
  40. Hash: hash,
  41. Height: height,
  42. Index: index,
  43. TxResult: r.Result,
  44. Tx: r.Tx,
  45. Proof: proof,
  46. }, nil
  47. }
  48. }
  49. return nil, fmt.Errorf("transaction querying is disabled on this node due to the KV event sink being disabled")
  50. }
  51. // TxSearch allows you to query for multiple transactions results. It returns a
  52. // list of transactions (maximum ?per_page entries) and the total count.
  53. // More: https://docs.tendermint.com/master/rpc/#/Info/tx_search
  54. func (env *Environment) TxSearch(
  55. ctx *rpctypes.Context,
  56. query string,
  57. prove bool,
  58. pagePtr, perPagePtr *int,
  59. orderBy string,
  60. ) (*ctypes.ResultTxSearch, error) {
  61. if !indexer.KVSinkEnabled(env.EventSinks) {
  62. return nil, fmt.Errorf("transaction searching is disabled due to no kvEventSink")
  63. }
  64. q, err := tmquery.New(query)
  65. if err != nil {
  66. return nil, err
  67. }
  68. for _, sink := range env.EventSinks {
  69. if sink.Type() == indexer.KV {
  70. results, err := sink.SearchTxEvents(ctx.Context(), q)
  71. if err != nil {
  72. return nil, err
  73. }
  74. // sort results (must be done before pagination)
  75. switch orderBy {
  76. case "desc", "":
  77. sort.Slice(results, func(i, j int) bool {
  78. if results[i].Height == results[j].Height {
  79. return results[i].Index > results[j].Index
  80. }
  81. return results[i].Height > results[j].Height
  82. })
  83. case "asc":
  84. sort.Slice(results, func(i, j int) bool {
  85. if results[i].Height == results[j].Height {
  86. return results[i].Index < results[j].Index
  87. }
  88. return results[i].Height < results[j].Height
  89. })
  90. default:
  91. return nil, fmt.Errorf("expected order_by to be either `asc` or `desc` or empty: %w", ctypes.ErrInvalidRequest)
  92. }
  93. // paginate results
  94. totalCount := len(results)
  95. perPage := env.validatePerPage(perPagePtr)
  96. page, err := validatePage(pagePtr, perPage, totalCount)
  97. if err != nil {
  98. return nil, err
  99. }
  100. skipCount := validateSkipCount(page, perPage)
  101. pageSize := tmmath.MinInt(perPage, totalCount-skipCount)
  102. apiResults := make([]*ctypes.ResultTx, 0, pageSize)
  103. for i := skipCount; i < skipCount+pageSize; i++ {
  104. r := results[i]
  105. var proof types.TxProof
  106. if prove {
  107. block := env.BlockStore.LoadBlock(r.Height)
  108. proof = block.Data.Txs.Proof(int(r.Index)) // XXX: overflow on 32-bit machines
  109. }
  110. apiResults = append(apiResults, &ctypes.ResultTx{
  111. Hash: types.Tx(r.Tx).Hash(),
  112. Height: r.Height,
  113. Index: r.Index,
  114. TxResult: r.Result,
  115. Tx: r.Tx,
  116. Proof: proof,
  117. })
  118. }
  119. return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil
  120. }
  121. }
  122. return nil, fmt.Errorf("transaction searching is disabled on this node due to the KV event sink being disabled")
  123. }