From 8ece8073019f92d0de1d1a4fa67de248ed36dbbd Mon Sep 17 00:00:00 2001 From: Fabian Date: Mon, 11 Jun 2018 13:09:29 -0700 Subject: [PATCH] Merge PR #1085: Re-enable tx history in LCD * reenable tx search * removed not needed argument * register types for decoding * trying to fix indexing tests * added tx hash query test * Fix x/bank tagging * remove dead code * remove print * extended tests for tx querying * changelog * added txs address querying * linted * rename * use prefix for bech32 addresses in tags * changed error message * Fix tiny linter issue --- CHANGELOG.md | 1 + client/lcd/helpers.go | 1 - client/lcd/lcd_test.go | 61 +++++++++++++++++++++++++++--------------- client/tx/root.go | 2 +- client/tx/search.go | 49 +++++++++++++++++++++++++-------- types/account.go | 11 ++++---- types/wire.go | 1 + x/auth/wire.go | 1 + x/bank/keeper.go | 4 +-- 9 files changed, 90 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71a8faa626..16346def30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ IMPROVEMENTS FIXES * [lcd] Switch to bech32 for addresses on all human readable inputs and outputs +* fixed tx indexing/querying * [cli] Added `--gas` flag to specify transaction gas limit ## 0.18.0 diff --git a/client/lcd/helpers.go b/client/lcd/helpers.go index 367e1a53de..14cd5c16c7 100644 --- a/client/lcd/helpers.go +++ b/client/lcd/helpers.go @@ -48,7 +48,6 @@ func GetConfig() *cfg.Config { tm, rpc, _ := makeAddrs() globalConfig.P2P.ListenAddress = tm globalConfig.RPC.ListenAddress = rpc - globalConfig.TxIndex.IndexTags = "app.creator" // see kvstore application } return globalConfig } diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 59f5b24646..3565d99c31 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -243,7 +243,7 @@ func TestCoinSend(t *testing.T) { initialBalance := acc.GetCoins() // create TX - receiveAddr, resultTx := doSend(t, port, seed) + receiveAddr, resultTx := doSend(t, port) tests.WaitForHeight(resultTx.Height+1, port) // check if tx was commited @@ -290,39 +290,57 @@ func TestIBCTransfer(t *testing.T) { } func TestTxs(t *testing.T) { - - // TODO: re-enable once we can get txs by tag - // query wrong - // res, body := request(t, port, "GET", "/txs", nil) - // require.Equal(t, http.StatusBadRequest, res.StatusCode, body) + res, body := request(t, port, "GET", "/txs", nil) + require.Equal(t, http.StatusBadRequest, res.StatusCode, body) // query empty - // res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=coin.sender='%s'", "8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6"), nil) - // require.Equal(t, http.StatusOK, res.StatusCode, body) - - // assert.Equal(t, "[]", body) + res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", "cosmosaccaddr1jawd35d9aq4u76sr3fjalmcqc8hqygs9gtnmv3"), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + assert.Equal(t, "[]", body) // create TX - _, resultTx := doSend(t, port, seed) + receiveAddr, resultTx := doSend(t, 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) + res, body = request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - // // query sender - // res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=coin.sender='%s'", addr), nil) - // require.Equal(t, http.StatusOK, res.StatusCode, body) + type txInfo struct { + Height int64 `json:"height"` + Tx sdk.Tx `json:"tx"` + Result abci.ResponseDeliverTx `json:"result"` + } + var indexedTxs []txInfo - // assert.NotEqual(t, "[]", body) + // check if tx is queryable + res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=tx.hash='%s'", resultTx.Hash), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + assert.NotEqual(t, "[]", body) - // // query receiver - // res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=coin.receiver='%s'", receiveAddr), nil) - // require.Equal(t, http.StatusOK, res.StatusCode, body) + err := cdc.UnmarshalJSON([]byte(body), &indexedTxs) + require.NoError(t, err) + assert.Equal(t, len(indexedTxs), 1) - // assert.NotEqual(t, "[]", body) + // query sender + res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", sendAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err = cdc.UnmarshalJSON([]byte(body), &indexedTxs) + require.NoError(t, err) + assert.Equal(t, 2, len(indexedTxs)) // there are 2 txs created with doSend + assert.Equal(t, resultTx.Height, indexedTxs[1].Height) + + // query recipient + res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=recipient_bech32='%s'", receiveAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err = cdc.UnmarshalJSON([]byte(body), &indexedTxs) + require.NoError(t, err) + assert.Equal(t, 1, len(indexedTxs)) + assert.Equal(t, resultTx.Height, indexedTxs[0].Height) } func TestValidatorsQuery(t *testing.T) { @@ -401,6 +419,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) { config := GetConfig() config.Consensus.TimeoutCommit = 1000 config.Consensus.SkipTimeoutCommit = false + config.TxIndex.IndexAllTags = true logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) logger = log.NewFilter(logger, log.AllowError()) @@ -553,7 +572,7 @@ func getAccount(t *testing.T, sendAddr string) auth.Account { return acc } -func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctypes.ResultBroadcastTxCommit) { +func doSend(t *testing.T, port string) (receiveAddr string, resultTx ctypes.ResultBroadcastTxCommit) { // create receive address kb := client.MockKeyBase() diff --git a/client/tx/root.go b/client/tx/root.go index e8abf33185..5def5a5440 100644 --- a/client/tx/root.go +++ b/client/tx/root.go @@ -19,7 +19,7 @@ func AddCommands(cmd *cobra.Command, cdc *wire.Codec) { // register REST routes func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) { r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, ctx)).Methods("GET") - // r.HandleFunc("/txs", SearchTxRequestHandler(cdc)).Methods("GET") + r.HandleFunc("/txs", SearchTxRequestHandlerFn(ctx, cdc)).Methods("GET") // r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST") // r.HandleFunc("/txs/broadcast", BroadcastTxRequestHandler).Methods("POST") } diff --git a/client/tx/search.go b/client/tx/search.go index 527661626a..3ab3a3df1b 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" ) @@ -29,7 +30,11 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { tags := viper.GetStringSlice(flagTags) - output, err := searchTx(context.NewCoreContextFromViper(), cdc, tags) + txs, err := searchTxs(context.NewCoreContextFromViper(), cdc, tags) + if err != nil { + return err + } + output, err := cdc.MarshalJSON(txs) if err != nil { return err } @@ -47,13 +52,12 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command { return cmd } -func searchTx(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]byte, error) { +func searchTxs(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]txInfo, error) { if len(tags) == 0 { return nil, errors.New("Must declare at least one tag to search") } // XXX: implement ANY query := strings.Join(tags, " AND ") - // get the node node, err := ctx.GetNode() if err != nil { @@ -74,11 +78,7 @@ func searchTx(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]byte, return nil, err } - output, err := cdc.MarshalJSON(info) - if err != nil { - return nil, err - } - return output, nil + return info, nil } func formatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]txInfo, error) { @@ -102,17 +102,44 @@ func SearchTxRequestHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.Han tag := r.FormValue("tag") if tag == "" { w.WriteHeader(400) - w.Write([]byte("You need to provide a tag to search for.")) + 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 := keyValue[1] + if strings.HasSuffix(key, "_bech32") { + bech32address := strings.Trim(value, "'") + prefix := strings.Split(bech32address, "1")[0] + bz, err := sdk.GetFromBech32(bech32address, prefix) + if err != nil { + w.WriteHeader(400) + w.Write([]byte(err.Error())) + return + } - tags := []string{tag} - output, err := searchTx(ctx, cdc, tags) + tag = strings.TrimRight(key, "_bech32") + "='" + sdk.Address(bz).String() + "'" + } + + txs, err := searchTxs(ctx, cdc, []string{tag}) if err != nil { w.WriteHeader(500) w.Write([]byte(err.Error())) return } + + if len(txs) == 0 { + w.Write([]byte("[]")) + return + } + + output, err := cdc.MarshalJSON(txs) + if err != nil { + w.WriteHeader(500) + w.Write([]byte(err.Error())) + return + } + w.Write(output) } } diff --git a/types/account.go b/types/account.go index 381fb7af8a..a7dd50eadc 100644 --- a/types/account.go +++ b/types/account.go @@ -91,7 +91,7 @@ func GetAccAddressHex(address string) (addr Address, err error) { // create an Address from a string func GetAccAddressBech32(address string) (addr Address, err error) { - bz, err := getFromBech32(address, Bech32PrefixAccAddr) + bz, err := GetFromBech32(address, Bech32PrefixAccAddr) if err != nil { return nil, err } @@ -100,7 +100,7 @@ func GetAccAddressBech32(address string) (addr Address, err error) { // create a Pubkey from a string func GetAccPubKeyBech32(address string) (pk crypto.PubKey, err error) { - bz, err := getFromBech32(address, Bech32PrefixAccPub) + bz, err := GetFromBech32(address, Bech32PrefixAccPub) if err != nil { return nil, err } @@ -127,7 +127,7 @@ func GetValAddressHex(address string) (addr Address, err error) { // create an Address from a bech32 string func GetValAddressBech32(address string) (addr Address, err error) { - bz, err := getFromBech32(address, Bech32PrefixValAddr) + bz, err := GetFromBech32(address, Bech32PrefixValAddr) if err != nil { return nil, err } @@ -136,7 +136,7 @@ func GetValAddressBech32(address string) (addr Address, err error) { // decode a validator public key into a PubKey func GetValPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { - bz, err := getFromBech32(pubkey, Bech32PrefixValPub) + bz, err := GetFromBech32(pubkey, Bech32PrefixValPub) if err != nil { return nil, err } @@ -149,7 +149,8 @@ func GetValPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { return pk, nil } -func getFromBech32(bech32str, prefix string) ([]byte, error) { +// decode a bytestring from a bech32-encoded string +func GetFromBech32(bech32str, prefix string) ([]byte, error) { if len(bech32str) == 0 { return nil, errors.New("must provide non-empty string") } diff --git a/types/wire.go b/types/wire.go index 245b3677c7..2ef28820db 100644 --- a/types/wire.go +++ b/types/wire.go @@ -5,4 +5,5 @@ import wire "github.com/cosmos/cosmos-sdk/wire" // Register the sdk message type func RegisterWire(cdc *wire.Codec) { cdc.RegisterInterface((*Msg)(nil), nil) + cdc.RegisterInterface((*Tx)(nil), nil) } diff --git a/x/auth/wire.go b/x/auth/wire.go index 309464c864..6e430be4cd 100644 --- a/x/auth/wire.go +++ b/x/auth/wire.go @@ -9,6 +9,7 @@ func RegisterWire(cdc *wire.Codec) { cdc.RegisterInterface((*Account)(nil), nil) cdc.RegisterConcrete(&BaseAccount{}, "auth/Account", nil) cdc.RegisterConcrete(MsgChangeKey{}, "auth/ChangeKey", nil) + cdc.RegisterConcrete(StdTx{}, "auth/StdTx", nil) } var msgCdc = wire.NewCodec() diff --git a/x/bank/keeper.go b/x/bank/keeper.go index b14da4d81f..71c884ffe4 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -151,7 +151,7 @@ func subtractCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.Address, amt return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt)) } err := setCoins(ctx, am, addr, newCoins) - tags := sdk.NewTags("sender", addr.Bytes()) + tags := sdk.NewTags("sender", []byte(addr.String())) return newCoins, tags, err } @@ -164,7 +164,7 @@ func addCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.Address, amt sdk. return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt)) } err := setCoins(ctx, am, addr, newCoins) - tags := sdk.NewTags("recipient", addr.Bytes()) + tags := sdk.NewTags("recipient", []byte(addr.String())) return newCoins, tags, err }