Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block Time in Txs Response #4007

Merged
merged 10 commits into from
Apr 3, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#3238 Add block time to tx responses when querying for
txs by tags or hash.
178 changes: 121 additions & 57 deletions client/tx/query.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
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/tendermint/tendermint/types"

"github.com/spf13/viper"

Expand All @@ -17,14 +16,100 @@ import (
"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"
)

const (
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
flagTags = "tags"
flagPage = "page"
flagLimit = "limit"
)

// ----------------------------------------------------------------------------
// CLI
// ----------------------------------------------------------------------------

// SearchTxCmd returns a command to search through tagged transactions.
func SearchTxCmd(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "txs",
Short: "Search for paginated transactions that match a set of tags",
Long: strings.TrimSpace(`
Search for transactions that match the exact given tags where results are paginated.

Example:
$ gaiacli query txs --tags '<tag1>:<value1>&<tag2>:<value2>' --page 1 --limit 30
`),
RunE: func(cmd *cobra.Command, args []string) error {
tagsStr := viper.GetString(flagTags)
tagsStr = strings.Trim(tagsStr, "'")

var tags []string
if strings.Contains(tagsStr, "&") {
tags = strings.Split(tagsStr, "&")
} else {
tags = append(tags, tagsStr)
}

var tmTags []string
for _, tag := range tags {
if !strings.Contains(tag, ":") {
return fmt.Errorf("%s should be of the format <key>:<value>", tagsStr)
} else if strings.Count(tag, ":") > 1 {
return fmt.Errorf("%s should only contain one <key>:<value> pair", tagsStr)
}

keyValue := strings.Split(tag, ":")
if keyValue[0] == types.TxHeightKey {
tag = fmt.Sprintf("%s=%s", keyValue[0], keyValue[1])
} else {
tag = fmt.Sprintf("%s='%s'", keyValue[0], keyValue[1])
}
tmTags = append(tmTags, tag)
}

page := viper.GetInt(flagPage)
limit := viper.GetInt(flagLimit)

cliCtx := context.NewCLIContext().WithCodec(cdc)
txs, err := SearchTxs(cliCtx, cdc, tmTags, page, limit)
if err != nil {
return err
}

var output []byte
if cliCtx.Indent {
output, err = cdc.MarshalJSONIndent(txs, "", " ")
} else {
output, err = cdc.MarshalJSON(txs)
}

if err != nil {
return err
}

fmt.Println(string(output))
return nil
},
}

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))

cmd.Flags().String(flagTags, "", "tag:value list of tags that must match")
cmd.Flags().Int32(flagPage, rest.DefaultPage, "Query a specific page of paginated results")
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
cmd.Flags().Int32(flagLimit, rest.DefaultLimit, "Query number of transactions results per page returned")
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
cmd.MarkFlagRequired(flagTags)

return cmd
}

// 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",
Short: "Find a transaction by hash in a committed block.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
Expand All @@ -46,76 +131,55 @@ func QueryTxCmd(cdc *codec.Codec) *cobra.Command {
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
}
// ----------------------------------------------------------------------------
// REST
// ----------------------------------------------------------------------------

res, err := node.Tx(hash, !cliCtx.TrustNode)
if err != nil {
return out, err
}
// QueryTxsByTagsRequestHandlerFn implements a REST handler that searches for
// transactions by tags.
func QueryTxsByTagsRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var (
tags []string
txs []sdk.TxResponse
page, limit int
)

if !cliCtx.TrustNode {
if err = ValidateTxResult(cliCtx, res); err != nil {
return out, err
err := r.ParseForm()
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest,
sdk.AppendMsgToErr("could not parse query parameters", err.Error()))
return
}
}

if out, err = formatTxResult(cdc, res); err != nil {
return out, err
}
if len(r.Form) == 0 {
rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent)
return
}

return out, nil
}
tags, page, limit, err = rest.ParseHTTPArgs(r)

// 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
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
err = res.Proof.Validate(check.Header.DataHash)

txs, err = SearchTxs(cliCtx, cdc, tags, page, limit)
if err != nil {
return err
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
}
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
rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent)
}

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
// QueryTxRequestHandlerFn implements a REST handler that queries a transaction
// by hash in a committed block.
func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
Expand Down
2 changes: 1 addition & 1 deletion client/tx/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
// register REST routes
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) {
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, cliCtx)).Methods("GET")
r.HandleFunc("/txs", SearchTxRequestHandlerFn(cliCtx, cdc)).Methods("GET")
r.HandleFunc("/txs", QueryTxsByTagsRequestHandlerFn(cliCtx, cdc)).Methods("GET")
r.HandleFunc("/txs", BroadcastTxRequest(cliCtx, cdc)).Methods("POST")
r.HandleFunc("/txs/encode", EncodeTxRequestHandlerFn(cdc, cliCtx)).Methods("POST")
}
Loading