Merge PR ##4007: Add Block Time in Txs Responses
This commit is contained in:
parent
5b87966ee2
commit
e2928d5b70
@ -0,0 +1,2 @@
|
||||
#3238 Add block time to tx responses when querying for
|
||||
txs by tags or hash.
|
||||
@ -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 '<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().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)
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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 '<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")
|
||||
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)
|
||||
}
|
||||
}
|
||||
173
client/tx/utils.go
Normal file
173
client/tx/utils.go
Normal file
@ -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
|
||||
}
|
||||
@ -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())
|
||||
}
|
||||
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user