175 lines
3.9 KiB
Go
175 lines
3.9 KiB
Go
package tx
|
|
|
|
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"
|
|
)
|
|
|
|
// SearchTxs performs a search for transactions for a given set of tags via
|
|
// Tendermint RPC. It returns a slice of Info object containing txs and metadata.
|
|
// An error is returned if the query fails.
|
|
func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string, page, limit int) ([]sdk.TxResponse, error) {
|
|
if len(tags) == 0 {
|
|
return nil, errors.New("must declare at least one tag 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(tags, " 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(cdc, resTxs.Txs, resBlocks)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return txs, 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 auth.StdTx
|
|
|
|
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return tx, nil
|
|
}
|
|
|
|
func queryTx(cdc *codec.Codec, 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(cdc, resTx, resBlocks[resTx.Height])
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
|
|
return out, nil
|
|
}
|