package tx import ( "encoding/hex" "fmt" "net/http" "strings" "github.com/gorilla/mux" "github.com/spf13/cobra" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" "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/types/rest" "github.com/cosmos/cosmos-sdk/x/auth" ) // QueryTxCmd implements the default command for a tx query. func QueryTxCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "tx [hash]", Short: "Matches this txhash over all committed blocks", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) output, err := queryTx(cdc, cliCtx, args[0]) if err != nil { return err } if output.Empty() { return fmt.Errorf("No transaction found with hash %s", args[0]) } return cliCtx.PrintOutput(output) }, } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) return cmd } func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string) (out sdk.TxResponse, err error) { hash, err := hex.DecodeString(hashHexStr) if err != nil { return out, err } node, err := cliCtx.GetNode() if err != nil { return out, err } res, err := node.Tx(hash, !cliCtx.TrustNode) if err != nil { return out, err } if !cliCtx.TrustNode { if err = ValidateTxResult(cliCtx, res); err != nil { return out, err } } if out, err = formatTxResult(cdc, res); err != nil { return out, err } return out, nil } // ValidateTxResult performs transaction verification func ValidateTxResult(cliCtx context.CLIContext, res *ctypes.ResultTx) error { if !cliCtx.TrustNode { check, err := cliCtx.Verify(res.Height) if err != nil { return err } err = res.Proof.Validate(check.Header.DataHash) if err != nil { return err } } return nil } func formatTxResult(cdc *codec.Codec, res *ctypes.ResultTx) (sdk.TxResponse, error) { tx, err := parseTx(cdc, res.Tx) if err != nil { return sdk.TxResponse{}, err } return sdk.NewResponseResultTx(res, tx), 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 } // REST // QueryTxRequestHandlerFn transaction query REST handler func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) hashHexStr := vars["hash"] output, err := queryTx(cdc, cliCtx, hashHexStr) if err != nil { if strings.Contains(err.Error(), hashHexStr) { rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } if output.Empty() { rest.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("no transaction found with hash %s", hashHexStr)) } rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) } }