github.com/Finschia/finschia-sdk@v0.49.1/x/auth/tx/query.go (about)

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