## Description Closes: https://github.com/cosmos/cosmos-sdk/issues/11538 Also see thread after https://github.com/cosmos/cosmos-sdk/issues/11538#issuecomment-1150040922 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
154 lines
4.1 KiB
Go
154 lines
4.1 KiB
Go
package tx
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/tendermint/tendermint/rpc/coretypes"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
)
|
|
|
|
// QueryTxsByEvents performs a search for transactions for a given set of events
|
|
// via the Tendermint RPC. An event takes the form of:
|
|
// "{eventAttribute}.{attributeKey} = '{attributeValue}'". Each event is
|
|
// concatenated with an 'AND' operand. It returns a slice of Info object
|
|
// containing txs and metadata. An error is returned if the query fails.
|
|
// If an empty string is provided it will order txs by asc
|
|
func QueryTxsByEvents(clientCtx client.Context, events []string, page, limit int, orderBy string) (*sdk.SearchTxsResult, error) {
|
|
if len(events) == 0 {
|
|
return nil, errors.New("must declare at least one event to search")
|
|
}
|
|
|
|
if page <= 0 {
|
|
return nil, errors.New("page must be greater than 0")
|
|
}
|
|
|
|
if limit <= 0 {
|
|
return nil, errors.New("limit must be greater than 0")
|
|
}
|
|
|
|
// XXX: implement ANY
|
|
query := strings.Join(events, " AND ")
|
|
|
|
node, err := clientCtx.GetNode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO: this may not always need to be proven
|
|
// https://github.com/cosmos/cosmos-sdk/issues/6807
|
|
resTxs, err := node.TxSearch(context.Background(), query, true, &page, &limit, orderBy)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resBlocks, err := getBlocksForTxResults(clientCtx, resTxs.Txs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
txs, err := formatTxResults(clientCtx.TxConfig, resTxs.Txs, resBlocks)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := sdk.NewSearchTxsResult(uint64(resTxs.TotalCount), uint64(len(txs)), uint64(page), uint64(limit), txs)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// QueryTx queries for a single transaction by a hash string in hex format. An
|
|
// error is returned if the transaction does not exist or cannot be queried.
|
|
func QueryTx(clientCtx client.Context, hashHexStr string) (*sdk.TxResponse, error) {
|
|
hash, err := hex.DecodeString(hashHexStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
node, err := clientCtx.GetNode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO: this may not always need to be proven
|
|
// https://github.com/cosmos/cosmos-sdk/issues/6807
|
|
resTx, err := node.Tx(context.Background(), hash, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resBlocks, err := getBlocksForTxResults(clientCtx, []*coretypes.ResultTx{resTx})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
out, err := mkTxResult(clientCtx.TxConfig, resTx, resBlocks[resTx.Height])
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
// formatTxResults parses the indexed txs into a slice of TxResponse objects.
|
|
func formatTxResults(txConfig client.TxConfig, resTxs []*coretypes.ResultTx, resBlocks map[int64]*coretypes.ResultBlock) ([]*sdk.TxResponse, error) {
|
|
var err error
|
|
out := make([]*sdk.TxResponse, len(resTxs))
|
|
for i := range resTxs {
|
|
out[i], err = mkTxResult(txConfig, resTxs[i], resBlocks[resTxs[i].Height])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func getBlocksForTxResults(clientCtx client.Context, resTxs []*coretypes.ResultTx) (map[int64]*coretypes.ResultBlock, error) {
|
|
node, err := clientCtx.GetNode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resBlocks := make(map[int64]*coretypes.ResultBlock)
|
|
|
|
for _, resTx := range resTxs {
|
|
if _, ok := resBlocks[resTx.Height]; !ok {
|
|
resBlock, err := node.Block(context.Background(), &resTx.Height)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resBlocks[resTx.Height] = resBlock
|
|
}
|
|
}
|
|
|
|
return resBlocks, nil
|
|
}
|
|
|
|
func mkTxResult(txConfig client.TxConfig, resTx *coretypes.ResultTx, resBlock *coretypes.ResultBlock) (*sdk.TxResponse, error) {
|
|
txb, err := txConfig.TxDecoder()(resTx.Tx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p, ok := txb.(intoAny)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expecting a type implementing intoAny, got: %T", txb)
|
|
}
|
|
any := p.AsAny()
|
|
return sdk.NewResponseResultTx(resTx, any, resBlock.Block.Time.Format(time.RFC3339)), nil
|
|
}
|
|
|
|
// Deprecated: this interface is used only internally for scenario we are
|
|
// deprecating (StdTxConfig support)
|
|
type intoAny interface {
|
|
AsAny() *codectypes.Any
|
|
}
|