Merge PR #2842: Fix tx search

This commit is contained in:
Christopher Goes 2018-11-28 00:09:14 +01:00 committed by GitHub
commit 1a18a428a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 221 additions and 90 deletions

View File

@ -3,6 +3,7 @@
BREAKING CHANGES BREAKING CHANGES
* Gaia REST API (`gaiacli advanced rest-server`) * Gaia REST API (`gaiacli advanced rest-server`)
* [gaia-lite] [\#2819](https://github.com/cosmos/cosmos-sdk/pull/2819) Txs query param format is now: `/txs?tag=value` (removed '' wrapping the query parameter `value`)
* Gaia CLI (`gaiacli`) * Gaia CLI (`gaiacli`)
* [cli] [\#2728](https://github.com/cosmos/cosmos-sdk/pull/2728) Seperate `tx` and `query` subcommands by module * [cli] [\#2728](https://github.com/cosmos/cosmos-sdk/pull/2728) Seperate `tx` and `query` subcommands by module
@ -56,10 +57,12 @@ FEATURES
IMPROVEMENTS IMPROVEMENTS
* Gaia REST API (`gaiacli advanced rest-server`) * Gaia REST API (`gaiacli advanced rest-server`)
* [gaia-lite] [\#2819](https://github.com/cosmos/cosmos-sdk/pull/2819) Tx search now supports multiple tags as query parameters
* [\#2836](https://github.com/cosmos/cosmos-sdk/pull/2836) Expose LCD router to allow users to register routes there. * [\#2836](https://github.com/cosmos/cosmos-sdk/pull/2836) Expose LCD router to allow users to register routes there.
* Gaia CLI (`gaiacli`) * Gaia CLI (`gaiacli`)
* [\#2749](https://github.com/cosmos/cosmos-sdk/pull/2749) Add --chain-id flag to gaiad testnet * [\#2749](https://github.com/cosmos/cosmos-sdk/pull/2749) Add --chain-id flag to gaiad testnet
* [\#2819](https://github.com/cosmos/cosmos-sdk/pull/2819) Tx search now supports multiple tags as query parameters
* Gaia * Gaia
- #2772 Update BaseApp to not persist state when the ante handler fails on DeliverTx. - #2772 Update BaseApp to not persist state when the ante handler fails on DeliverTx.
@ -78,7 +81,7 @@ IMPROVEMENTS
- #2779 Introduce `ValidateBasic` to the `Tx` interface and call it in the ante - #2779 Introduce `ValidateBasic` to the `Tx` interface and call it in the ante
handler. handler.
- #2825 More staking and distribution invariants - #2825 More staking and distribution invariants
* #2912 Print commit ID in hex when commit is synced. - #2912 Print commit ID in hex when commit is synced.
* Tendermint * Tendermint
- #2796 Update to go-amino 0.14.1 - #2796 Update to go-amino 0.14.1
@ -95,7 +98,7 @@ BUG FIXES
* Gaia * Gaia
* [\#2723] Use `cosmosvalcons` Bech32 prefix in `tendermint show-address` * [\#2723] Use `cosmosvalcons` Bech32 prefix in `tendermint show-address`
* [\#2742](https://github.com/cosmos/cosmos-sdk/issues/2742) Fix time format of TimeoutCommit override * [\#2742](https://github.com/cosmos/cosmos-sdk/issues/2742) Fix time format of TimeoutCommit override
* [\#2898](https://github.com/cosmos/cosmos-sdk/issues/2898) Remove redundant '$' in docker-compose.yml * [\#2898](https://github.com/cosmos/cosmos-sdk/issues/2898) Remove redundant '$' in docker-compose.yml
* SDK * SDK

View File

@ -7,6 +7,7 @@ import (
"net/http" "net/http"
"os" "os"
"regexp" "regexp"
"strings"
"testing" "testing"
"time" "time"
@ -399,57 +400,39 @@ func TestTxs(t *testing.T) {
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup() defer cleanup()
// query wrong var emptyTxs []tx.Info
res, body := Request(t, port, "GET", "/txs", nil) txs := getTransactions(t, port)
require.Equal(t, http.StatusBadRequest, res.StatusCode, body) require.Equal(t, emptyTxs, txs)
// query empty // query empty
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", "cosmos1jawd35d9aq4u76sr3fjalmcqc8hqygs90d0g0v"), nil) txs = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String()))
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, emptyTxs, txs)
require.Equal(t, "[]", body)
// create TX // also tests url decoding
txs = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String()))
require.Equal(t, emptyTxs, txs)
txs = getTransactions(t, port, fmt.Sprintf("action=submit%%20proposal&proposer=%s", addr.String()))
require.Equal(t, emptyTxs, txs)
// create tx
receiveAddr, resultTx := doSend(t, port, seed, name, password, addr) receiveAddr, resultTx := doSend(t, port, seed, name, password, addr)
tests.WaitForHeight(resultTx.Height+1, port) tests.WaitForHeight(resultTx.Height+1, port)
// check if tx is findable
res, body = Request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var indexedTxs []tx.Info
// check if tx is queryable // check if tx is queryable
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=tx.hash='%s'", resultTx.Hash), nil) txs = getTransactions(t, port, fmt.Sprintf("tx.hash=%s", resultTx.Hash))
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Len(t, txs, 1)
require.NotEqual(t, "[]", body) require.Equal(t, resultTx.Hash, txs[0].Hash)
err := cdc.UnmarshalJSON([]byte(body), &indexedTxs)
require.NoError(t, err)
require.Equal(t, 1, len(indexedTxs))
// XXX should this move into some other testfile for txs in general?
// test if created TX hash is the correct hash
require.Equal(t, resultTx.Hash, indexedTxs[0].Hash)
// query sender // query sender
// also tests url decoding txs = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String()))
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32=%%27%s%%27", addr), nil) require.Len(t, txs, 1)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, resultTx.Height, txs[0].Height)
err = cdc.UnmarshalJSON([]byte(body), &indexedTxs)
require.NoError(t, err)
require.Equal(t, 1, len(indexedTxs), "%v", indexedTxs) // there are 2 txs created with doSend
require.Equal(t, resultTx.Height, indexedTxs[0].Height)
// query recipient // query recipient
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=recipient_bech32='%s'", receiveAddr), nil) txs = getTransactions(t, port, fmt.Sprintf("recipient=%s", receiveAddr.String()))
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
err = cdc.UnmarshalJSON([]byte(body), &indexedTxs)
require.NoError(t, err)
require.Equal(t, 1, len(indexedTxs))
require.Equal(t, resultTx.Height, indexedTxs[0].Height)
} }
func TestPoolParamsQuery(t *testing.T) { func TestPoolParamsQuery(t *testing.T) {
@ -534,6 +517,14 @@ func TestBonding(t *testing.T) {
require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.CheckTx.Code)
require.Equal(t, uint32(0), resultTx.DeliverTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
// query tx
txs := getTransactions(t, port,
fmt.Sprintf("action=delegate&delegator=%s", addr),
fmt.Sprintf("destination-validator=%s", operAddrs[0]),
)
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
acc := getAccount(t, port, addr) acc := getAccount(t, port, addr)
coins := acc.GetCoins() coins := acc.GetCoins()
@ -571,6 +562,14 @@ func TestBonding(t *testing.T) {
coins = acc.GetCoins() coins = acc.GetCoins()
require.Equal(t, int64(40), coins.AmountOf(stakeTypes.DefaultBondDenom).Int64()) require.Equal(t, int64(40), coins.AmountOf(stakeTypes.DefaultBondDenom).Int64())
// query tx
txs = getTransactions(t, port,
fmt.Sprintf("action=begin-unbonding&delegator=%s", addr),
fmt.Sprintf("source-validator=%s", operAddrs[0]),
)
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
unbonding := getUndelegation(t, port, addr, operAddrs[0]) unbonding := getUndelegation(t, port, addr, operAddrs[0])
require.Equal(t, "30", unbonding.Balance.Amount.String()) require.Equal(t, "30", unbonding.Balance.Amount.String())
@ -581,6 +580,15 @@ func TestBonding(t *testing.T) {
require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.CheckTx.Code)
require.Equal(t, uint32(0), resultTx.DeliverTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
// query tx
txs = getTransactions(t, port,
fmt.Sprintf("action=begin-redelegation&delegator=%s", addr),
fmt.Sprintf("source-validator=%s", operAddrs[0]),
fmt.Sprintf("destination-validator=%s", operAddrs[1]),
)
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
// query delegations, unbondings and redelegations from validator and delegator // query delegations, unbondings and redelegations from validator and delegator
delegatorDels = getDelegatorDelegations(t, port, addr) delegatorDels = getDelegatorDelegations(t, port, addr)
require.Len(t, delegatorDels, 1) require.Len(t, delegatorDels, 1)
@ -606,7 +614,7 @@ func TestBonding(t *testing.T) {
// require.Equal(t, sdk.Unbonding, bondedValidators[0].Status) // require.Equal(t, sdk.Unbonding, bondedValidators[0].Status)
// query txs // query txs
txs := getBondingTxs(t, port, addr, "") txs = getBondingTxs(t, port, addr, "")
require.Len(t, txs, 3, "All Txs found") require.Len(t, txs, 3, "All Txs found")
txs = getBondingTxs(t, port, addr, "bond") txs = getBondingTxs(t, port, addr, "bond")
@ -639,6 +647,11 @@ func TestSubmitProposal(t *testing.T) {
// query proposal // query proposal
proposal := getProposal(t, port, proposalID) proposal := getProposal(t, port, proposalID)
require.Equal(t, "Test", proposal.GetTitle()) require.Equal(t, "Test", proposal.GetTitle())
// query tx
txs := getTransactions(t, port, fmt.Sprintf("action=submit-proposal&proposer=%s", addr))
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
} }
func TestDeposit(t *testing.T) { func TestDeposit(t *testing.T) {
@ -666,6 +679,11 @@ func TestDeposit(t *testing.T) {
resultTx = doDeposit(t, port, seed, name, password, addr, proposalID, 5) resultTx = doDeposit(t, port, seed, name, password, addr, proposalID, 5)
tests.WaitForHeight(resultTx.Height+1, port) tests.WaitForHeight(resultTx.Height+1, port)
// query tx
txs := getTransactions(t, port, fmt.Sprintf("action=deposit&depositor=%s", addr))
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
// query proposal // query proposal
proposal = getProposal(t, port, proposalID) proposal = getProposal(t, port, proposalID)
require.True(t, proposal.GetTotalDeposit().IsEqual(sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)})) require.True(t, proposal.GetTotalDeposit().IsEqual(sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 10)}))
@ -708,6 +726,11 @@ func TestVote(t *testing.T) {
resultTx = doVote(t, port, seed, name, password, addr, proposalID) resultTx = doVote(t, port, seed, name, password, addr, proposalID)
tests.WaitForHeight(resultTx.Height+1, port) tests.WaitForHeight(resultTx.Height+1, port)
// query tx
txs := getTransactions(t, port, fmt.Sprintf("action=vote&voter=%s", addr))
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
vote := getVote(t, port, proposalID, addr) vote := getVote(t, port, proposalID, addr)
require.Equal(t, proposalID, vote.ProposalID) require.Equal(t, proposalID, vote.ProposalID)
require.Equal(t, gov.OptionYes, vote.Option) require.Equal(t, gov.OptionYes, vote.Option)
@ -866,7 +889,7 @@ func TestProposalsQuery(t *testing.T) {
//_____________________________________________________________________________ //_____________________________________________________________________________
// get the account to get the sequence // get the account to get the sequence
func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", addr), nil) res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", addr.String()), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
var acc auth.Account var acc auth.Account
err := cdc.UnmarshalJSON([]byte(body), &acc) err := cdc.UnmarshalJSON([]byte(body), &acc)
@ -944,6 +967,22 @@ func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress
return receiveAddr, resultTx return receiveAddr, resultTx
} }
func getTransactions(t *testing.T, port string, tags ...string) []tx.Info {
var txs []tx.Info
if len(tags) == 0 {
return txs
}
queryStr := strings.Join(tags, "&")
res, body := Request(t, port, "GET", fmt.Sprintf("/txs?%s", queryStr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err := cdc.UnmarshalJSON([]byte(body), &txs)
require.NoError(t, err)
return txs
}
// ============= IBC Module ================
func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) { func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) {
// create receive address // create receive address
kb := client.MockKeyBase() kb := client.MockKeyBase()
@ -984,6 +1023,8 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Acc
return resultTx return resultTx
} }
// ============= Slashing Module ================
func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.ValidatorSigningInfo { func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.ValidatorSigningInfo {
res, body := Request(t, port, "GET", fmt.Sprintf("/slashing/validators/%s/signing_info", validatorPubKey), nil) res, body := Request(t, port, "GET", fmt.Sprintf("/slashing/validators/%s/signing_info", validatorPubKey), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
@ -995,6 +1036,31 @@ func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.
return signingInfo return signingInfo
} }
func doUnjail(t *testing.T, port, seed, name, password string,
valAddr sdk.ValAddress) (resultTx ctypes.ResultBroadcastTxCommit) {
chainID := viper.GetString(client.FlagChainID)
jsonStr := []byte(fmt.Sprintf(`{
"base_req": {
"name": "%s",
"password": "%s",
"chain_id": "%s",
"account_number":"1",
"sequence":"1"
}
}`, name, password, chainID))
res, body := Request(t, port, "POST", fmt.Sprintf("/slashing/validators/%s/unjail", valAddr.String()), jsonStr)
// TODO : fails with "401 must use own validator address"
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results []ctypes.ResultBroadcastTxCommit
err := cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
return results[0]
}
// ============= Stake Module ================ // ============= Stake Module ================
func getDelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.Delegation { func getDelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.Delegation {

View File

@ -209,14 +209,14 @@ paths:
tags: tags:
- ICS0 - ICS0
summary: Search transactions summary: Search transactions
description: Search transactions by tag description: Search transactions by tag(s).
produces: produces:
- application/json - application/json
parameters: parameters:
- in: query - in: query
name: tag name: tag
type: string type: string
description: "transaction tag, for instance: sender_bech32=`'cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc'`" description: "transaction tags such as 'action=submit-proposal' and 'proposer=cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc' which results in the following endpoint: 'GET /txs?action=submit-proposal&proposer=cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc'"
required: true required: true
- in: query - in: query
name: page name: page
@ -228,7 +228,7 @@ paths:
type: integer type: integer
responses: responses:
200: 200:
description: All Tx matching the provided tags description: All txs matching the provided tags
schema: schema:
type: array type: array
items: items:

View File

@ -9,18 +9,18 @@ import (
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/utils"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
) )
const ( const (
flagTags = "tag" flagTags = "tags"
flagAny = "any" flagAny = "any"
) )
@ -30,24 +30,35 @@ func SearchTxCmd(cdc *codec.Codec) *cobra.Command {
Use: "txs", Use: "txs",
Short: "Search for all transactions that match the given tags.", Short: "Search for all transactions that match the given tags.",
Long: strings.TrimSpace(` Long: strings.TrimSpace(`
Search for transactions that match the given tags. By default, transactions must match ALL tags Search for transactions that match exactly the given tags. For example:
passed to the --tags option. To match any transaction, use the --any option.
For example: $ gaiacli query txs --tags '<tag1>:<value1>&<tag2>:<value2>'
$ gaiacli tendermint txs --tag test1,test2
will match any transaction tagged with both test1,test2. To match a transaction tagged with either
test1 or test2, use:
$ gaiacli tendermint txs --tag test1,test2 --any
`), `),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
tags := viper.GetStringSlice(flagTags) 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, ":")
tag = fmt.Sprintf("%s='%s'", keyValue[0], keyValue[1])
tmTags = append(tmTags, tag)
}
cliCtx := context.NewCLIContext().WithCodec(cdc) cliCtx := context.NewCLIContext().WithCodec(cdc)
txs, err := searchTxs(cliCtx, cdc, tmTags)
txs, err := searchTxs(cliCtx, cdc, tags)
if err != nil { if err != nil {
return err return err
} }
@ -74,8 +85,7 @@ $ gaiacli tendermint txs --tag test1,test2 --any
viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID))
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") 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)) viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
cmd.Flags().StringSlice(flagTags, nil, "Comma-separated list of tags that must match") cmd.Flags().String(flagTags, "", "tag:value list of tags that must match")
cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL")
return cmd return cmd
} }
@ -139,45 +149,35 @@ func FormatTxResults(cdc *codec.Codec, res []*ctypes.ResultTx) ([]Info, error) {
// Search Tx REST Handler // Search Tx REST Handler
func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
tag := r.FormValue("tag") var tags []string
if tag == "" { var txs []Info
w.WriteHeader(http.StatusBadRequest) err := r.ParseForm()
w.Write([]byte("You need to provide at least a tag as a key=value pair to search for. Postfix the key with _bech32 to search bech32-encoded addresses or public keys"))
return
}
keyValue := strings.Split(tag, "=")
key := keyValue[0]
value, err := url.QueryUnescape(keyValue[1])
if err != nil { if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("could not decode address", err.Error())) utils.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("could not parse query parameters", err.Error()))
return
}
if len(r.Form) == 0 {
utils.PostProcessResponse(w, cdc, txs, cliCtx.Indent)
return return
} }
if strings.HasSuffix(key, "_bech32") { for key, values := range r.Form {
bech32address := strings.Trim(value, "'") value, err := url.QueryUnescape(values[0])
prefix := strings.Split(bech32address, "1")[0]
bz, err := sdk.GetFromBech32(bech32address, prefix)
if err != nil { if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) utils.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("could not decode query value", err.Error()))
return return
} }
tag = strings.TrimRight(key, "_bech32") + "='" + sdk.AccAddress(bz).String() + "'" tag := fmt.Sprintf("%s='%s'", key, value)
tags = append(tags, tag)
} }
txs, err := searchTxs(cliCtx, cdc, []string{tag}) txs, err = searchTxs(cliCtx, cdc, tags)
if err != nil { if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return return
} }
if len(txs) == 0 {
w.Write([]byte("[]"))
return
}
utils.PostProcessResponse(w, cdc, txs, cliCtx.Indent) utils.PostProcessResponse(w, cdc, txs, cliCtx.Indent)
} }
} }

View File

@ -21,6 +21,7 @@ import (
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server"
@ -277,7 +278,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
require.NotZero(t, validatorDelegations[0].Shares) require.NotZero(t, validatorDelegations[0].Shares)
// unbond a single share // unbond a single share
unbondStr := fmt.Sprintf("gaiacli tx stake unbond begin %v", flags) unbondStr := fmt.Sprintf("gaiacli tx stake unbond %v", flags)
unbondStr += fmt.Sprintf(" --from=%s", "bar") unbondStr += fmt.Sprintf(" --from=%s", "bar")
unbondStr += fmt.Sprintf(" --validator=%s", sdk.ValAddress(barAddr)) unbondStr += fmt.Sprintf(" --validator=%s", sdk.ValAddress(barAddr))
unbondStr += fmt.Sprintf(" --shares-amount=%v", "1") unbondStr += fmt.Sprintf(" --shares-amount=%v", "1")
@ -354,6 +355,9 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
executeWrite(t, spStr, app.DefaultKeyPass) executeWrite(t, spStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port) tests.WaitForNextNBlocksTM(2, port)
txs := executeGetTxs(t, fmt.Sprintf("gaiacli query txs --tags='action:submit-proposal&proposer:%s' %v", fooAddr, flags))
require.Len(t, txs, 1)
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64())
@ -398,6 +402,9 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
fooAddr, flags)) fooAddr, flags))
require.Equal(t, int64(15), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) require.Equal(t, int64(15), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64())
txs = executeGetTxs(t, fmt.Sprintf("gaiacli query txs --tags=action:deposit&depositor:%s %v", fooAddr, flags))
require.Len(t, txs, 1)
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64())
@ -432,6 +439,9 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, uint64(1), votes[0].ProposalID) require.Equal(t, uint64(1), votes[0].ProposalID)
require.Equal(t, gov.OptionYes, votes[0].Option) require.Equal(t, gov.OptionYes, votes[0].Option)
txs = executeGetTxs(t, fmt.Sprintf("gaiacli query txs --tags=action:vote&voter:%s %v", fooAddr, flags))
require.Len(t, txs, 1)
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query gov proposals --status=DepositPeriod %v", flags), "") proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query gov proposals --status=DepositPeriod %v", flags), "")
require.Equal(t, "No matching proposals found", proposalsQuery) require.Equal(t, "No matching proposals found", proposalsQuery)
@ -736,6 +746,18 @@ func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
return acc return acc
} }
//___________________________________________________________________________________
// txs
func executeGetTxs(t *testing.T, cmdStr string) []tx.Info {
out, _ := tests.ExecuteT(t, cmdStr, "")
var txs []tx.Info
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &txs)
require.NoError(t, err, "out %v\n, err %v", out, err)
return txs
}
//___________________________________________________________________________________ //___________________________________________________________________________________
// stake // stake

View File

@ -183,10 +183,50 @@ gaiacli tx sign --validate-signatures signedSendTx.json
You can broadcast the signed transaction to a node by providing the JSON file to the following command: You can broadcast the signed transaction to a node by providing the JSON file to the following command:
``` ```bash
gaiacli tx broadcast --node=<node> signedSendTx.json gaiacli tx broadcast --node=<node> signedSendTx.json
``` ```
### Query Transactions
#### Matching a set of tags
You can use the transaction search command to query for transactions that match a specific set of `tags`, which are added on every transaction.
Each tag is conformed by a key-value pair in the form of `<tag>:<value>`. Tags can also be combined to query for a more specific result using the `&` symbol.
The command for querying transactions using a `tag` is the following:
```bash
gaiacli query txs --tags='<tag>:<value>'
```
And for using multiple `tags`:
```bash
gaiacli query txs --tags='<tag1>:<value1>&<tag2>:<value2>'
```
::: tip Note
You can find a list of available `tags` on each of the SDK modules:
- [Common tags](https://github.com/cosmos/cosmos-sdk/blob/d1e76221d8e28824bb4791cb4ad8662d2ae9051e/types/tags.go#L57-L63)
- [Staking tags](https://github.com/cosmos/cosmos-sdk/blob/d1e76221d8e28824bb4791cb4ad8662d2ae9051e/x/stake/tags/tags.go#L8-L24)
- [Governance tags](https://github.com/cosmos/cosmos-sdk/blob/d1e76221d8e28824bb4791cb4ad8662d2ae9051e/x/gov/tags/tags.go#L8-L22)
- [Slashing tags](https://github.com/cosmos/cosmos-sdk/blob/d1e76221d8e28824bb4791cb4ad8662d2ae9051e/x/slashing/handler.go#L52)
- [Distribution tags](https://github.com/cosmos/cosmos-sdk/blob/develop/x/distribution/tags/tags.go#L8-L17)
- [Bank tags](https://github.com/cosmos/cosmos-sdk/blob/d1e76221d8e28824bb4791cb4ad8662d2ae9051e/x/bank/keeper.go#L193-L206)
:::
#### Matching a transaction's hash
You can also query a single transaction by its hash using the following command:
```bash
gaiacli query tx [hash]
```
### Staking ### Staking
#### Set up a Validator #### Set up a Validator