cosmos-sdk/x/auth/client/query.go
Alessio Treglia b647824716
Refactor x/auth/client/utils/ (#5555)
Packages named utils, common, or misc provide clients with no
sense of what the package contains. This makes it harder for
clients to use the package and makes it harder for maintainers
to keep the package focused. Over time, they accumulate dependencies
that can make compilation significantly and unnecessarily slower,
especially in large programs. And since such package names are
generic, they are more likely to collide with other packages
imported by client code, forcing clients to invent names to
distinguish them.

 cit. https://blog.golang.org/package-names
2020-01-24 16:40:56 +00:00

181 lines
4.3 KiB
Go

package client
import (
"encoding/hex"
"errors"
"strings"
"time"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/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.
func QueryTxsByEvents(cliCtx context.CLIContext, events []string, page, limit int) (*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 greater than 0")
}
if limit <= 0 {
return nil, errors.New("limit must greater than 0")
}
// XXX: implement ANY
query := strings.Join(events, " AND ")
node, err := cliCtx.GetNode()
if err != nil {
return nil, err
}
prove := !cliCtx.TrustNode
resTxs, err := node.TxSearch(query, prove, page, limit)
if err != nil {
return nil, err
}
if prove {
for _, tx := range resTxs.Txs {
err := ValidateTxResult(cliCtx, tx)
if err != nil {
return nil, err
}
}
}
resBlocks, err := getBlocksForTxResults(cliCtx, resTxs.Txs)
if err != nil {
return nil, err
}
txs, err := formatTxResults(cliCtx.Codec, resTxs.Txs, resBlocks)
if err != nil {
return nil, err
}
result := sdk.NewSearchTxsResult(resTxs.TotalCount, len(txs), page, 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(cliCtx context.CLIContext, hashHexStr string) (sdk.TxResponse, error) {
hash, err := hex.DecodeString(hashHexStr)
if err != nil {
return sdk.TxResponse{}, err
}
node, err := cliCtx.GetNode()
if err != nil {
return sdk.TxResponse{}, err
}
resTx, err := node.Tx(hash, !cliCtx.TrustNode)
if err != nil {
return sdk.TxResponse{}, err
}
if !cliCtx.TrustNode {
if err = ValidateTxResult(cliCtx, resTx); err != nil {
return sdk.TxResponse{}, err
}
}
resBlocks, err := getBlocksForTxResults(cliCtx, []*ctypes.ResultTx{resTx})
if err != nil {
return sdk.TxResponse{}, err
}
out, err := formatTxResult(cliCtx.Codec, 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(cdc *codec.Codec, resTxs []*ctypes.ResultTx, resBlocks map[int64]*ctypes.ResultBlock) ([]sdk.TxResponse, error) {
var err error
out := make([]sdk.TxResponse, len(resTxs))
for i := range resTxs {
out[i], err = formatTxResult(cdc, resTxs[i], resBlocks[resTxs[i].Height])
if err != nil {
return nil, err
}
}
return out, nil
}
// ValidateTxResult performs transaction verification.
func ValidateTxResult(cliCtx context.CLIContext, resTx *ctypes.ResultTx) error {
if !cliCtx.TrustNode {
check, err := cliCtx.Verify(resTx.Height)
if err != nil {
return err
}
err = resTx.Proof.Validate(check.Header.DataHash)
if err != nil {
return err
}
}
return nil
}
func getBlocksForTxResults(cliCtx context.CLIContext, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) {
node, err := cliCtx.GetNode()
if err != nil {
return nil, err
}
resBlocks := make(map[int64]*ctypes.ResultBlock)
for _, resTx := range resTxs {
if _, ok := resBlocks[resTx.Height]; !ok {
resBlock, err := node.Block(&resTx.Height)
if err != nil {
return nil, err
}
resBlocks[resTx.Height] = resBlock
}
}
return resBlocks, nil
}
func formatTxResult(cdc *codec.Codec, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (sdk.TxResponse, error) {
tx, err := parseTx(cdc, resTx.Tx)
if err != nil {
return sdk.TxResponse{}, err
}
return sdk.NewResponseResultTx(resTx, tx, resBlock.Block.Time.Format(time.RFC3339)), nil
}
func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) {
var tx types.StdTx
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil {
return nil, err
}
return tx, nil
}