Merge PR #2842: Fix tx search
This commit is contained in:
commit
1a18a428a4
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue