From dafbe1fa5d2be1ff579955bf9bfe7694fd31e6dd Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Wed, 21 Jul 2021 15:06:22 +0200 Subject: [PATCH 01/19] WIP for auth query --- baseapp/baseapp.go | 23 ++++++++++ types/events.go | 5 +++ x/auth/client/cli/query.go | 51 +++++++++++++++++---- x/auth/client/testutil/suite.go | 79 ++++++++++++++++++++++++++++++--- 4 files changed, 143 insertions(+), 15 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 903fe55f926e..b01913056219 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "reflect" + "strconv" "strings" "github.com/gogo/protobuf/proto" @@ -21,6 +22,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdktx "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" ) const ( @@ -676,6 +678,27 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re // is a branch of a branch. runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes) + // Also emit the following events, so that txs can be indexed by these + // indices: + // - signature, + // - address and sequence. + events = append(events) + sigTx, ok := tx.(authsigning.SigVerifiableTx) + if !ok { + return sdk.GasInfo{}, nil, sdkerrors.ErrInvalidType.Wrapf("expected %T, got %T", (*authsigning.SigVerifiableTx)(nil), tx) + } + sigs, err := sigTx.GetSignaturesV2() + if err != nil { + return sdk.GasInfo{}, nil, err + } + + for _, sig := range sigs { + events = append(events, sdk.NewEvent(sdk.EventTypeTx, + sdk.NewAttribute(sdk.AttributeKeySequence, strconv.FormatUint(sig.Sequence, 10)), + // sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sig.Data)), + )) + } + // Attempt to execute all messages and only update state if all messages pass // and we're in DeliverTx. Note, runMsgs will never return a reference to a // Result if any single message fails or does not have a registered Handler. diff --git a/types/events.go b/types/events.go index 5a2bf3af4b08..56d4e7e6783f 100644 --- a/types/events.go +++ b/types/events.go @@ -223,6 +223,11 @@ func toBytes(i interface{}) []byte { // Common event types and attribute keys var ( + EventTypeTx = "tx" + + AttributeKeySequence = "sequence" + AttributeKeySignature = "signature" + EventTypeMessage = "message" AttributeKeyAction = "action" diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 6f378e8f8b56..ac5858d91ff5 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -17,7 +17,10 @@ import ( ) const ( - flagEvents = "events" + flagEvents = "events" + flagSignature = "signature" + flagAddress = "address" + flagSequence = "sequence" eventFormat = "{eventType}.{eventAttribute}={value}" ) @@ -212,19 +215,49 @@ func QueryTxCmd() *cobra.Command { cmd := &cobra.Command{ Use: "tx [hash]", Short: "Query for a transaction by hash in a committed block", - Args: cobra.ExactArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } - output, err := authtx.QueryTx(clientCtx, args[0]) - if err != nil { - return err - } - if output.Empty() { - return fmt.Errorf("no transaction found with hash %s", args[0]) + var output *sdk.TxResponse + fmt.Println("QueryTxCmd args=", args) + if len(args) > 0 && args[0] != "" { + // If hash is given, then query the tx by hash. + output, err = authtx.QueryTx(clientCtx, args[0]) + if err != nil { + return err + } + + if output.Empty() { + return fmt.Errorf("no transaction found with hash %s", args[0]) + } + } else { + // If hash is omitted, then query the tx: + // - either by signature + // - or by address and sequence + + addr, _ := cmd.Flags().GetString(flagAddress) + seq, _ := cmd.Flags().GetUint64(flagSequence) + if addr == "" && seq == 0 { + return fmt.Errorf("either") + } + tmEvents := []string{ + fmt.Sprintf("%s.%s=%s", sdk.EventTypeMessage, sdk.AttributeKeySender, addr), + fmt.Sprintf("%s.%s=%d", sdk.EventTypeTx, sdk.AttributeKeySequence, seq), + } + fmt.Println("QueryTxCmd tmEvents=", tmEvents) + txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "") + if err != nil { + return err + } + if len(txs.Txs) == 0 { + return fmt.Errorf("found no txs matching address and sequence combination") + } + + return clientCtx.PrintProto(txs.Txs[0]) } return clientCtx.PrintProto(output) @@ -232,6 +265,8 @@ func QueryTxCmd() *cobra.Command { } flags.AddQueryFlagsToCmd(cmd) + cmd.Flags().String(flagAddress, "", fmt.Sprintf("Query the tx by signer, to be used in conjunction with --%s", flagSequence)) + cmd.Flags().Int(flagSequence, 0, fmt.Sprintf("Query the tx by sequence, to be used in conjunction with --%s", flagAddress)) return cmd } diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index dc843db9fac6..5cf1bf748a5e 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -245,7 +245,7 @@ func checkSignatures(require *require.Assertions, txCfg client.TxConfig, output } } -func (s *IntegrationTestSuite) TestCLIQueryTxCmd() { +func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByHash() { val := s.network.Validators[0] account2, err := val.ClientCtx.Keyring.Key("newAccount2") @@ -269,23 +269,26 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() { expectErr bool rawLogContains string }{ + { + "not enough args", + []string{}, + true, "", + }, { "with invalid hash", []string{"somethinginvalid", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, - true, - "", + true, "", }, { "with valid and not existing hash", []string{"C7E7D3A86A17AB3A321172239F3B61357937AF0F25D9FA4D2F4DCCAD9B0D7747", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, - true, - "", + true, "", }, { - "happy case", + "with hash happy case", []string{txRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, - "/cosmos.bank.v1beta1.MsgSend", + sdk.MsgTypeURL(&banktypes.MsgSend{}), }, } @@ -310,6 +313,68 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() { } } +func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByEvents() { + val := s.network.Validators[0] + + account2, err := val.ClientCtx.Keyring.Key("newAccount2") + s.Require().NoError(err) + + sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) + + // Send coins. + out, err := s.createBankMsg( + val, account2.GetAddress(), + sdk.NewCoins(sendTokens), + ) + s.Require().NoError(err) + var txRes sdk.TxResponse + s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) + s.Require().NoError(s.network.WaitForNextBlock()) + + // Query the tx by hash to get the inner tx. + out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.QueryTxCmd(), []string{txRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}) + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) + + protoTx := txRes.GetTx().(*tx.Tx) + fmt.Printf("%T %s\n", txRes.GetTx(), txRes.GetTx().(*tx.Tx).Signatures) + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "with --address and --sequence happy case", + []string{ + fmt.Sprintf("--address=%s", val.Address.String()), + fmt.Sprintf("--sequence=%d", protoTx.AuthInfo.SignerInfos[0].Sequence), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := authcli.QueryTxCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().NotEqual("internal", err.Error()) + } else { + var result sdk.TxResponse + s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &result)) + s.Require().NotNil(result.Height) + } + }) + } +} + func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() { val1 := s.network.Validators[0] From d801fbd0a6f9d6102fbc15af237d5af73d5ea9d7 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Thu, 22 Jul 2021 12:14:52 +0200 Subject: [PATCH 02/19] Make query by addr and seq work --- x/auth/client/cli/query.go | 17 +++++++------- x/auth/client/testutil/suite.go | 39 ++++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index ac5858d91ff5..6e3df7c113e1 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -223,7 +223,6 @@ func QueryTxCmd() *cobra.Command { } var output *sdk.TxResponse - fmt.Println("QueryTxCmd args=", args) if len(args) > 0 && args[0] != "" { // If hash is given, then query the tx by hash. output, err = authtx.QueryTx(clientCtx, args[0]) @@ -240,21 +239,21 @@ func QueryTxCmd() *cobra.Command { // - or by address and sequence addr, _ := cmd.Flags().GetString(flagAddress) - seq, _ := cmd.Flags().GetUint64(flagSequence) - if addr == "" && seq == 0 { - return fmt.Errorf("either") + seq, _ := cmd.Flags().GetInt(flagSequence) + if addr == "" || seq < 0 { + return fmt.Errorf("both --address and --sequence need to be set") } + tmEvents := []string{ - fmt.Sprintf("%s.%s=%s", sdk.EventTypeMessage, sdk.AttributeKeySender, addr), - fmt.Sprintf("%s.%s=%d", sdk.EventTypeTx, sdk.AttributeKeySequence, seq), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, addr), + fmt.Sprintf("%s.%s='%d'", sdk.EventTypeTx, sdk.AttributeKeySequence, seq), } - fmt.Println("QueryTxCmd tmEvents=", tmEvents) txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "") if err != nil { return err } if len(txs.Txs) == 0 { - return fmt.Errorf("found no txs matching address and sequence combination") + return fmt.Errorf("found no txs matching given address and sequence combination") } return clientCtx.PrintProto(txs.Txs[0]) @@ -266,7 +265,7 @@ func QueryTxCmd() *cobra.Command { flags.AddQueryFlagsToCmd(cmd) cmd.Flags().String(flagAddress, "", fmt.Sprintf("Query the tx by signer, to be used in conjunction with --%s", flagSequence)) - cmd.Flags().Int(flagSequence, 0, fmt.Sprintf("Query the tx by sequence, to be used in conjunction with --%s", flagAddress)) + cmd.Flags().Int(flagSequence, -1, fmt.Sprintf("Query the tx by sequence, to be used in conjunction with --%s", flagAddress)) return cmd } diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index 5cf1bf748a5e..60fec0fdd9e4 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -335,15 +335,39 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByEvents() { out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.QueryTxCmd(), []string{txRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}) s.Require().NoError(err) s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) - protoTx := txRes.GetTx().(*tx.Tx) - fmt.Printf("%T %s\n", txRes.GetTx(), txRes.GetTx().(*tx.Tx).Signatures) testCases := []struct { - name string - args []string - expectErr bool + name string + args []string + expectErr bool + expectErrStr string }{ + { + "with --address only", + []string{ + fmt.Sprintf("--address=%s", val.Address.String()), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, "both --address and --sequence need to be set", + }, + { + "with --sequence only", + []string{ + fmt.Sprintf("--sequence=%d", protoTx.AuthInfo.SignerInfos[0].Sequence), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, "both --address and --sequence need to be set", + }, + { + "non-existing --address and --sequence combo", + []string{ + fmt.Sprintf("--address=%s", val.Address.String()), + fmt.Sprintf("--sequence=%d", 42), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, "found no txs matching given address and sequence combination", + }, { "with --address and --sequence happy case", []string{ @@ -351,7 +375,7 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByEvents() { fmt.Sprintf("--sequence=%d", protoTx.AuthInfo.SignerInfos[0].Sequence), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - false, + false, "", }, } @@ -362,10 +386,9 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByEvents() { clientCtx := val.ClientCtx out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) - if tc.expectErr { s.Require().Error(err) - s.Require().NotEqual("internal", err.Error()) + s.Require().Contains(err.Error(), tc.expectErrStr) } else { var result sdk.TxResponse s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &result)) From 36281007d3c5f09c25da92622d9cba36616ec738 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Thu, 22 Jul 2021 12:28:30 +0200 Subject: [PATCH 03/19] Add tests for sigs --- x/auth/client/cli/query.go | 103 ++++++++++++++++++++------------ x/auth/client/testutil/suite.go | 17 +++++- 2 files changed, 80 insertions(+), 40 deletions(-) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 6e3df7c113e1..9c118a660755 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -17,10 +17,10 @@ import ( ) const ( - flagEvents = "events" - flagSignature = "signature" - flagAddress = "address" - flagSequence = "sequence" + flagEvents = "events" + flagSignatures = "signatures" + flagAddress = "address" + flagSequence = "sequence" eventFormat = "{eventType}.{eventAttribute}={value}" ) @@ -222,50 +222,77 @@ func QueryTxCmd() *cobra.Command { return err } - var output *sdk.TxResponse - if len(args) > 0 && args[0] != "" { - // If hash is given, then query the tx by hash. - output, err = authtx.QueryTx(clientCtx, args[0]) - if err != nil { - return err + addr, _ := cmd.Flags().GetString(flagAddress) + seq, _ := cmd.Flags().GetInt(flagSequence) + sigs, _ := cmd.Flags().GetString(flagSignatures) + + switch { + // Query tx by hash. + case len(args) > 0 && args[0] != "": + { + // If hash is given, then query the tx by hash. + output, err := authtx.QueryTx(clientCtx, args[0]) + if err != nil { + return err + } + + if output.Empty() { + return fmt.Errorf("no transaction found with hash %s", args[0]) + } + + return clientCtx.PrintProto(output) } - - if output.Empty() { - return fmt.Errorf("no transaction found with hash %s", args[0]) - } - } else { - // If hash is omitted, then query the tx: - // - either by signature - // - or by address and sequence - - addr, _ := cmd.Flags().GetString(flagAddress) - seq, _ := cmd.Flags().GetInt(flagSequence) - if addr == "" || seq < 0 { - return fmt.Errorf("both --address and --sequence need to be set") - } - - tmEvents := []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, addr), - fmt.Sprintf("%s.%s='%d'", sdk.EventTypeTx, sdk.AttributeKeySequence, seq), - } - txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "") - if err != nil { - return err + // Query tx by signature. + case sigs != "": + { + tmEvents := []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, sigs), + } + txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "") + if err != nil { + return err + } + if len(txs.Txs) == 0 { + return fmt.Errorf("found no txs matching given signatures") + } + if len(txs.Txs) > 1 { + // This case means there's a bug somewhere else in the code. Should not happen. + return fmt.Errorf("found %d txs matching given signatures", len(txs.Txs)) + } + + return clientCtx.PrintProto(txs.Txs[0]) } - if len(txs.Txs) == 0 { - return fmt.Errorf("found no txs matching given address and sequence combination") + // Query tx by addr+seq combo. + case addr != "" && seq >= 0: + { + tmEvents := []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, addr), + fmt.Sprintf("%s.%s='%d'", sdk.EventTypeTx, sdk.AttributeKeySequence, seq), + } + txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "") + if err != nil { + return err + } + if len(txs.Txs) == 0 { + return fmt.Errorf("found no txs matching given address and sequence combination") + } + if len(txs.Txs) > 1 { + // This case means there's a bug somewhere else in the code. Should not happen. + return fmt.Errorf("found %d txs matching given address and sequence combination", len(txs.Txs)) + } + + return clientCtx.PrintProto(txs.Txs[0]) } - - return clientCtx.PrintProto(txs.Txs[0]) + default: + return fmt.Errorf("either pass a tx hash, OR a --signature flag, OR both --address and --sequence flags") } - - return clientCtx.PrintProto(output) }, } flags.AddQueryFlagsToCmd(cmd) cmd.Flags().String(flagAddress, "", fmt.Sprintf("Query the tx by signer, to be used in conjunction with --%s", flagSequence)) cmd.Flags().Int(flagSequence, -1, fmt.Sprintf("Query the tx by sequence, to be used in conjunction with --%s", flagAddress)) + cmd.Flags().String(flagSignatures, "", "Query the tx by comma-separated signatures in base64 encoding") return cmd } diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index 60fec0fdd9e4..5ed321d83148 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -343,13 +343,18 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByEvents() { expectErr bool expectErrStr string }{ + { + "with no flags", + []string{}, + true, "either pass a tx hash, OR a --signature flag, OR both --address and --sequence flags", + }, { "with --address only", []string{ fmt.Sprintf("--address=%s", val.Address.String()), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - true, "both --address and --sequence need to be set", + true, "either pass a tx hash, OR a --signature flag, OR both --address and --sequence flags", }, { "with --sequence only", @@ -357,7 +362,7 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByEvents() { fmt.Sprintf("--sequence=%d", protoTx.AuthInfo.SignerInfos[0].Sequence), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - true, "both --address and --sequence need to be set", + true, "either pass a tx hash, OR a --signature flag, OR both --address and --sequence flags", }, { "non-existing --address and --sequence combo", @@ -377,6 +382,14 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByEvents() { }, false, "", }, + { + "non-existing --signatures", + []string{ + fmt.Sprintf("--signatures=%s", "foo"), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, "found no txs matching given signatures", + }, } for _, tc := range testCases { From ea3dc2e6efda19013896eb8e022dfd52c4eeeca1 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Thu, 22 Jul 2021 13:29:40 +0200 Subject: [PATCH 04/19] Make query by sig work --- baseapp/baseapp.go | 52 ++++++++++++++++++++++++++++++--- x/auth/client/cli/query.go | 7 +++-- x/auth/client/testutil/suite.go | 13 +++++++-- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index b01913056219..98dfc83be788 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -1,6 +1,7 @@ package baseapp import ( + "encoding/base64" "errors" "fmt" "reflect" @@ -15,12 +16,14 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store/rootmulti" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdktx "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" ) @@ -680,9 +683,8 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re // Also emit the following events, so that txs can be indexed by these // indices: - // - signature, - // - address and sequence. - events = append(events) + // - signature (via `tx.signature=''`), + // - address and sequence (via `message.sender= AND tx.sequence='' `). sigTx, ok := tx.(authsigning.SigVerifiableTx) if !ok { return sdk.GasInfo{}, nil, sdkerrors.ErrInvalidType.Wrapf("expected %T, got %T", (*authsigning.SigVerifiableTx)(nil), tx) @@ -693,9 +695,14 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re } for _, sig := range sigs { + sigBz, err := signatureDataToBz(sig.Data) + if err != nil { + return sdk.GasInfo{}, nil, err + } + events = append(events, sdk.NewEvent(sdk.EventTypeTx, sdk.NewAttribute(sdk.AttributeKeySequence, strconv.FormatUint(sig.Sequence, 10)), - // sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sig.Data)), + sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz)), )) } @@ -792,3 +799,40 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*s Events: events.ToABCIEvents(), }, nil } + +// signatureDataToBz converts a SignatureData into raw bytes signature. It is +// the same function as in auth/tx/sigs.go, but copied here because of import +// cycles. +func signatureDataToBz(data signing.SignatureData) ([]byte, error) { + if data == nil { + return nil, fmt.Errorf("got empty SignatureData") + } + + switch data := data.(type) { + case *signing.SingleSignatureData: + return data.Signature, nil + case *signing.MultiSignatureData: + n := len(data.Signatures) + sigs := make([][]byte, n) + var err error + + for i, d := range data.Signatures { + sigs[i], err = signatureDataToBz(d) + if err != nil { + return nil, err + } + } + + multisig := cryptotypes.MultiSignature{ + Signatures: sigs, + } + sig, err := multisig.Marshal() + if err != nil { + return nil, err + } + + return sig, nil + default: + return nil, fmt.Errorf("unexpected signature data type %T", data) + } +} diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 9c118a660755..14233f462cb7 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -245,9 +245,12 @@ func QueryTxCmd() *cobra.Command { // Query tx by signature. case sigs != "": { - tmEvents := []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, sigs), + sigParts := strings.Split(sigs, ",") + tmEvents := make([]string, len(sigParts)) + for i, sig := range sigParts { + tmEvents[i] = fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, sig) } + txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "") if err != nil { return err diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index 5ed321d83148..970cc4f49e3c 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -2,6 +2,7 @@ package testutil import ( "context" + "encoding/base64" "encoding/json" "fmt" "io/ioutil" @@ -285,7 +286,7 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByHash() { true, "", }, { - "with hash happy case", + "happy case", []string{txRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, sdk.MsgTypeURL(&banktypes.MsgSend{}), @@ -390,6 +391,14 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByEvents() { }, true, "found no txs matching given signatures", }, + { + "with --signatures happy case", + []string{ + fmt.Sprintf("--signatures=%s", base64.StdEncoding.EncodeToString(protoTx.Signatures[0])), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, "", + }, } for _, tc := range testCases { @@ -782,7 +791,7 @@ func (s *IntegrationTestSuite) TestCLIMultisign() { sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) - // Sign with account1 + // Sign with account2 account2Signature, err := TxSignExec(val1.ClientCtx, account2.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) s.Require().NoError(err) From 96d357c96be8654eb779eed8c211eccf37de92c5 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Thu, 22 Jul 2021 13:51:17 +0200 Subject: [PATCH 05/19] Fix lint --- x/auth/client/testutil/suite.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index 970cc4f49e3c..e27eb38705f9 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -246,7 +246,7 @@ func checkSignatures(require *require.Assertions, txCfg client.TxConfig, output } } -func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByHash() { +func (s *IntegrationTestSuite) TestCLIQueryTxCmdByHash() { val := s.network.Validators[0] account2, err := val.ClientCtx.Keyring.Key("newAccount2") @@ -314,7 +314,7 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByHash() { } } -func (s *IntegrationTestSuite) TestCLIQueryTxCmd_ByEvents() { +func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { val := s.network.Validators[0] account2, err := val.ClientCtx.Keyring.Key("newAccount2") From 069e8233029f54ec29311da814ea7e35961a4e89 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Thu, 22 Jul 2021 13:58:24 +0200 Subject: [PATCH 06/19] Improve err message --- x/auth/client/cli/query.go | 2 +- x/auth/client/testutil/suite.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 14233f462cb7..7e093f0dc820 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -287,7 +287,7 @@ func QueryTxCmd() *cobra.Command { return clientCtx.PrintProto(txs.Txs[0]) } default: - return fmt.Errorf("either pass a tx hash, OR a --signature flag, OR both --address and --sequence flags") + return fmt.Errorf("either pass a tx hash, OR a --%s flag, OR both --%s and --%s flags", flagSignatures, flagAddress, flagSequence) } }, } diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index e27eb38705f9..d4290b45a0d9 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -347,7 +347,7 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { { "with no flags", []string{}, - true, "either pass a tx hash, OR a --signature flag, OR both --address and --sequence flags", + true, "either pass a tx hash, OR a --signatures flag, OR both --address and --sequence flags", }, { "with --address only", @@ -355,7 +355,7 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { fmt.Sprintf("--address=%s", val.Address.String()), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - true, "either pass a tx hash, OR a --signature flag, OR both --address and --sequence flags", + true, "either pass a tx hash, OR a --signatures flag, OR both --address and --sequence flags", }, { "with --sequence only", @@ -363,7 +363,7 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { fmt.Sprintf("--sequence=%d", protoTx.AuthInfo.SignerInfos[0].Sequence), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - true, "either pass a tx hash, OR a --signature flag, OR both --address and --sequence flags", + true, "either pass a tx hash, OR a --signatures flag, OR both --address and --sequence flags", }, { "non-existing --address and --sequence combo", From f319d4a78d758a32d4684568bbd89bf248ec3e69 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Thu, 22 Jul 2021 14:27:09 +0200 Subject: [PATCH 07/19] Move HasExtensionOptionsTx to authtx? --- baseapp/baseapp.go | 67 ---------------------------------------- x/auth/ante/ext.go | 9 ++---- x/auth/ante/sigverify.go | 26 ++++++++++++++++ x/auth/tx/builder.go | 14 ++++++--- 4 files changed, 37 insertions(+), 79 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 98dfc83be788..903fe55f926e 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -1,11 +1,9 @@ package baseapp import ( - "encoding/base64" "errors" "fmt" "reflect" - "strconv" "strings" "github.com/gogo/protobuf/proto" @@ -16,16 +14,13 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec/types" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store/rootmulti" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdktx "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" - authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" ) const ( @@ -681,31 +676,6 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re // is a branch of a branch. runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes) - // Also emit the following events, so that txs can be indexed by these - // indices: - // - signature (via `tx.signature=''`), - // - address and sequence (via `message.sender= AND tx.sequence='' `). - sigTx, ok := tx.(authsigning.SigVerifiableTx) - if !ok { - return sdk.GasInfo{}, nil, sdkerrors.ErrInvalidType.Wrapf("expected %T, got %T", (*authsigning.SigVerifiableTx)(nil), tx) - } - sigs, err := sigTx.GetSignaturesV2() - if err != nil { - return sdk.GasInfo{}, nil, err - } - - for _, sig := range sigs { - sigBz, err := signatureDataToBz(sig.Data) - if err != nil { - return sdk.GasInfo{}, nil, err - } - - events = append(events, sdk.NewEvent(sdk.EventTypeTx, - sdk.NewAttribute(sdk.AttributeKeySequence, strconv.FormatUint(sig.Sequence, 10)), - sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz)), - )) - } - // Attempt to execute all messages and only update state if all messages pass // and we're in DeliverTx. Note, runMsgs will never return a reference to a // Result if any single message fails or does not have a registered Handler. @@ -799,40 +769,3 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*s Events: events.ToABCIEvents(), }, nil } - -// signatureDataToBz converts a SignatureData into raw bytes signature. It is -// the same function as in auth/tx/sigs.go, but copied here because of import -// cycles. -func signatureDataToBz(data signing.SignatureData) ([]byte, error) { - if data == nil { - return nil, fmt.Errorf("got empty SignatureData") - } - - switch data := data.(type) { - case *signing.SingleSignatureData: - return data.Signature, nil - case *signing.MultiSignatureData: - n := len(data.Signatures) - sigs := make([][]byte, n) - var err error - - for i, d := range data.Signatures { - sigs[i], err = signatureDataToBz(d) - if err != nil { - return nil, err - } - } - - multisig := cryptotypes.MultiSignature{ - Signatures: sigs, - } - sig, err := multisig.Marshal() - if err != nil { - return nil, err - } - - return sig, nil - default: - return nil, fmt.Errorf("unexpected signature data type %T", data) - } -} diff --git a/x/auth/ante/ext.go b/x/auth/ante/ext.go index 362b8d32a971..e8650aec9de8 100644 --- a/x/auth/ante/ext.go +++ b/x/auth/ante/ext.go @@ -1,16 +1,11 @@ package ante import ( - codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" ) -type HasExtensionOptionsTx interface { - GetExtensionOptions() []*codectypes.Any - GetNonCriticalExtensionOptions() []*codectypes.Any -} - // RejectExtensionOptionsDecorator is an AnteDecorator that rejects all extension // options which can optionally be included in protobuf transactions. Users that // need extension options should create a custom AnteHandler chain that handles @@ -26,7 +21,7 @@ var _ types.AnteDecorator = RejectExtensionOptionsDecorator{} // AnteHandle implements the AnteDecorator.AnteHandle method func (r RejectExtensionOptionsDecorator) AnteHandle(ctx types.Context, tx types.Tx, simulate bool, next types.AnteHandler) (newCtx types.Context, err error) { - if hasExtOptsTx, ok := tx.(HasExtensionOptionsTx); ok { + if hasExtOptsTx, ok := tx.(authtx.HasExtensionOptionsTx); ok { if len(hasExtOptsTx.GetExtensionOptions()) != 0 { return ctx, sdkerrors.ErrUnknownExtensionOptions } diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index 49de89b220fa..f855bd026e16 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -2,8 +2,10 @@ package ante import ( "bytes" + "encoding/base64" "encoding/hex" "fmt" + "strconv" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" @@ -16,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -94,6 +97,29 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b spkd.ak.SetAccount(ctx, acc) } + // Also emit the following events, so that txs can be indexed by these + // indices: + // - signature (via `tx.signature=''`), + // - address and sequence (via `message.sender= AND tx.sequence='' `). + sigs, err := sigTx.GetSignaturesV2() + if err != nil { + return ctx, err + } + + var events sdk.Events + for _, sig := range sigs { + _, sigBz := authtx.SignatureDataToModeInfoAndSig(sig.Data) + if err != nil { + return ctx, err + } + + events = append(events, sdk.NewEvent(sdk.EventTypeTx, + sdk.NewAttribute(sdk.AttributeKeySequence, strconv.FormatUint(sig.Sequence, 10)), + sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz)), + )) + } + ctx.EventManager().EmitEvents(events) + return next(ctx, tx, simulate) } diff --git a/x/auth/tx/builder.go b/x/auth/tx/builder.go index 359c646087a5..ccfc229d5982 100644 --- a/x/auth/tx/builder.go +++ b/x/auth/tx/builder.go @@ -10,10 +10,14 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/types/tx/signing" - "github.com/cosmos/cosmos-sdk/x/auth/ante" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" ) +type HasExtensionOptionsTx interface { + GetExtensionOptions() []*codectypes.Any + GetNonCriticalExtensionOptions() []*codectypes.Any +} + // wrapper is a wrapper around the tx.Tx proto.Message which retain the raw // body and auth_info bytes. type wrapper struct { @@ -31,10 +35,10 @@ type wrapper struct { } var ( - _ authsigning.Tx = &wrapper{} - _ client.TxBuilder = &wrapper{} - _ ante.HasExtensionOptionsTx = &wrapper{} - _ ExtensionOptionsTxBuilder = &wrapper{} + _ authsigning.Tx = &wrapper{} + _ client.TxBuilder = &wrapper{} + _ HasExtensionOptionsTx = &wrapper{} + _ ExtensionOptionsTxBuilder = &wrapper{} ) // ExtensionOptionsTxBuilder defines a TxBuilder that can also set extensions. From eb065334a7883b8d9b5b4361f9eac734ef29e999 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Thu, 22 Jul 2021 15:15:34 +0200 Subject: [PATCH 08/19] Switch to bez's CLI UX --- x/auth/client/cli/query.go | 55 ++++++++++++++++++++------------- x/auth/client/testutil/suite.go | 51 ++++++++++++++++++++---------- 2 files changed, 69 insertions(+), 37 deletions(-) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 7e093f0dc820..981cad573c8b 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -17,10 +17,13 @@ import ( ) const ( - flagEvents = "events" - flagSignatures = "signatures" - flagAddress = "address" - flagSequence = "sequence" + flagEvents = "events" + flagType = "type" + flagAddress = "address" + + typeHash = "hash" + typeSeq = "sequence" + typeSig = "signature" eventFormat = "{eventType}.{eventAttribute}={value}" ) @@ -215,21 +218,22 @@ func QueryTxCmd() *cobra.Command { cmd := &cobra.Command{ Use: "tx [hash]", Short: "Query for a transaction by hash in a committed block", - Args: cobra.MaximumNArgs(1), + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } - addr, _ := cmd.Flags().GetString(flagAddress) - seq, _ := cmd.Flags().GetInt(flagSequence) - sigs, _ := cmd.Flags().GetString(flagSignatures) + typ, _ := cmd.Flags().GetString(flagType) - switch { - // Query tx by hash. - case len(args) > 0 && args[0] != "": + switch typ { + case typeHash: { + if args[0] == "" { + return fmt.Errorf("argument should be a tx hash") + } + // If hash is given, then query the tx by hash. output, err := authtx.QueryTx(clientCtx, args[0]) if err != nil { @@ -242,10 +246,13 @@ func QueryTxCmd() *cobra.Command { return clientCtx.PrintProto(output) } - // Query tx by signature. - case sigs != "": + case typeSig: { - sigParts := strings.Split(sigs, ",") + if args[0] == "" { + return fmt.Errorf("argument should be comma-separated signatures") + } + + sigParts := strings.Split(args[0], ",") tmEvents := make([]string, len(sigParts)) for i, sig := range sigParts { tmEvents[i] = fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, sig) @@ -265,12 +272,19 @@ func QueryTxCmd() *cobra.Command { return clientCtx.PrintProto(txs.Txs[0]) } - // Query tx by addr+seq combo. - case addr != "" && seq >= 0: + case typeSeq: { + if args[0] == "" { + return fmt.Errorf("argument should be a sequence") + } + addr, _ := cmd.Flags().GetString(flagAddress) + if addr == "" { + return fmt.Errorf("--%s is required when using --%s=%s", flagAddress, flagType, typeSeq) + } + tmEvents := []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, addr), - fmt.Sprintf("%s.%s='%d'", sdk.EventTypeTx, sdk.AttributeKeySequence, seq), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySequence, args[0]), } txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "") if err != nil { @@ -287,15 +301,14 @@ func QueryTxCmd() *cobra.Command { return clientCtx.PrintProto(txs.Txs[0]) } default: - return fmt.Errorf("either pass a tx hash, OR a --%s flag, OR both --%s and --%s flags", flagSignatures, flagAddress, flagSequence) + return fmt.Errorf("unknown --%s value %s", flagType, typ) } }, } flags.AddQueryFlagsToCmd(cmd) - cmd.Flags().String(flagAddress, "", fmt.Sprintf("Query the tx by signer, to be used in conjunction with --%s", flagSequence)) - cmd.Flags().Int(flagSequence, -1, fmt.Sprintf("Query the tx by sequence, to be used in conjunction with --%s", flagAddress)) - cmd.Flags().String(flagSignatures, "", "Query the tx by comma-separated signatures in base64 encoding") + cmd.Flags().String(flagAddress, "", fmt.Sprintf("Query the tx by signer and sequence, required if --%s=%s", flagType, typeSeq)) + cmd.Flags().String(flagType, typeHash, fmt.Sprintf("The type to be used when querying tx, can be one of %s, %s,%s", typeHash, typeSeq, typeSig)) return cmd } diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index d4290b45a0d9..fe519197e408 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -345,48 +345,66 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { expectErrStr string }{ { - "with no flags", - []string{}, - true, "either pass a tx hash, OR a --signatures flag, OR both --address and --sequence flags", + "invalid --type", + []string{ + fmt.Sprintf("--type=%s", "foo"), + "bar", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, "unknown --type value foo", }, { - "with --address only", + "--type=sequence with no sequence", []string{ - fmt.Sprintf("--address=%s", val.Address.String()), + "--type=sequence", + "", fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - true, "either pass a tx hash, OR a --signatures flag, OR both --address and --sequence flags", + true, "argument should be a sequence", }, { - "with --sequence only", + "--type=sequence with no address", []string{ - fmt.Sprintf("--sequence=%d", protoTx.AuthInfo.SignerInfos[0].Sequence), + "--type=sequence", + fmt.Sprintf("%d", protoTx.AuthInfo.SignerInfos[0].Sequence), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - true, "either pass a tx hash, OR a --signatures flag, OR both --address and --sequence flags", + true, "--address is required when using --type=sequence", }, { - "non-existing --address and --sequence combo", + "non-existing addr+seq combo", []string{ + "--type=sequence", + "42", fmt.Sprintf("--address=%s", val.Address.String()), - fmt.Sprintf("--sequence=%d", 42), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, true, "found no txs matching given address and sequence combination", }, { - "with --address and --sequence happy case", + "addr+seq happy case", []string{ + "--type=sequence", + fmt.Sprintf("%d", protoTx.AuthInfo.SignerInfos[0].Sequence), fmt.Sprintf("--address=%s", val.Address.String()), - fmt.Sprintf("--sequence=%d", protoTx.AuthInfo.SignerInfos[0].Sequence), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, false, "", }, { - "non-existing --signatures", + "--type=signature with no signature", + []string{ + "--type=signature", + "", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, "argument should be comma-separated signatures", + }, + { + "non-existing signatures", []string{ - fmt.Sprintf("--signatures=%s", "foo"), + "--type=signature", + "foo", fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, true, "found no txs matching given signatures", @@ -394,7 +412,8 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { { "with --signatures happy case", []string{ - fmt.Sprintf("--signatures=%s", base64.StdEncoding.EncodeToString(protoTx.Signatures[0])), + "--type=signature", + base64.StdEncoding.EncodeToString(protoTx.Signatures[0]), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, false, "", From b7218b1dd9c1705049a89357812bdc6fd0711974 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Thu, 22 Jul 2021 15:23:02 +0200 Subject: [PATCH 09/19] Fix cycle dep --- x/auth/ante/ext.go | 9 +++++++-- x/auth/ante/sigverify.go | 40 ++++++++++++++++++++++++++++++++++++++-- x/auth/tx/builder.go | 14 +++++--------- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/x/auth/ante/ext.go b/x/auth/ante/ext.go index e8650aec9de8..362b8d32a971 100644 --- a/x/auth/ante/ext.go +++ b/x/auth/ante/ext.go @@ -1,11 +1,16 @@ package ante import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" ) +type HasExtensionOptionsTx interface { + GetExtensionOptions() []*codectypes.Any + GetNonCriticalExtensionOptions() []*codectypes.Any +} + // RejectExtensionOptionsDecorator is an AnteDecorator that rejects all extension // options which can optionally be included in protobuf transactions. Users that // need extension options should create a custom AnteHandler chain that handles @@ -21,7 +26,7 @@ var _ types.AnteDecorator = RejectExtensionOptionsDecorator{} // AnteHandle implements the AnteDecorator.AnteHandle method func (r RejectExtensionOptionsDecorator) AnteHandle(ctx types.Context, tx types.Tx, simulate bool, next types.AnteHandler) (newCtx types.Context, err error) { - if hasExtOptsTx, ok := tx.(authtx.HasExtensionOptionsTx); ok { + if hasExtOptsTx, ok := tx.(HasExtensionOptionsTx); ok { if len(hasExtOptsTx.GetExtensionOptions()) != 0 { return ctx, sdkerrors.ErrUnknownExtensionOptions } diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index f855bd026e16..0eb133c8673a 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -18,7 +18,6 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -108,7 +107,7 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b var events sdk.Events for _, sig := range sigs { - _, sigBz := authtx.SignatureDataToModeInfoAndSig(sig.Data) + sigBz, err := signatureDataToBz(sig.Data) if err != nil { return ctx, err } @@ -473,3 +472,40 @@ func CountSubKeys(pub cryptotypes.PubKey) int { return numKeys } + +// signatureDataToBz converts a SignatureData into raw bytes signature. It is +// the same function as in auth/tx/sigs.go, but copied here because of import +// cycles. +func signatureDataToBz(data signing.SignatureData) ([]byte, error) { + if data == nil { + return nil, fmt.Errorf("got empty SignatureData") + } + + switch data := data.(type) { + case *signing.SingleSignatureData: + return data.Signature, nil + case *signing.MultiSignatureData: + n := len(data.Signatures) + sigs := make([][]byte, n) + var err error + + for i, d := range data.Signatures { + sigs[i], err = signatureDataToBz(d) + if err != nil { + return nil, err + } + } + + multisig := cryptotypes.MultiSignature{ + Signatures: sigs, + } + sig, err := multisig.Marshal() + if err != nil { + return nil, err + } + + return sig, nil + default: + return nil, fmt.Errorf("unexpected signature data type %T", data) + } +} diff --git a/x/auth/tx/builder.go b/x/auth/tx/builder.go index ccfc229d5982..359c646087a5 100644 --- a/x/auth/tx/builder.go +++ b/x/auth/tx/builder.go @@ -10,14 +10,10 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/ante" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" ) -type HasExtensionOptionsTx interface { - GetExtensionOptions() []*codectypes.Any - GetNonCriticalExtensionOptions() []*codectypes.Any -} - // wrapper is a wrapper around the tx.Tx proto.Message which retain the raw // body and auth_info bytes. type wrapper struct { @@ -35,10 +31,10 @@ type wrapper struct { } var ( - _ authsigning.Tx = &wrapper{} - _ client.TxBuilder = &wrapper{} - _ HasExtensionOptionsTx = &wrapper{} - _ ExtensionOptionsTxBuilder = &wrapper{} + _ authsigning.Tx = &wrapper{} + _ client.TxBuilder = &wrapper{} + _ ante.HasExtensionOptionsTx = &wrapper{} + _ ExtensionOptionsTxBuilder = &wrapper{} ) // ExtensionOptionsTxBuilder defines a TxBuilder that can also set extensions. From 56c331a9de0f9ad0933affa4501c68ae9ab3be92 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Thu, 22 Jul 2021 15:47:30 +0200 Subject: [PATCH 10/19] Cleanups --- CHANGELOG.md | 1 + x/auth/ante/sigverify.go | 1 + x/auth/client/cli/query.go | 16 +++++++++++----- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8350a6fb8ad4..7b76ff39965c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9533](https://github.com/cosmos/cosmos-sdk/pull/9533) Added a new gRPC method, `DenomOwners`, in `x/bank` to query for all account holders of a specific denomination. * (bank) [\#9618](https://github.com/cosmos/cosmos-sdk/pull/9618) Update bank.Metadata: add URI and URIHash attributes. +* [\#9750](https://github.com/cosmos/cosmos-sdk/pull/9750) Emit events for tx signature and sequence, so clients can now query by signature (`tx.signature=''`) or by address and sequence combo (`tx.sequence='' AND message.sender=''`). ### API Breaking Changes diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index 0eb133c8673a..c752d8212d84 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -476,6 +476,7 @@ func CountSubKeys(pub cryptotypes.PubKey) int { // signatureDataToBz converts a SignatureData into raw bytes signature. It is // the same function as in auth/tx/sigs.go, but copied here because of import // cycles. +// TODO: https://github.com/cosmos/cosmos-sdk/issues/9753 func signatureDataToBz(data signing.SignatureData) ([]byte, error) { if data == nil { return nil, fmt.Errorf("got empty SignatureData") diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 981cad573c8b..6f7845016a71 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -216,9 +216,15 @@ $ %s query txs --%s 'message.sender=cosmos1...&message.action=withdraw_delegator // QueryTxCmd implements the default command for a tx query. func QueryTxCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "tx [hash]", - Short: "Query for a transaction by hash in a committed block", - Args: cobra.ExactArgs(1), + Use: "tx [hash|sequence|signature] --type=[hash|sequence|signature]", + Short: "Query for a transaction by hash, sequence or signature in a committed block", + Long: strings.TrimSpace(fmt.Sprintf(` +Example: +$ %s query tx +$ %s query tx --%s=%s --%s= +$ %s query tx --%s=%s +`, version.AppName, version.AppName, flagType, typeSeq, flagAddress, version.AppName, flagType, typeSig)), + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { @@ -307,8 +313,8 @@ func QueryTxCmd() *cobra.Command { } flags.AddQueryFlagsToCmd(cmd) - cmd.Flags().String(flagAddress, "", fmt.Sprintf("Query the tx by signer and sequence, required if --%s=%s", flagType, typeSeq)) - cmd.Flags().String(flagType, typeHash, fmt.Sprintf("The type to be used when querying tx, can be one of %s, %s,%s", typeHash, typeSeq, typeSig)) + cmd.Flags().String(flagAddress, "", fmt.Sprintf("Query the tx by signer and sequence, required if --%s=%s is set", flagType, typeSeq)) + cmd.Flags().String(flagType, typeHash, fmt.Sprintf("The type to be used when querying tx, can be one of \"%s\", \"%s\", \"%s\"", typeHash, typeSeq, typeSig)) return cmd } From 620b3ea6e9ce85a7f2c5deb21c606a20f5c08807 Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaurym@users.noreply.github.com> Date: Thu, 22 Jul 2021 15:49:00 +0200 Subject: [PATCH 11/19] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b76ff39965c..aea87c1e556a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9533](https://github.com/cosmos/cosmos-sdk/pull/9533) Added a new gRPC method, `DenomOwners`, in `x/bank` to query for all account holders of a specific denomination. * (bank) [\#9618](https://github.com/cosmos/cosmos-sdk/pull/9618) Update bank.Metadata: add URI and URIHash attributes. -* [\#9750](https://github.com/cosmos/cosmos-sdk/pull/9750) Emit events for tx signature and sequence, so clients can now query by signature (`tx.signature=''`) or by address and sequence combo (`tx.sequence='' AND message.sender=''`). +* [\#9750](https://github.com/cosmos/cosmos-sdk/pull/9750) Emit events for tx signature and sequence, so clients can now query txs by signature (`tx.signature=''`) or by address and sequence combo (`tx.sequence='' AND message.sender=''`). ### API Breaking Changes From 76f6fdd880f9579454bc6df4f48a15e3b28daff7 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Fri, 23 Jul 2021 09:42:37 +0200 Subject: [PATCH 12/19] Emit all nested sigs --- x/auth/ante/sigverify.go | 42 +++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index c752d8212d84..b9f8302681b1 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -107,16 +107,21 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b var events sdk.Events for _, sig := range sigs { - sigBz, err := signatureDataToBz(sig.Data) - if err != nil { - return ctx, err - } - events = append(events, sdk.NewEvent(sdk.EventTypeTx, sdk.NewAttribute(sdk.AttributeKeySequence, strconv.FormatUint(sig.Sequence, 10)), - sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz)), )) + + sigBzs, err := signatureDataToBz(sig.Data) + if err != nil { + return ctx, err + } + for _, sigBz := range sigBzs { + events = append(events, sdk.NewEvent(sdk.EventTypeTx, + sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz)), + )) + } } + ctx.EventManager().EmitEvents(events) return next(ctx, tx, simulate) @@ -473,39 +478,40 @@ func CountSubKeys(pub cryptotypes.PubKey) int { return numKeys } -// signatureDataToBz converts a SignatureData into raw bytes signature. It is -// the same function as in auth/tx/sigs.go, but copied here because of import -// cycles. -// TODO: https://github.com/cosmos/cosmos-sdk/issues/9753 -func signatureDataToBz(data signing.SignatureData) ([]byte, error) { +// signatureDataToBz converts a SignatureData into raw bytes signature. +// For SingleSignatureData, it returns the signature raw bytes. +// For MultiSignatureData, it returns an array of all individual signatures, +// as well as the aggregated signature. +func signatureDataToBz(data signing.SignatureData) ([][]byte, error) { if data == nil { return nil, fmt.Errorf("got empty SignatureData") } switch data := data.(type) { case *signing.SingleSignatureData: - return data.Signature, nil + return [][]byte{data.Signature}, nil case *signing.MultiSignatureData: - n := len(data.Signatures) - sigs := make([][]byte, n) + sigs := [][]byte{} var err error - for i, d := range data.Signatures { - sigs[i], err = signatureDataToBz(d) + for _, d := range data.Signatures { + nestedSigs, err := signatureDataToBz(d) if err != nil { return nil, err } + sigs = append(sigs, nestedSigs...) } multisig := cryptotypes.MultiSignature{ Signatures: sigs, } - sig, err := multisig.Marshal() + aggregatedSig, err := multisig.Marshal() if err != nil { return nil, err } + sigs = append(sigs, aggregatedSig) - return sig, nil + return sigs, nil default: return nil, fmt.Errorf("unexpected signature data type %T", data) } From f6e61e12b8cbc063224a36c3e313a34265f26648 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Fri, 23 Jul 2021 10:28:29 +0200 Subject: [PATCH 13/19] Index by addr++seq --- types/events.go | 4 ++-- x/auth/ante/sigverify.go | 5 ++--- x/auth/client/cli/query.go | 33 +++++++++++++-------------------- x/auth/client/testutil/suite.go | 23 ++++++----------------- 4 files changed, 23 insertions(+), 42 deletions(-) diff --git a/types/events.go b/types/events.go index 56d4e7e6783f..27a0017635af 100644 --- a/types/events.go +++ b/types/events.go @@ -225,8 +225,8 @@ func toBytes(i interface{}) []byte { var ( EventTypeTx = "tx" - AttributeKeySequence = "sequence" - AttributeKeySignature = "signature" + AttributeKeyAccountSequence = "acc_seq" + AttributeKeySignature = "signature" EventTypeMessage = "message" diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index b9f8302681b1..7a4500066355 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "encoding/hex" "fmt" - "strconv" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" @@ -106,9 +105,9 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b } var events sdk.Events - for _, sig := range sigs { + for i, sig := range sigs { events = append(events, sdk.NewEvent(sdk.EventTypeTx, - sdk.NewAttribute(sdk.AttributeKeySequence, strconv.FormatUint(sig.Sequence, 10)), + sdk.NewAttribute(sdk.AttributeKeyAccountSequence, fmt.Sprintf("%s%d", signers[i], sig.Sequence)), )) sigBzs, err := signatureDataToBz(sig.Data) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 6f7845016a71..d4ad5ad44309 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -17,13 +17,12 @@ import ( ) const ( - flagEvents = "events" - flagType = "type" - flagAddress = "address" + flagEvents = "events" + flagType = "type" - typeHash = "hash" - typeSeq = "sequence" - typeSig = "signature" + typeHash = "hash" + typeAccSeq = "acc_seq" + typeSig = "signature" eventFormat = "{eventType}.{eventAttribute}={value}" ) @@ -216,14 +215,14 @@ $ %s query txs --%s 'message.sender=cosmos1...&message.action=withdraw_delegator // QueryTxCmd implements the default command for a tx query. func QueryTxCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "tx [hash|sequence|signature] --type=[hash|sequence|signature]", - Short: "Query for a transaction by hash, sequence or signature in a committed block", + Use: "tx [hash|acc_seq|signature] --type=[hash|acc_seq|signature]", + Short: "Query for a transaction by hash, addr++seq combination or signature in a committed block", Long: strings.TrimSpace(fmt.Sprintf(` Example: $ %s query tx -$ %s query tx --%s=%s --%s= +$ %s query tx --%s=%s $ %s query tx --%s=%s -`, version.AppName, version.AppName, flagType, typeSeq, flagAddress, version.AppName, flagType, typeSig)), +`, version.AppName, version.AppName, flagType, typeAccSeq, version.AppName, flagType, typeSig)), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -278,19 +277,14 @@ $ %s query tx --%s=%s return clientCtx.PrintProto(txs.Txs[0]) } - case typeSeq: + case typeAccSeq: { if args[0] == "" { - return fmt.Errorf("argument should be a sequence") - } - addr, _ := cmd.Flags().GetString(flagAddress) - if addr == "" { - return fmt.Errorf("--%s is required when using --%s=%s", flagAddress, flagType, typeSeq) + return fmt.Errorf("argument should be a combination") } tmEvents := []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, addr), - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySequence, args[0]), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeyAccountSequence, args[0]), } txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "") if err != nil { @@ -313,8 +307,7 @@ $ %s query tx --%s=%s } flags.AddQueryFlagsToCmd(cmd) - cmd.Flags().String(flagAddress, "", fmt.Sprintf("Query the tx by signer and sequence, required if --%s=%s is set", flagType, typeSeq)) - cmd.Flags().String(flagType, typeHash, fmt.Sprintf("The type to be used when querying tx, can be one of \"%s\", \"%s\", \"%s\"", typeHash, typeSeq, typeSig)) + cmd.Flags().String(flagType, typeHash, fmt.Sprintf("The type to be used when querying tx, can be one of \"%s\", \"%s\", \"%s\"", typeHash, typeAccSeq, typeSig)) return cmd } diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index fe519197e408..c2ae6ec055a7 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -354,29 +354,19 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { true, "unknown --type value foo", }, { - "--type=sequence with no sequence", + "--type=acc_seq with no addr+seq", []string{ - "--type=sequence", + "--type=acc_seq", "", fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - true, "argument should be a sequence", - }, - { - "--type=sequence with no address", - []string{ - "--type=sequence", - fmt.Sprintf("%d", protoTx.AuthInfo.SignerInfos[0].Sequence), - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - }, - true, "--address is required when using --type=sequence", + true, "argument should be a combination", }, { "non-existing addr+seq combo", []string{ - "--type=sequence", + "--type=acc_seq", "42", - fmt.Sprintf("--address=%s", val.Address.String()), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, true, "found no txs matching given address and sequence combination", @@ -384,9 +374,8 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { { "addr+seq happy case", []string{ - "--type=sequence", - fmt.Sprintf("%d", protoTx.AuthInfo.SignerInfos[0].Sequence), - fmt.Sprintf("--address=%s", val.Address.String()), + "--type=acc_seq", + fmt.Sprintf("%s%d", val.Address, protoTx.AuthInfo.SignerInfos[0].Sequence), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, false, "", From 1d0c8c59996fb0257a5142bf53d0423acde5838b Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Mon, 26 Jul 2021 12:24:17 +0200 Subject: [PATCH 14/19] Use '/' delimiter --- CHANGELOG.md | 2 +- x/auth/ante/sigverify.go | 4 ++-- x/auth/client/cli/query.go | 4 ++-- x/auth/client/testutil/suite.go | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aea87c1e556a..a0c21ad37ec7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9533](https://github.com/cosmos/cosmos-sdk/pull/9533) Added a new gRPC method, `DenomOwners`, in `x/bank` to query for all account holders of a specific denomination. * (bank) [\#9618](https://github.com/cosmos/cosmos-sdk/pull/9618) Update bank.Metadata: add URI and URIHash attributes. -* [\#9750](https://github.com/cosmos/cosmos-sdk/pull/9750) Emit events for tx signature and sequence, so clients can now query txs by signature (`tx.signature=''`) or by address and sequence combo (`tx.sequence='' AND message.sender=''`). +* [\#9750](https://github.com/cosmos/cosmos-sdk/pull/9750) Emit events for tx signature and sequence, so clients can now query txs by signature (`tx.signature=''`) or by address and sequence combo (`tx.acc_seq='/'`). ### API Breaking Changes diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index 7a4500066355..e7af78a5bd62 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -98,7 +98,7 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b // Also emit the following events, so that txs can be indexed by these // indices: // - signature (via `tx.signature=''`), - // - address and sequence (via `message.sender= AND tx.sequence='' `). + // - concat(address,"/",sequence) (via `tx.acc_seq='cosmos1abc...def/42'`). sigs, err := sigTx.GetSignaturesV2() if err != nil { return ctx, err @@ -107,7 +107,7 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b var events sdk.Events for i, sig := range sigs { events = append(events, sdk.NewEvent(sdk.EventTypeTx, - sdk.NewAttribute(sdk.AttributeKeyAccountSequence, fmt.Sprintf("%s%d", signers[i], sig.Sequence)), + sdk.NewAttribute(sdk.AttributeKeyAccountSequence, fmt.Sprintf("%s/%d", signers[i], sig.Sequence)), )) sigBzs, err := signatureDataToBz(sig.Data) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index d4ad5ad44309..2d0dadeb476e 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -220,7 +220,7 @@ func QueryTxCmd() *cobra.Command { Long: strings.TrimSpace(fmt.Sprintf(` Example: $ %s query tx -$ %s query tx --%s=%s +$ %s query tx --%s=%s : $ %s query tx --%s=%s `, version.AppName, version.AppName, flagType, typeAccSeq, version.AppName, flagType, typeSig)), Args: cobra.ExactArgs(1), @@ -280,7 +280,7 @@ $ %s query tx --%s=%s case typeAccSeq: { if args[0] == "" { - return fmt.Errorf("argument should be a combination") + return fmt.Errorf("`acc_seq` type takes an argument '/'") } tmEvents := []string{ diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index c2ae6ec055a7..e1f903952af6 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -360,13 +360,13 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { "", fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - true, "argument should be a combination", + true, "`acc_seq` type takes an argument '/'", }, { "non-existing addr+seq combo", []string{ "--type=acc_seq", - "42", + "foobar", fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, true, "found no txs matching given address and sequence combination", @@ -375,7 +375,7 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { "addr+seq happy case", []string{ "--type=acc_seq", - fmt.Sprintf("%s%d", val.Address, protoTx.AuthInfo.SignerInfos[0].Sequence), + fmt.Sprintf("%s/%d", val.Address, protoTx.AuthInfo.SignerInfos[0].Sequence), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, false, "", From fade9f7717d1270c808cf70cace900c43e9c0157 Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaurym@users.noreply.github.com> Date: Tue, 27 Jul 2021 14:54:37 +0200 Subject: [PATCH 15/19] Update x/auth/client/cli/query.go Co-authored-by: Robert Zaremba --- x/auth/client/cli/query.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 2d0dadeb476e..706aa5eb32cc 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -222,7 +222,9 @@ Example: $ %s query tx $ %s query tx --%s=%s : $ %s query tx --%s=%s -`, version.AppName, version.AppName, flagType, typeAccSeq, version.AppName, flagType, typeSig)), +`, version.AppName, +version.AppName, flagType, typeAccSeq, +version.AppName, flagType, typeSig)), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) From 10f846e330aa65c7d0ff6f39ee192cf19ff95ec5 Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaurym@users.noreply.github.com> Date: Tue, 27 Jul 2021 14:54:56 +0200 Subject: [PATCH 16/19] Update x/auth/client/cli/query.go Co-authored-by: Robert Zaremba --- x/auth/client/cli/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 706aa5eb32cc..d7cae7a1bdc9 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -274,7 +274,7 @@ version.AppName, flagType, typeSig)), } if len(txs.Txs) > 1 { // This case means there's a bug somewhere else in the code. Should not happen. - return fmt.Errorf("found %d txs matching given signatures", len(txs.Txs)) + return errors.ErrLogic.Wrapf("found %d txs matching given signatures", len(txs.Txs)) } return clientCtx.PrintProto(txs.Txs[0]) From 2e9a636c61a92ff41b40a87f6c8d87c661476600 Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaurym@users.noreply.github.com> Date: Tue, 27 Jul 2021 14:55:10 +0200 Subject: [PATCH 17/19] Update x/auth/client/cli/query.go Co-authored-by: Robert Zaremba --- x/auth/client/cli/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index d7cae7a1bdc9..044d728480f8 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -255,7 +255,7 @@ version.AppName, flagType, typeSig)), } case typeSig: { - if args[0] == "" { + if len(args) != 1 || args[0] == "" { return fmt.Errorf("argument should be comma-separated signatures") } From 4e19f3d02f15febdcc4a1b1808fe3a5abc45d2d1 Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaurym@users.noreply.github.com> Date: Tue, 27 Jul 2021 14:55:18 +0200 Subject: [PATCH 18/19] Update x/auth/client/cli/query.go Co-authored-by: Robert Zaremba --- x/auth/client/cli/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 044d728480f8..b02e11b481ca 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -215,7 +215,7 @@ $ %s query txs --%s 'message.sender=cosmos1...&message.action=withdraw_delegator // QueryTxCmd implements the default command for a tx query. func QueryTxCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "tx [hash|acc_seq|signature] --type=[hash|acc_seq|signature]", + Use: "tx --type=[hash|acc_seq|signature] [hash|acc_seq|signature]", Short: "Query for a transaction by hash, addr++seq combination or signature in a committed block", Long: strings.TrimSpace(fmt.Sprintf(` Example: From 91314fefd9a28b36fd85e143cd20d04953afba85 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Tue, 27 Jul 2021 15:12:13 +0200 Subject: [PATCH 19/19] Address review comments --- x/auth/ante/sigverify.go | 2 +- x/auth/client/cli/query.go | 24 +++++++++++++++++------- x/auth/client/cli/query_test.go | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 x/auth/client/cli/query_test.go diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index e7af78a5bd62..638e837aa3f4 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -512,6 +512,6 @@ func signatureDataToBz(data signing.SignatureData) ([][]byte, error) { return sigs, nil default: - return nil, fmt.Errorf("unexpected signature data type %T", data) + return nil, sdkerrors.ErrInvalidType.Wrapf("unexpected signature data type %T", data) } } diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index b02e11b481ca..ce4f6e95921d 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" "github.com/cosmos/cosmos-sdk/version" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" @@ -222,9 +223,10 @@ Example: $ %s query tx $ %s query tx --%s=%s : $ %s query tx --%s=%s -`, version.AppName, -version.AppName, flagType, typeAccSeq, -version.AppName, flagType, typeSig)), +`, + version.AppName, + version.AppName, flagType, typeAccSeq, + version.AppName, flagType, typeSig)), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) @@ -255,11 +257,10 @@ version.AppName, flagType, typeSig)), } case typeSig: { - if len(args) != 1 || args[0] == "" { - return fmt.Errorf("argument should be comma-separated signatures") + sigParts, err := parseSigArgs(args) + if err != nil { + return err } - - sigParts := strings.Split(args[0], ",") tmEvents := make([]string, len(sigParts)) for i, sig := range sigParts { tmEvents[i] = fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, sig) @@ -313,3 +314,12 @@ version.AppName, flagType, typeSig)), return cmd } + +// parseSigArgs parses comma-separated signatures from the CLI arguments. +func parseSigArgs(args []string) ([]string, error) { + if len(args) != 1 || args[0] == "" { + return nil, fmt.Errorf("argument should be comma-separated signatures") + } + + return strings.Split(args[0], ","), nil +} diff --git a/x/auth/client/cli/query_test.go b/x/auth/client/cli/query_test.go new file mode 100644 index 000000000000..0168d3008179 --- /dev/null +++ b/x/auth/client/cli/query_test.go @@ -0,0 +1,32 @@ +package cli + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseSigs(t *testing.T) { + cases := []struct { + name string + args []string + expErr bool + expNumSigs int + }{ + {"no args", []string{}, true, 0}, + {"empty args", []string{""}, true, 0}, + {"too many args", []string{"foo", "bar"}, true, 0}, + {"1 sig", []string{"foo"}, false, 1}, + {"3 sigs", []string{"foo,bar,baz"}, false, 3}, + } + + for _, tc := range cases { + sigs, err := parseSigArgs(tc.args) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expNumSigs, len(sigs)) + } + } +}