github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/tx/query.go (about)

     1  package tx
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"time"
     9  
    10  	coretypes "github.com/cometbft/cometbft/rpc/core/types"
    11  
    12  	"github.com/cosmos/cosmos-sdk/client"
    13  	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
    14  	sdk "github.com/cosmos/cosmos-sdk/types"
    15  	querytypes "github.com/cosmos/cosmos-sdk/types/query"
    16  )
    17  
    18  // QueryTxsByEvents retrieves a list of paginated transactions from CometBFT's
    19  // TxSearch RPC method given a set of pagination criteria and an events query.
    20  // Note, the events query must be valid based on CometBFT's query semantics.
    21  // An error is returned if the query or parsing fails or if the query is empty.
    22  //
    23  // Note, if an empty orderBy is provided, the default behavior is ascending. If
    24  // negative values are provided for page or limit, defaults will be used.
    25  func QueryTxsByEvents(clientCtx client.Context, page, limit int, query, orderBy string) (*sdk.SearchTxsResult, error) {
    26  	if len(query) == 0 {
    27  		return nil, errors.New("query cannot be empty")
    28  	}
    29  
    30  	// CometBFT node.TxSearch that is used for querying txs defines pages
    31  	// starting from 1, so we default to 1 if not provided in the request.
    32  	if page <= 0 {
    33  		page = 1
    34  	}
    35  
    36  	if limit <= 0 {
    37  		limit = querytypes.DefaultLimit
    38  	}
    39  
    40  	node, err := clientCtx.GetNode()
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	resTxs, err := node.TxSearch(context.Background(), query, false, &page, &limit, orderBy)
    46  	if err != nil {
    47  		return nil, fmt.Errorf("failed to search for txs: %w", err)
    48  	}
    49  
    50  	resBlocks, err := getBlocksForTxResults(clientCtx, resTxs.Txs)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	txs, err := formatTxResults(clientCtx.TxConfig, resTxs.Txs, resBlocks)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	return sdk.NewSearchTxsResult(uint64(resTxs.TotalCount), uint64(len(txs)), uint64(page), uint64(limit), txs), nil
    61  }
    62  
    63  // QueryTx queries for a single transaction by a hash string in hex format. An
    64  // error is returned if the transaction does not exist or cannot be queried.
    65  func QueryTx(clientCtx client.Context, hashHexStr string) (*sdk.TxResponse, error) {
    66  	hash, err := hex.DecodeString(hashHexStr)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	node, err := clientCtx.GetNode()
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	// TODO: this may not always need to be proven
    77  	// https://github.com/cosmos/cosmos-sdk/issues/6807
    78  	resTx, err := node.Tx(context.Background(), hash, true)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	resBlocks, err := getBlocksForTxResults(clientCtx, []*coretypes.ResultTx{resTx})
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	out, err := mkTxResult(clientCtx.TxConfig, resTx, resBlocks[resTx.Height])
    89  	if err != nil {
    90  		return out, err
    91  	}
    92  
    93  	return out, nil
    94  }
    95  
    96  // formatTxResults parses the indexed txs into a slice of TxResponse objects.
    97  func formatTxResults(txConfig client.TxConfig, resTxs []*coretypes.ResultTx, resBlocks map[int64]*coretypes.ResultBlock) ([]*sdk.TxResponse, error) {
    98  	var err error
    99  	out := make([]*sdk.TxResponse, len(resTxs))
   100  	for i := range resTxs {
   101  		out[i], err = mkTxResult(txConfig, resTxs[i], resBlocks[resTxs[i].Height])
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  	}
   106  
   107  	return out, nil
   108  }
   109  
   110  func getBlocksForTxResults(clientCtx client.Context, resTxs []*coretypes.ResultTx) (map[int64]*coretypes.ResultBlock, error) {
   111  	node, err := clientCtx.GetNode()
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	resBlocks := make(map[int64]*coretypes.ResultBlock)
   117  
   118  	for _, resTx := range resTxs {
   119  		if _, ok := resBlocks[resTx.Height]; !ok {
   120  			resBlock, err := node.Block(context.Background(), &resTx.Height)
   121  			if err != nil {
   122  				return nil, err
   123  			}
   124  
   125  			resBlocks[resTx.Height] = resBlock
   126  		}
   127  	}
   128  
   129  	return resBlocks, nil
   130  }
   131  
   132  func mkTxResult(txConfig client.TxConfig, resTx *coretypes.ResultTx, resBlock *coretypes.ResultBlock) (*sdk.TxResponse, error) {
   133  	txb, err := txConfig.TxDecoder()(resTx.Tx)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	p, ok := txb.(intoAny)
   138  	if !ok {
   139  		return nil, fmt.Errorf("expecting a type implementing intoAny, got: %T", txb)
   140  	}
   141  	any := p.AsAny()
   142  	return sdk.NewResponseResultTx(resTx, any, resBlock.Block.Time.Format(time.RFC3339)), nil
   143  }
   144  
   145  // Deprecated: this interface is used only internally for scenario we are
   146  // deprecating (StdTxConfig support)
   147  type intoAny interface {
   148  	AsAny() *codectypes.Any
   149  }