From e2928d5b70557009937f1a8cdbc8343f400d2347 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 2 Apr 2019 21:09:37 -0400 Subject: [PATCH] Merge PR ##4007: Add Block Time in Txs Responses --- ...ock-time-to-tx-responses-when-querying-for | 2 + client/tx/query.go | 202 ++++++++++++------ client/tx/root.go | 2 +- client/tx/search.go | 198 ----------------- client/tx/utils.go | 173 +++++++++++++++ types/result.go | 8 +- x/staking/client/rest/query.go | 12 +- x/staking/client/rest/utils.go | 71 +----- 8 files changed, 325 insertions(+), 343 deletions(-) create mode 100644 .pending/improvements/sdk/3238-Add-block-time-to-tx-responses-when-querying-for delete mode 100644 client/tx/search.go create mode 100644 client/tx/utils.go diff --git a/.pending/improvements/sdk/3238-Add-block-time-to-tx-responses-when-querying-for b/.pending/improvements/sdk/3238-Add-block-time-to-tx-responses-when-querying-for new file mode 100644 index 0000000000..d254b93e62 --- /dev/null +++ b/.pending/improvements/sdk/3238-Add-block-time-to-tx-responses-when-querying-for @@ -0,0 +1,2 @@ +#3238 Add block time to tx responses when querying for +txs by tags or hash. diff --git a/client/tx/query.go b/client/tx/query.go index 74bb38495d..8bb8e10cd7 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -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" @@ -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 ( + 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 ':&:' --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 :", tagsStr) + } else if strings.Count(tag, ":") > 1 { + return fmt.Errorf("%s should only contain one : 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().Uint32(flagPage, rest.DefaultPage, "Query a specific page of paginated results") + cmd.Flags().Uint32(flagLimit, rest.DefaultLimit, "Query number of transactions results per page returned") + 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) @@ -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 - } - - 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 +// 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 + ) + + err := r.ParseForm() + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, + sdk.AppendMsgToErr("could not parse query parameters", err.Error())) + return + } + + if len(r.Form) == 0 { + rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) + return + } + + tags, page, limit, err = rest.ParseHTTPArgs(r) + + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txs, err = SearchTxs(cliCtx, cdc, tags, page, limit) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) + } +} + +// 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) diff --git a/client/tx/root.go b/client/tx/root.go index 104e0a57cb..cb2c646091 100644 --- a/client/tx/root.go +++ b/client/tx/root.go @@ -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") } diff --git a/client/tx/search.go b/client/tx/search.go deleted file mode 100644 index 3ee0206768..0000000000 --- a/client/tx/search.go +++ /dev/null @@ -1,198 +0,0 @@ -package tx - -import ( - "errors" - "fmt" - "net/http" - "strings" - - "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" - rest "github.com/cosmos/cosmos-sdk/types/rest" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - ctypes "github.com/tendermint/tendermint/rpc/core/types" - "github.com/tendermint/tendermint/types" -) - -const ( - flagTags = "tags" - flagAny = "any" - flagPage = "page" - flagLimit = "limit" -) - -// default client command to search through tagged transactions -func SearchTxCmd(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "txs", - Short: "Search for all transactions that match the given tags.", - Long: strings.TrimSpace(` -Search for transactions that match exactly the given tags. For example: - -$ gaiacli query txs --tags ':&:' --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 :", tagsStr) - } else if strings.Count(tag, ":") > 1 { - return fmt.Errorf("%s should only contain one : 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") - cmd.Flags().Int32(flagLimit, rest.DefaultLimit, "Query number of transactions results per page returned") - cmd.MarkFlagRequired(flagTags) - return cmd -} - -// 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 ") - - // get the node - node, err := cliCtx.GetNode() - if err != nil { - return nil, err - } - - prove := !cliCtx.TrustNode - - res, err := node.TxSearch(query, prove, page, limit) - if err != nil { - return nil, err - } - - if prove { - for _, tx := range res.Txs { - err := ValidateTxResult(cliCtx, tx) - if err != nil { - return nil, err - } - } - } - - info, err := FormatTxResults(cdc, res.Txs) - if err != nil { - return nil, err - } - - return info, nil -} - -// parse the indexed txs into an array of Info -func FormatTxResults(cdc *codec.Codec, res []*ctypes.ResultTx) ([]sdk.TxResponse, error) { - var err error - out := make([]sdk.TxResponse, len(res)) - for i := range res { - out[i], err = formatTxResult(cdc, res[i]) - if err != nil { - return nil, err - } - } - return out, nil -} - -///////////////////////////////////////// -// REST - -// Search Tx REST Handler -func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var tags []string - var page, limit int - var txs []sdk.TxResponse - err := r.ParseForm() - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, - sdk.AppendMsgToErr("could not parse query parameters", err.Error())) - return - } - if len(r.Form) == 0 { - rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) - return - } - - tags, page, limit, err = rest.ParseHTTPArgs(r) - - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txs, err = SearchTxs(cliCtx, cdc, tags, page, limit) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) - } -} diff --git a/client/tx/utils.go b/client/tx/utils.go new file mode 100644 index 0000000000..41dfd72e88 --- /dev/null +++ b/client/tx/utils.go @@ -0,0 +1,173 @@ +package tx + +import ( + "encoding/hex" + "errors" + "strings" + "time" + + "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" + ctypes "github.com/tendermint/tendermint/rpc/core/types" +) + +// 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 +} diff --git a/types/result.go b/types/result.go index df88acdf38..9b679b3860 100644 --- a/types/result.go +++ b/types/result.go @@ -76,10 +76,11 @@ type TxResponse struct { Tags StringTags `json:"tags,omitempty"` Codespace string `json:"codespace,omitempty"` Tx Tx `json:"tx,omitempty"` + Timestamp string `json:"timestamp,omitempty"` } // NewResponseResultTx returns a TxResponse given a ResultTx from tendermint -func NewResponseResultTx(res *ctypes.ResultTx, tx Tx) TxResponse { +func NewResponseResultTx(res *ctypes.ResultTx, tx Tx, timestamp string) TxResponse { if res == nil { return TxResponse{} } @@ -98,6 +99,7 @@ func NewResponseResultTx(res *ctypes.ResultTx, tx Tx) TxResponse { GasUsed: res.TxResult.GasUsed, Tags: TagsToStringTags(res.TxResult.Tags), Tx: tx, + Timestamp: timestamp, } } @@ -230,6 +232,10 @@ func (r TxResponse) String() string { sb.WriteString(fmt.Sprintf(" Codespace: %s\n", r.Codespace)) } + if r.Timestamp != "" { + sb.WriteString(fmt.Sprintf(" Timestamp: %s\n", r.Timestamp)) + } + return strings.TrimSpace(sb.String()) } diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go index 8a5a5be3ce..63fcc6d24b 100644 --- a/x/staking/client/rest/query.go +++ b/x/staking/client/rest/query.go @@ -125,14 +125,6 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han return } - node, err := cliCtx.GetNode() - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - // Get values from query - typesQuery := r.URL.Query().Get("type") trimmedQuery := strings.TrimSpace(typesQuery) if len(trimmedQuery) != 0 { @@ -167,9 +159,9 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han } for _, action := range actions { - foundTxs, errQuery := queryTxs(node, cliCtx, cdc, action, delegatorAddr) + foundTxs, errQuery := queryTxs(cliCtx, cdc, action, delegatorAddr) if errQuery != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, errQuery.Error()) } txs = append(txs, foundTxs...) } diff --git a/x/staking/client/rest/utils.go b/x/staking/client/rest/utils.go index dd34dd053e..a34864e604 100644 --- a/x/staking/client/rest/utils.go +++ b/x/staking/client/rest/utils.go @@ -6,8 +6,6 @@ import ( "github.com/gorilla/mux" - rpcclient "github.com/tendermint/tendermint/rpc/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" @@ -28,70 +26,15 @@ func contains(stringSlice []string, txType string) bool { } // queries staking txs -func queryTxs(node rpcclient.Client, cliCtx context.CLIContext, cdc *codec.Codec, tag string, delegatorAddr string) ([]sdk.TxResponse, error) { - page := 0 - perPage := 100 - prove := !cliCtx.TrustNode - query := fmt.Sprintf("%s='%s' AND %s='%s'", tags.Action, tag, tags.Delegator, delegatorAddr) - res, err := node.TxSearch(query, prove, page, perPage) - if err != nil { - return nil, err +func queryTxs(cliCtx context.CLIContext, cdc *codec.Codec, tag string, delegatorAddr string) ([]sdk.TxResponse, error) { + page := 1 + limit := 100 + tags := []string{ + fmt.Sprintf("%s='%s'", tags.Action, tag), + fmt.Sprintf("%s='%s'", tags.Delegator, delegatorAddr), } - if prove { - for _, txData := range res.Txs { - err := tx.ValidateTxResult(cliCtx, txData) - if err != nil { - return nil, err - } - } - } - - return tx.FormatTxResults(cdc, res.Txs) -} - -func queryRedelegations(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - bech32delegator := vars["delegatorAddr"] - bech32srcValidator := vars["srcValidatorAddr"] - bech32dstValidator := vars["dstValidatorAddr"] - - delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - srcValidatorAddr, err := sdk.ValAddressFromBech32(bech32srcValidator) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - dstValidatorAddr, err := sdk.ValAddressFromBech32(bech32dstValidator) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - params := staking.QueryRedelegationParams{ - DelegatorAddr: delegatorAddr, - SrcValidatorAddr: srcValidatorAddr, - DstValidatorAddr: dstValidatorAddr, - } - - bz, err := cdc.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - res, err := cliCtx.QueryWithData(endpoint, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } + return tx.SearchTxs(cliCtx, cdc, tags, page, limit) } func queryBonds(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string) http.HandlerFunc {