From a527e05a3eba27be9ae21c81f55bc37f5091dfcf Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 4 Oct 2023 14:06:32 +0200 Subject: [PATCH 1/3] feat(client/v2): signing --- CHANGELOG.md | 4 +- client/flags/flags.go | 4 +- client/tx/factory.go | 7 +- client/tx/tx.go | 2 +- client/v2/Makefile | 2 +- client/v2/README.md | 25 +++- client/v2/autocli/app.go | 33 ++--- client/v2/autocli/builder.go | 49 ++++---- client/v2/autocli/common.go | 34 ++++- client/v2/autocli/common_test.go | 29 +++-- client/v2/autocli/flag/address.go | 11 +- client/v2/autocli/flag/builder.go | 119 +++++++++++++----- client/v2/autocli/flag/messager_binder.go | 26 ++-- client/v2/autocli/flags.go | 3 - client/v2/autocli/keyring/interface.go | 17 +++ client/v2/autocli/keyring/no_keyring.go | 24 +++- client/v2/autocli/msg.go | 59 +++++---- client/v2/autocli/query.go | 10 +- .../testdata/help-deprecated-msg.golden | 1 - .../v2/autocli/testdata/help-echo-msg.golden | 1 - client/v2/go.mod | 14 ++- client/v2/go.sum | 52 ++++++-- client/v2/internal/flags/flags.go | 19 +++ codec/bench_test.go | 14 ++- codec/types/any.go | 26 +++- codec/types/interface_registry.go | 2 +- codec/types/util.go | 15 +++ crypto/keyring/autocli.go | 86 +++++++++++++ crypto/keyring/keyring.go | 18 --- simapp/simd/cmd/root.go | 5 +- simapp/simd/cmd/root_v2.go | 19 ++- types/tx_msg.go | 9 +- x/auth/signing/sig_verifiable_tx.go | 2 +- x/bank/types/msgs.go | 6 - 34 files changed, 542 insertions(+), 205 deletions(-) delete mode 100644 client/v2/autocli/flags.go create mode 100644 client/v2/internal/flags/flags.go create mode 100644 codec/types/util.go create mode 100644 crypto/keyring/autocli.go diff --git a/CHANGELOG.md b/CHANGELOG.md index c322f4c07df..72dc0420308 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements +* (keyring) [#17913](https://github.com/cosmos/cosmos-sdk/pull/17913) Add `NewAutoCLIKeyring` for creating an AutoCLI keyring from a SDK keyring. +* (codec) [#17913](https://github.com/cosmos/cosmos-sdk/pull/17913) `codectypes.NewAnyWithValue` supports proto v2 messages. * (client) [#17503](https://github.com/cosmos/cosmos-sdk/pull/17503) Add `client.Context{}.WithAddressCodec`, `WithValidatorAddressCodec`, `WithConsensusAddressCodec` to provide address codecs to the client context. See the [UPGRADING.md](./UPGRADING.md) for more details. * (crypto/keyring) [#17503](https://github.com/cosmos/cosmos-sdk/pull/17503) Simplify keyring interfaces to use `[]byte` instead of `sdk.Address` for addresses. * (all) [#16537](https://github.com/cosmos/cosmos-sdk/pull/16537) Properly propagated `fmt.Errorf` errors and using `errors.New` where appropriate. @@ -65,7 +67,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/distribution) [#17657](https://github.com/cosmos/cosmos-sdk/pull/17657) The `FundCommunityPool` and `DistributeFromFeePool` keeper methods are now removed from x/distribution. * (x/distribution) [#17657](https://github.com/cosmos/cosmos-sdk/pull/17657) The distribution module keeper now takes a new argument `PoolKeeper` in addition. * (app) [#17838](https://github.com/cosmos/cosmos-sdk/pull/17838) Params module was removed from simapp and all imports of the params module removed throughout the repo. - * The Cosmos SDK has migrated aay from using params, if you're app still uses it, then you can leave it plugged into your app + * The Cosmos SDK has migrated away from using params, if your app still uses it, then you can leave it plugged into your app * (x/staking) [#17778](https://github.com/cosmos/cosmos-sdk/pull/17778) Use collections for `Params` * remove from `Keeper`: `GetParams`, `SetParams` * (types/simulation) [#17737](https://github.com/cosmos/cosmos-sdk/pull/17737) Remove unused parameter from `RandomFees` diff --git a/client/flags/flags.go b/client/flags/flags.go index e58cabca638..06458e2d441 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -117,7 +117,9 @@ func AddQueryFlagsToCmd(cmd *cobra.Command) { func AddTxFlagsToCmd(cmd *cobra.Command) { f := cmd.Flags() f.StringP(FlagOutput, "o", OutputFormatJSON, "Output format (text|json)") - f.String(FlagFrom, "", "Name or address of private key with which to sign") + if cmd.Flag(FlagFrom) == nil { // avoid flag redefinition when it's already been added by AutoCLI + f.String(FlagFrom, "", "Name or address of private key with which to sign") + } f.Uint64P(FlagAccountNumber, "a", 0, "The account number of the signing account (offline mode only)") f.Uint64P(FlagSequence, "s", 0, "The sequence number of the signing account (offline mode only)") f.String(FlagNote, "", "Note to add a description to the transaction (previously --memo)") diff --git a/client/tx/factory.go b/client/tx/factory.go index f21d861f3bb..81ce5784d5a 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -419,7 +419,12 @@ func (f Factory) BuildSimTx(msgs ...sdk.Msg) ([]byte, error) { return nil, err } - return f.txConfig.TxEncoder()(txb.GetTx()) + encoder := f.txConfig.TxEncoder() + if encoder == nil { + return nil, fmt.Errorf("cannot simulate tx: tx encoder is nil") + } + + return encoder(txb.GetTx()) } // getSimPK gets the public key to use for building a simulation tx. diff --git a/client/tx/tx.go b/client/tx/tx.go index b7c66536235..8d905922cad 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -107,7 +107,7 @@ func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { txBytes, err := encoder(tx.GetTx()) if err != nil { - return err + return fmt.Errorf("failed to encode transaction: %w", err) } if err := clientCtx.PrintRaw(json.RawMessage(txBytes)); err != nil { diff --git a/client/v2/Makefile b/client/v2/Makefile index 6868d1941a3..1b4bb0cbe7f 100644 --- a/client/v2/Makefile +++ b/client/v2/Makefile @@ -1,2 +1,2 @@ codegen: - @(cd internal; buf generate) + @(cd internal; buf generate) \ No newline at end of file diff --git a/client/v2/README.md b/client/v2/README.md index 516c7f49569..e5458ea9a9a 100644 --- a/client/v2/README.md +++ b/client/v2/README.md @@ -75,7 +75,7 @@ if err := rootCmd.Execute(); err != nil { ### Keyring -`autocli` supports a keyring for key name resolving and signing transactions. Providing a keyring is optional, but if you want to use the `autocli` generated commands to sign transactions, you must provide a keyring. +`autocli` uses a keyring for key name resolving and signing transactions. Providing a keyring is optional, but if you want to use the `autocli` generated commands to sign transactions, you must provide a keyring. :::tip This provides a better UX as it allows to resolve key names directly from the keyring in all transactions and commands. @@ -87,16 +87,23 @@ This provides a better UX as it allows to resolve key names directly from the ke ::: -The keyring to be provided to `client/v2` must match the `client/v2` keyring interface. The Cosmos SDK keyring and Hubl keyring both implement this interface. +The keyring to be provided to `client/v2` must match the `client/v2` keyring interface. The keyring should be provided in the `appOptions` struct as follows, and can be gotten from the client context: +:::tip +The Cosmos SDK keyring and Hubl keyring both implement the `client/v2/autocli/keyring` interface, thanks to the following wrapper: + +```go +keyring.NewAutoCLIKeyring(kb) +``` + +::: + :::warning When using AutoCLI the keyring will only be created once and before any command flag parsing. ::: ```go -// Get the keyring from the client context -keyring := ctx.Keyring // Set the keyring in the appOptions appOptions.Keyring = keyring @@ -104,6 +111,16 @@ err := autoCliOpts.EnhanceRootCommand(rootCmd) ... ``` +## Signing + +`autocli` supports signing transactions with the keyring. +The [`cosmos.msg.v1.signer` protobuf annotation](https://github.com/cosmos/cosmos-sdk/blob/9dd34510e27376005e7e7ff3628eab9dbc8ad6dc/docs/build/building-modules/05-protobuf-annotations.md#L9) defines the signer field of the message. +This field is automatically filled when using the `--from` flag or defining the signer as a positional argument. + +:::warning +AutoCLI currently supports only one signer per transaction. +::: + ## Module Wiring & Customization The `AutoCLIOptions()` method on your module allows to specify custom commands, sub-commands or flags for each service, as it was a `cobra.Command` instance, within the `RpcCommandOptions` struct. Defining such options will customize the behavior of the `autocli` command generation, which by default generates a command for each method in your gRPC service. diff --git a/client/v2/autocli/app.go b/client/v2/autocli/app.go index 5133d822c66..052b660e157 100644 --- a/client/v2/autocli/app.go +++ b/client/v2/autocli/app.go @@ -11,10 +11,10 @@ import ( "cosmossdk.io/client/v2/autocli/keyring" "cosmossdk.io/core/appmodule" "cosmossdk.io/depinject" - "cosmossdk.io/log" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/x/auth/tx" ) // AppOptions are autocli options for an app. These options can be built via depinject based on an app config. Ex: @@ -28,9 +28,6 @@ import ( type AppOptions struct { depinject.In - // Logger is the logger to use for client/v2. - Logger log.Logger - // Modules are the AppModule implementations for the modules in the app. Modules map[string]appmodule.AppModule @@ -40,11 +37,14 @@ type AppOptions struct { // module or need to be improved. ModuleOptions map[string]*autocliv1.ModuleOptions `optional:"true"` - // ClientCtx contains the necessary information needed to execute the commands. - ClientCtx *client.Context - // Keyring is the keyring to use for client/v2. Keyring keyring.Keyring `optional:"true"` + + // ClientCtx contains the necessary information needed to execute the commands. + ClientCtx client.Context + + // TxConfigOptions are the transactions config options. + TxConfigOpts tx.ConfigOptions } // EnhanceRootCommand enhances the provided root command with autocli AppOptions, @@ -64,18 +64,21 @@ type AppOptions struct { // err = autoCliOpts.EnhanceRootCommand(rootCmd) func (appOptions AppOptions) EnhanceRootCommand(rootCmd *cobra.Command) error { builder := &Builder{ - Logger: appOptions.Logger, Builder: flag.Builder{ - TypeResolver: protoregistry.GlobalTypes, - FileResolver: proto.HybridResolver, - ClientCtx: appOptions.ClientCtx, - Keyring: appOptions.Keyring, + TypeResolver: protoregistry.GlobalTypes, + FileResolver: proto.HybridResolver, + Keyring: appOptions.Keyring, + AddressCodec: appOptions.ClientCtx.AddressCodec, + ValidatorAddressCodec: appOptions.ClientCtx.ValidatorAddressCodec, + ConsensusAddressCodec: appOptions.ClientCtx.ConsensusAddressCodec, }, + ClientCtx: appOptions.ClientCtx, + TxConfigOpts: appOptions.TxConfigOpts, GetClientConn: func(cmd *cobra.Command) (grpc.ClientConnInterface, error) { return client.GetClientQueryContext(cmd) }, - AddQueryConnFlags: flags.AddQueryFlagsToCmd, - AddTxConnFlags: flags.AddTxFlagsToCmd, + AddQueryConnFlags: sdkflags.AddQueryFlagsToCmd, + AddTxConnFlags: sdkflags.AddTxFlagsToCmd, } return appOptions.EnhanceRootCommandWithBuilder(rootCmd, builder) diff --git a/client/v2/autocli/builder.go b/client/v2/autocli/builder.go index ef2299d3278..f92147df324 100644 --- a/client/v2/autocli/builder.go +++ b/client/v2/autocli/builder.go @@ -8,7 +8,9 @@ import ( "cosmossdk.io/client/v2/autocli/flag" "cosmossdk.io/client/v2/autocli/keyring" - "cosmossdk.io/log" + + "github.com/cosmos/cosmos-sdk/client" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" ) // Builder manages options for building CLI commands. @@ -16,13 +18,16 @@ type Builder struct { // flag.Builder embeds the flag builder and its options. flag.Builder - // Logger is the logger used by the builder. - Logger log.Logger - // GetClientConn specifies how CLI commands will resolve a grpc.ClientConnInterface // from a given context. GetClientConn func(*cobra.Command) (grpc.ClientConnInterface, error) + // ClientCtx contains the necessary information needed to execute the commands. + ClientCtx client.Context + + // TxConfigOptions is required to support sign mode textual + TxConfigOpts authtx.ConfigOptions + // AddQueryConnFlags and AddTxConnFlags are functions that add flags to query and transaction commands AddQueryConnFlags func(*cobra.Command) AddTxConnFlags func(*cobra.Command) @@ -33,40 +38,28 @@ type Builder struct { // If the Logger is nil, it will be set to a nop logger. // If the keyring is nil, it will be set to a no keyring. func (b *Builder) ValidateAndComplete() error { - if b.Logger == nil { - b.Logger = log.NewNopLogger() - } - - if b.ClientCtx == nil { - return errors.New("client context is required in builder") - } - - if b.ClientCtx.AddressCodec == nil { - return errors.New("address codec is required in builder") + if b.Builder.AddressCodec == nil { + return errors.New("address codec is required in flag builder") } - if b.ClientCtx.ValidatorAddressCodec == nil { - return errors.New("validator address codec is required in builder") + if b.Builder.ValidatorAddressCodec == nil { + return errors.New("validator address codec is required in flag builder") } - if b.ClientCtx.ConsensusAddressCodec == nil { - return errors.New("consensus address codec is required in builder") + if b.Builder.ConsensusAddressCodec == nil { + return errors.New("consensus address codec is required in flag builder") } - if b.Keyring == nil { - if b.ClientCtx.Keyring != nil { - b.Keyring = b.ClientCtx.Keyring - } else { - b.Keyring = keyring.NoKeyring{} - } + if b.Builder.Keyring == nil { + b.Keyring = keyring.NoKeyring{} } - if b.TypeResolver == nil { - return errors.New("type resolver is required in builder") + if b.Builder.TypeResolver == nil { + return errors.New("type resolver is required in flag builder") } - if b.FileResolver == nil { - return errors.New("file resolver is required in builder") + if b.Builder.FileResolver == nil { + return errors.New("file resolver is required in flag builder") } return nil diff --git a/client/v2/autocli/common.go b/client/v2/autocli/common.go index 480faacd1eb..f6aa375e085 100644 --- a/client/v2/autocli/common.go +++ b/client/v2/autocli/common.go @@ -10,9 +10,8 @@ import ( "sigs.k8s.io/yaml" autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + "cosmossdk.io/client/v2/internal/flags" "cosmossdk.io/client/v2/internal/util" - - "github.com/cosmos/cosmos-sdk/client/flags" ) type cmdType int @@ -67,6 +66,37 @@ func (b *Builder) buildMethodCommandCommon(descriptor protoreflect.MethodDescrip return err } + // signer related logic, triggers only when there is a signer defined + if binder.SignerInfo.FieldName != "" { + // mark the signer flag as required if defined + // TODO(@julienrbrt): UX improvement by only marking the flag as required when there is more than one key in the keyring; + // when there is only one key, use that key by default. + if binder.SignerInfo.IsFlag { + if err := cmd.MarkFlagRequired(binder.SignerInfo.FieldName); err != nil { + return err + } + + // the client context uses the from flag to determine the signer. + // this sets the signer flags to the from flag value if a custom signer flag is set. + if binder.SignerInfo.FieldName != flags.FlagFrom { + signer, err := cmd.Flags().GetString(binder.SignerInfo.FieldName) + if err != nil { + return fmt.Errorf("failed to get signer flag: %w", err) + } + + if err := cmd.Flags().Set(flags.FlagFrom, signer); err != nil { + return err + } + } + } else { + // if the signer is not a flag, it is a positional argument + // we need to get the correct positional arguments + if err := cmd.Flags().Set(flags.FlagFrom, args[binder.SignerInfo.PositionalArgIndex]); err != nil { + return err + } + } + } + return exec(cmd, input) } diff --git a/client/v2/autocli/common_test.go b/client/v2/autocli/common_test.go index 5c4e0f322e7..5926941d8be 100644 --- a/client/v2/autocli/common_test.go +++ b/client/v2/autocli/common_test.go @@ -19,7 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" addresscodec "github.com/cosmos/cosmos-sdk/codec/address" - "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdkkeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" ) @@ -47,10 +47,16 @@ func initFixture(t *testing.T) *fixture { clientConn, err := grpc.Dial(listener.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) assert.NilError(t, err) - appCodec := moduletestutil.MakeTestEncodingConfig().Codec - kr, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendMemory, home, nil, appCodec) + encodingConfig := moduletestutil.MakeTestEncodingConfig() + kr, err := sdkkeyring.New(sdk.KeyringServiceName(), sdkkeyring.BackendMemory, home, nil, encodingConfig.Codec) assert.NilError(t, err) + akr, err := sdkkeyring.NewAutoCLIKeyring(kr) + assert.NilError(t, err) + + interfaceRegistry := encodingConfig.Codec.InterfaceRegistry() + interfaceRegistry.RegisterInterface(sdk.MsgTypeURL(&testpb.MsgRequest{}), (*sdk.Msg)(nil), &testpb.MsgRequest{}) + var initClientCtx client.Context initClientCtx = initClientCtx. WithAddressCodec(addresscodec.NewBech32Codec("cosmos")). @@ -59,21 +65,28 @@ func initFixture(t *testing.T) *fixture { WithKeyring(kr). WithKeyringDir(home). WithHomeDir(home). - WithViper("") + WithViper(""). + WithInterfaceRegistry(interfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithAccountRetriever(client.MockAccountRetriever{}). + WithChainID("autocli-test") conn := &testClientConn{ClientConn: clientConn} b := &Builder{ Builder: flag.Builder{ - TypeResolver: protoregistry.GlobalTypes, - FileResolver: protoregistry.GlobalFiles, - ClientCtx: &initClientCtx, - Keyring: kr, + TypeResolver: protoregistry.GlobalTypes, + FileResolver: protoregistry.GlobalFiles, + AddressCodec: initClientCtx.AddressCodec, + ValidatorAddressCodec: initClientCtx.ValidatorAddressCodec, + ConsensusAddressCodec: initClientCtx.ConsensusAddressCodec, + Keyring: akr, }, GetClientConn: func(*cobra.Command) (grpc.ClientConnInterface, error) { return conn, nil }, AddQueryConnFlags: flags.AddQueryFlagsToCmd, AddTxConnFlags: flags.AddTxFlagsToCmd, + ClientCtx: initClientCtx, } assert.NilError(t, b.ValidateAndComplete()) diff --git a/client/v2/autocli/flag/address.go b/client/v2/autocli/flag/address.go index 4d61e0f9dd2..ddc7e6bf2c0 100644 --- a/client/v2/autocli/flag/address.go +++ b/client/v2/autocli/flag/address.go @@ -19,7 +19,7 @@ import ( type addressStringType struct{} func (a addressStringType) NewValue(_ context.Context, b *Builder) Value { - return &addressValue{addressCodec: b.ClientCtx.AddressCodec, keyring: b.Keyring} + return &addressValue{addressCodec: b.AddressCodec, keyring: b.Keyring} } func (a addressStringType) DefaultValue() string { @@ -29,7 +29,7 @@ func (a addressStringType) DefaultValue() string { type validatorAddressStringType struct{} func (a validatorAddressStringType) NewValue(_ context.Context, b *Builder) Value { - return &addressValue{addressCodec: b.ClientCtx.ValidatorAddressCodec, keyring: b.Keyring} + return &addressValue{addressCodec: b.ValidatorAddressCodec, keyring: b.Keyring} } func (a validatorAddressStringType) DefaultValue() string { @@ -80,7 +80,12 @@ func (a addressValue) Type() string { type consensusAddressStringType struct{} func (a consensusAddressStringType) NewValue(ctx context.Context, b *Builder) Value { - return &consensusAddressValue{addressValue: addressValue{addressCodec: b.ClientCtx.ConsensusAddressCodec, keyring: b.Keyring}} + return &consensusAddressValue{ + addressValue: addressValue{ + addressCodec: b.ConsensusAddressCodec, + keyring: b.Keyring, + }, + } } func (a consensusAddressStringType) DefaultValue() string { diff --git a/client/v2/autocli/flag/builder.go b/client/v2/autocli/flag/builder.go index a97906029f9..f0accc2e097 100644 --- a/client/v2/autocli/flag/builder.go +++ b/client/v2/autocli/flag/builder.go @@ -14,10 +14,13 @@ import ( "google.golang.org/protobuf/reflect/protoregistry" autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + msgv1 "cosmossdk.io/api/cosmos/msg/v1" "cosmossdk.io/client/v2/autocli/keyring" + "cosmossdk.io/client/v2/internal/flags" "cosmossdk.io/client/v2/internal/util" + "cosmossdk.io/core/address" - "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/runtime" ) // Builder manages options for building pflag flags for protobuf messages. @@ -39,11 +42,13 @@ type Builder struct { messageFlagTypes map[protoreflect.FullName]Type scalarFlagTypes map[string]Type - // Keyring implementation + // Keyring is the keyring to use for client/v2. Keyring keyring.Keyring - // ClientCtx contains the necessary information needed to execute the commands. - ClientCtx *client.Context + // Address Codecs are the address codecs to use for client/v2. + AddressCodec address.Codec + ValidatorAddressCodec runtime.ValidatorAddressCodec + ConsensusAddressCodec runtime.ConsensusAddressCodec } func (b *Builder) init() { @@ -62,35 +67,48 @@ func (b *Builder) init() { } } +// DefineMessageFlagType allows to extend custom protobuf message type handling for flags (and positional arguments). func (b *Builder) DefineMessageFlagType(messageName protoreflect.FullName, flagType Type) { b.init() b.messageFlagTypes[messageName] = flagType } +// DefineScalarFlagType allows to extend custom scalar type handling for flags (and positional arguments). func (b *Builder) DefineScalarFlagType(scalarName string, flagType Type) { b.init() b.scalarFlagTypes[scalarName] = flagType } +// AddMessageFlags adds flags for each field in the message to the flag set. func (b *Builder) AddMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, messageType protoreflect.MessageType, commandOptions *autocliv1.RpcCommandOptions) (*MessageBinder, error) { return b.addMessageFlags(ctx, flagSet, messageType, commandOptions, namingOptions{}) } -// AddMessageFlags adds flags for each field in the message to the flag set. +// addMessageFlags adds flags for each field in the message to the flag set. func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, messageType protoreflect.MessageType, commandOptions *autocliv1.RpcCommandOptions, options namingOptions) (*MessageBinder, error) { - fields := messageType.Descriptor().Fields() - numFields := fields.Len() - handler := &MessageBinder{ + messageBinder := &MessageBinder{ messageType: messageType, + // positional args are also parsed using a FlagSet so that we can reuse all the same parsers + positionalFlagSet: pflag.NewFlagSet("positional", pflag.ContinueOnError), } + fields := messageType.Descriptor().Fields() + signerFieldName := getSignerFieldName(messageType.Descriptor()) + isPositional := map[string]bool{} - n := len(commandOptions.PositionalArgs) - // positional args are also parsed using a FlagSet so that we can reuse all the same parsers - handler.positionalFlagSet = pflag.NewFlagSet("positional", pflag.ContinueOnError) + + lengthPositionalArgsOptions := len(commandOptions.PositionalArgs) for i, arg := range commandOptions.PositionalArgs { isPositional[arg.ProtoField] = true + // verify if a positional field is a signer field + if arg.ProtoField == signerFieldName { + messageBinder.SignerInfo = SignerInfo{ + PositionalArgIndex: i, + FieldName: arg.ProtoField, + } + } + field := fields.ByName(protoreflect.Name(arg.ProtoField)) if field == nil { return nil, fmt.Errorf("can't find field %s on %s", arg.ProtoField, messageType.Descriptor().FullName()) @@ -101,24 +119,24 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m } if arg.Varargs { - if i != n-1 { + if i != lengthPositionalArgsOptions-1 { return nil, fmt.Errorf("varargs positional argument %s must be the last argument", arg.ProtoField) } - handler.hasVarargs = true + messageBinder.hasVarargs = true } if arg.Optional { - if i != n-1 { + if i != lengthPositionalArgsOptions-1 { return nil, fmt.Errorf("optional positional argument %s must be the last argument", arg.ProtoField) } - handler.hasOptional = true + messageBinder.hasOptional = true } _, hasValue, err := b.addFieldFlag( ctx, - handler.positionalFlagSet, + messageBinder.positionalFlagSet, field, &autocliv1.FlagOptions{Name: fmt.Sprintf("%d", i)}, namingOptions{}, @@ -127,21 +145,21 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m return nil, err } - handler.positionalArgs = append(handler.positionalArgs, fieldBinding{ + messageBinder.positionalArgs = append(messageBinder.positionalArgs, fieldBinding{ field: field, hasValue: hasValue, }) } - if handler.hasVarargs { - handler.CobraArgs = cobra.MinimumNArgs(n - 1) - handler.MandatoryArgUntil = n - 1 - } else if handler.hasOptional { - handler.CobraArgs = cobra.RangeArgs(n-1, n) - handler.MandatoryArgUntil = n - 1 + if messageBinder.hasVarargs { + messageBinder.CobraArgs = cobra.MinimumNArgs(lengthPositionalArgsOptions - 1) + messageBinder.mandatoryArgUntil = lengthPositionalArgsOptions - 1 + } else if messageBinder.hasOptional { + messageBinder.CobraArgs = cobra.RangeArgs(lengthPositionalArgsOptions-1, lengthPositionalArgsOptions) + messageBinder.mandatoryArgUntil = lengthPositionalArgsOptions - 1 } else { - handler.CobraArgs = cobra.ExactArgs(n) - handler.MandatoryArgUntil = n + messageBinder.CobraArgs = cobra.ExactArgs(lengthPositionalArgsOptions) + messageBinder.mandatoryArgUntil = lengthPositionalArgsOptions } // validate flag options @@ -149,12 +167,41 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m if fields.ByName(protoreflect.Name(name)) == nil { return nil, fmt.Errorf("can't find field %s on %s specified as a flag", name, messageType.Descriptor().FullName()) } + + // verify if a flag is a signer field + if name == signerFieldName { + messageBinder.SignerInfo = SignerInfo{ + FieldName: name, + IsFlag: false, + } + } } + // if signer has not been specified as positional arguments, + // add it as `--from` flag (instead of --field-name flags) + if signerFieldName != "" && messageBinder.SignerInfo.FieldName == "" { + if commandOptions.FlagOptions == nil { + commandOptions.FlagOptions = make(map[string]*autocliv1.FlagOptions) + } + + commandOptions.FlagOptions[signerFieldName] = &autocliv1.FlagOptions{ + Name: flags.FlagFrom, + Usage: "Name or address with which to sign the message", + Shorthand: "f", + } + + messageBinder.SignerInfo = SignerInfo{ + FieldName: flags.FlagFrom, + IsFlag: true, + } + } + + // define all other fields as flags flagOptsByFlagName := map[string]*autocliv1.FlagOptions{} - for i := 0; i < numFields; i++ { + for i := 0; i < fields.Len(); i++ { field := fields.Get(i) - if isPositional[string(field.Name())] { + // skips positional args and signer field + if isPositional[string(field.Name())] || string(field.Name()) == signerFieldName { continue } @@ -165,7 +212,7 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m return nil, err } - handler.flagBindings = append(handler.flagBindings, fieldBinding{ + messageBinder.flagBindings = append(messageBinder.flagBindings, fieldBinding{ hasValue: hasValue, field: field, }) @@ -184,7 +231,7 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m } }) - return handler, nil + return messageBinder, nil } // bindPageRequest create a flag for pagination @@ -259,10 +306,11 @@ func (b *Builder) addFieldFlag(ctx context.Context, flagSet *pflag.FlagSet, fiel // This is a bit of hacking around the pflag API, but the // defaultValue is set in this way because this is much easier than trying - // to parse the string into the types that StringSliceP, Int32P, etc. expect + // to parse the string into the types that StringSliceP, Int32P, etc. if defaultValue != "" { err = flagSet.Set(name, defaultValue) } + return name, val, err } @@ -363,3 +411,14 @@ func (b *Builder) resolveFlagTypeBasic(field protoreflect.FieldDescriptor) Type return nil } } + +// getSignerFieldName gets signer field name of a message. +// AutoCLI supports only one signer field per message. +func getSignerFieldName(descriptor protoreflect.MessageDescriptor) string { + signersFields := proto.GetExtension(descriptor.Options(), msgv1.E_Signer).([]string) + if len(signersFields) == 0 { + return "" + } + + return signersFields[0] +} diff --git a/client/v2/autocli/flag/messager_binder.go b/client/v2/autocli/flag/messager_binder.go index 9053e90439b..ecd82239503 100644 --- a/client/v2/autocli/flag/messager_binder.go +++ b/client/v2/autocli/flag/messager_binder.go @@ -8,15 +8,26 @@ import ( "google.golang.org/protobuf/reflect/protoreflect" ) +// SignerInfo contains information about the signer field. +// That field is special because it needs to be known for signing. +// This struct keeps track of the field name and whether it is a flag. +// IsFlag and PositionalArgIndex are mutually exclusive. +type SignerInfo struct { + PositionalArgIndex int + IsFlag bool + FieldName string +} + // MessageBinder binds multiple flags in a flag set to a protobuf message. type MessageBinder struct { - MandatoryArgUntil int - CobraArgs cobra.PositionalArgs + CobraArgs cobra.PositionalArgs + SignerInfo SignerInfo positionalFlagSet *pflag.FlagSet positionalArgs []fieldBinding hasVarargs bool hasOptional bool + mandatoryArgUntil int flagBindings []fieldBinding messageType protoreflect.MessageType @@ -39,15 +50,14 @@ func (m MessageBinder) Bind(msg protoreflect.Message, positionalArgs []string) e } name := fmt.Sprintf("%d", i) - if i == m.MandatoryArgUntil && m.hasVarargs { + if i == m.mandatoryArgUntil && m.hasVarargs { for _, v := range positionalArgs[i:] { if err := m.positionalFlagSet.Set(name, v); err != nil { return err } } } else { - err := m.positionalFlagSet.Set(name, positionalArgs[i]) - if err != nil { + if err := m.positionalFlagSet.Set(name, positionalArgs[i]); err != nil { return err } } @@ -55,16 +65,14 @@ func (m MessageBinder) Bind(msg protoreflect.Message, positionalArgs []string) e // bind positional arg values to the message for _, arg := range m.positionalArgs { - err := arg.bind(msg) - if err != nil { + if err := arg.bind(msg); err != nil { return err } } // bind flag values to the message for _, binding := range m.flagBindings { - err := binding.bind(msg) - if err != nil { + if err := binding.bind(msg); err != nil { return err } } diff --git a/client/v2/autocli/flags.go b/client/v2/autocli/flags.go deleted file mode 100644 index e6e153fcc6f..00000000000 --- a/client/v2/autocli/flags.go +++ /dev/null @@ -1,3 +0,0 @@ -package autocli - -var flagNoIndent = "no-indent" diff --git a/client/v2/autocli/keyring/interface.go b/client/v2/autocli/keyring/interface.go index fee5f3434ad..fa448bd2059 100644 --- a/client/v2/autocli/keyring/interface.go +++ b/client/v2/autocli/keyring/interface.go @@ -1,6 +1,23 @@ package keyring +import ( + signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// Keyring is an interface used for signing transactions. +// It aims to be simplistic and easy to use. type Keyring interface { + // List returns the names of all keys stored in the keyring. + List() ([]string, error) + // LookupAddressByKeyName returns the address of the key with the given name. LookupAddressByKeyName(name string) ([]byte, error) + + // GetPubKey returns the public key of the key with the given name. + GetPubKey(name string) (cryptotypes.PubKey, error) + + // Sign signs the given bytes with the key with the given name. + Sign(name string, msg []byte, signMode signingv1beta1.SignMode) ([]byte, error) } diff --git a/client/v2/autocli/keyring/no_keyring.go b/client/v2/autocli/keyring/no_keyring.go index 916fff4bd45..e14267cee5e 100644 --- a/client/v2/autocli/keyring/no_keyring.go +++ b/client/v2/autocli/keyring/no_keyring.go @@ -1,11 +1,31 @@ package keyring -import "errors" +import ( + "errors" + + signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) var _ Keyring = NoKeyring{} +var errNoKeyring = errors.New("no keyring configured") + type NoKeyring struct{} +func (k NoKeyring) List() ([]string, error) { + return nil, errNoKeyring +} + func (k NoKeyring) LookupAddressByKeyName(name string) ([]byte, error) { - return nil, errors.New("no keyring configured") + return nil, errNoKeyring +} + +func (k NoKeyring) GetPubKey(name string) (cryptotypes.PubKey, error) { + return nil, errNoKeyring +} + +func (k NoKeyring) Sign(name string, msg []byte, signMode signingv1beta1.SignMode) ([]byte, error) { + return nil, errNoKeyring } diff --git a/client/v2/autocli/msg.go b/client/v2/autocli/msg.go index d97786be051..c014d3aa500 100644 --- a/client/v2/autocli/msg.go +++ b/client/v2/autocli/msg.go @@ -4,12 +4,20 @@ import ( "context" "fmt" - autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" "github.com/cockroachdb/errors" - "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" - "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/dynamicpb" + + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + + "github.com/cosmos/cosmos-sdk/client" + clienttx "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtxconfig "github.com/cosmos/cosmos-sdk/x/auth/tx/config" ) // BuildMsgCommand builds the msg commands for all the provided modules. If a custom command is provided for a @@ -97,46 +105,43 @@ func (b *Builder) AddMsgServiceCommands(cmd *cobra.Command, cmdDescriptor *autoc // BuildMsgMethodCommand returns a command that outputs the JSON representation of the message. func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor, options *autocliv1.RpcCommandOptions) (*cobra.Command, error) { - jsonMarshalOptions := protojson.MarshalOptions{ - Indent: " ", - UseProtoNames: true, - UseEnumNumbers: false, - EmitUnpopulated: true, - Resolver: b.TypeResolver, - } - cmd, err := b.buildMethodCommandCommon(descriptor, options, func(cmd *cobra.Command, input protoreflect.Message) error { - if noIdent, _ := cmd.Flags().GetBool(flagNoIndent); noIdent { - jsonMarshalOptions.Indent = "" - } + cmd.SetContext(context.WithValue(context.Background(), client.ClientContextKey, &b.ClientCtx)) - bz, err := jsonMarshalOptions.Marshal(input.Interface()) + clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err } - clientCtx, err := client.ReadPersistentCommandFlags(*b.ClientCtx, cmd.Flags()) - if err != nil { - return err - } - - cmd.SetContext(context.WithValue(context.Background(), client.ClientContextKey, &clientCtx)) - if err = client.SetCmdClientContextHandler(clientCtx, cmd); err != nil { - return err - } + // enable sign mode textual and config tx options + b.TxConfigOpts.EnabledSignModes = append(b.TxConfigOpts.EnabledSignModes, signing.SignMode_SIGN_MODE_TEXTUAL) + b.TxConfigOpts.TextualCoinMetadataQueryFn = authtxconfig.NewGRPCCoinMetadataQueryFn(clientCtx) - clientCtx, err = client.GetClientTxContext(cmd) + txConfigWithTextual, err := authtx.NewTxConfigWithOptions( + codec.NewProtoCodec(clientCtx.InterfaceRegistry), + b.TxConfigOpts, + ) if err != nil { return err } + clientCtx = clientCtx.WithTxConfig(txConfigWithTextual) - return b.outOrStdoutFormat(cmd, bz) + // AutoCLI uses protov2 messages, while the SDK only supports proto v1 messages. + // Here we use dynamicpb, to create a proto v1 compatible message. + // The SDK codec will handle protov2 -> protov1 (marshal) + msg := dynamicpb.NewMessage(input.Descriptor()) + proto.Merge(msg, input.Interface()) + + return clienttx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }) if b.AddTxConnFlags != nil { b.AddTxConnFlags(cmd) + } - cmd.Flags().BoolP(flagNoIndent, "", false, "Do not indent JSON output") + // silence usage only for inner txs & queries commands + if cmd != nil { + cmd.SilenceUsage = true } return cmd, err diff --git a/client/v2/autocli/query.go b/client/v2/autocli/query.go index da88d5b0d25..d35a6e2101a 100644 --- a/client/v2/autocli/query.go +++ b/client/v2/autocli/query.go @@ -11,6 +11,7 @@ import ( "github.com/spf13/cobra" "google.golang.org/protobuf/reflect/protoreflect" + "cosmossdk.io/client/v2/internal/flags" "cosmossdk.io/client/v2/internal/util" ) @@ -111,7 +112,7 @@ func (b *Builder) BuildQueryMethodCommand(descriptor protoreflect.MethodDescript } cmd, err := b.buildMethodCommandCommon(descriptor, options, func(cmd *cobra.Command, input protoreflect.Message) error { - if noIndent, _ := cmd.Flags().GetBool(flagNoIndent); noIndent { + if noIndent, _ := cmd.Flags().GetBool(flags.FlagNoIndent); noIndent { encoderOptions.Indent = "" } @@ -140,7 +141,12 @@ func (b *Builder) BuildQueryMethodCommand(descriptor protoreflect.MethodDescript if b.AddQueryConnFlags != nil { b.AddQueryConnFlags(cmd) - cmd.Flags().BoolP(flagNoIndent, "", false, "Do not indent JSON output") + cmd.Flags().BoolP(flags.FlagNoIndent, "", false, "Do not indent JSON output") + } + + // silence usage only for inner txs & queries commands + if cmd != nil { + cmd.SilenceUsage = true } return cmd, nil diff --git a/client/v2/autocli/testdata/help-deprecated-msg.golden b/client/v2/autocli/testdata/help-deprecated-msg.golden index 4de93a2d067..31d61d736cb 100644 --- a/client/v2/autocli/testdata/help-deprecated-msg.golden +++ b/client/v2/autocli/testdata/help-deprecated-msg.golden @@ -37,7 +37,6 @@ Flags: --keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os") --keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used --ledger Use a connected Ledger device - --no-indent Do not indent JSON output --node string : to CometBFT rpc interface for this chain (default "tcp://localhost:26657") --note string Note to add a description to the transaction (previously --memo) --offline Offline mode (does not allow any online functionality) diff --git a/client/v2/autocli/testdata/help-echo-msg.golden b/client/v2/autocli/testdata/help-echo-msg.golden index 7a0905579ad..0b5e9e69b75 100644 --- a/client/v2/autocli/testdata/help-echo-msg.golden +++ b/client/v2/autocli/testdata/help-echo-msg.golden @@ -41,7 +41,6 @@ Flags: --keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os") --keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used --ledger Use a connected Ledger device - --no-indent Do not indent JSON output --node string : to CometBFT rpc interface for this chain (default "tcp://localhost:26657") --note string Note to add a description to the transaction (previously --memo) --offline Offline mode (does not allow any online functionality) diff --git a/client/v2/go.mod b/client/v2/go.mod index e7ac8538e15..011065cb198 100644 --- a/client/v2/go.mod +++ b/client/v2/go.mod @@ -3,10 +3,9 @@ module cosmossdk.io/client/v2 go 1.21 require ( - cosmossdk.io/api v0.7.1 + cosmossdk.io/api v0.7.2-0.20230927090904-9dd34510e273 cosmossdk.io/core v0.12.0 cosmossdk.io/depinject v1.0.0-alpha.4 - cosmossdk.io/log v1.2.1 cosmossdk.io/x/tx v0.10.0 github.com/cockroachdb/errors v1.11.1 github.com/cosmos/cosmos-proto v1.0.0-beta.3 @@ -23,6 +22,7 @@ require ( require ( cosmossdk.io/collections v0.4.0 // indirect cosmossdk.io/errors v1.0.0 // indirect + cosmossdk.io/log v1.2.1 // indirect cosmossdk.io/math v1.1.3-rc.1 // indirect cosmossdk.io/store v1.0.0-rc.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect @@ -73,6 +73,7 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.5.9 // indirect + github.com/google/orderedcode v0.0.1 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect @@ -95,18 +96,20 @@ require ( github.com/klauspost/compress v1.16.7 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/linxGnu/grocksdb v1.8.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20230110094441-db37f07504ce // indirect github.com/oklog/run v1.1.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.9 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -134,11 +137,12 @@ require ( golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect golang.org/x/net v0.15.0 // indirect + golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/term v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -146,3 +150,5 @@ require ( nhooyr.io/websocket v1.8.6 // indirect pgregory.net/rapid v1.1.0 // indirect ) + +replace github.com/cosmos/cosmos-sdk => ./../../ diff --git a/client/v2/go.sum b/client/v2/go.sum index 9244ba7ab9c..819af0aca15 100644 --- a/client/v2/go.sum +++ b/client/v2/go.sum @@ -35,8 +35,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cosmossdk.io/api v0.7.1 h1:PNQ1xN8+/0hj/sSD0ANqjkgfXFys+bZ5L8Hg7uzoUTU= -cosmossdk.io/api v0.7.1/go.mod h1:ure9edhcROIHsngavM6mBLilMGFnfjhV/AaYhEMUkdo= +cosmossdk.io/api v0.7.2-0.20230927090904-9dd34510e273 h1:rNTYK/OQQ5B8jNY0wGSOv+64Fwm7DG8Yke5eRmdTPqk= +cosmossdk.io/api v0.7.2-0.20230927090904-9dd34510e273/go.mod h1:RgzIuGUBiX4E4imKHFyxl+uSKU+qs2v0W2ymVoGoyQQ= cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= cosmossdk.io/core v0.12.0 h1:aFuvkG6eDv0IQC+UDjx86wxNWVAxdCFk7OABJ1Vh4RU= @@ -51,6 +51,8 @@ cosmossdk.io/math v1.1.3-rc.1 h1:NebCNWDqb1MJRNfvxr4YY7d8FSYgkuB3L75K6xvM+Zo= cosmossdk.io/math v1.1.3-rc.1/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= cosmossdk.io/store v1.0.0-rc.0 h1:9DwOjuUYxDtYxn/REkTxGQAmxlIGfRroB35MQ8TrxF4= cosmossdk.io/store v1.0.0-rc.0/go.mod h1:FtBDOJmwtOZfmKKF65bKZbTYgS3bDNjjo3nP76dAegk= +cosmossdk.io/x/protocolpool v0.0.0-20230925135524-a1bc045b3190 h1:XQJj9Dv9Gtze0l2TF79BU5lkP6MkUveTUuKICmxoz+o= +cosmossdk.io/x/protocolpool v0.0.0-20230925135524-a1bc045b3190/go.mod h1:7WUGupOvmlHJoIMBz1JbObQxeo6/TDiuDBxmtod8HRg= cosmossdk.io/x/tx v0.10.0 h1:LxWF/hksVDbeQmFj4voLM5ZCHyVZ1cCNIqKenfH9plc= cosmossdk.io/x/tx v0.10.0/go.mod h1:MKo9/b5wsoL8dd9y9pvD2yOP1CMvzHIWYxi1l2oLPFo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -60,18 +62,26 @@ github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMb github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= +github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -97,6 +107,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.9.0 h1:g1YivPG8jOtrN013Fe8OBXubkiTwvm7/vG2vXz03ANU= +github.com/bits-and-blooms/bitset v1.9.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= @@ -106,6 +118,7 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtyd github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= @@ -151,6 +164,8 @@ github.com/cometbft/cometbft v0.38.0 h1:ogKnpiPX7gxCvqTEF4ly25/wAxUqf181t30P3vqd github.com/cometbft/cometbft v0.38.0/go.mod h1:5Jz0Z8YsHSf0ZaAqGvi/ifioSdVFPtEGrm8Y9T/993k= github.com/cometbft/cometbft-db v0.8.0 h1:vUMDaH3ApkX8m0KZvOFFy9b5DZHBAjsnEuo9AKVZpjo= github.com/cometbft/cometbft-db v0.8.0/go.mod h1:6ASCP4pfhmrCBpfk01/9E1SI29nD3HfVHrY4PG8x5c0= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -163,8 +178,6 @@ github.com/cosmos/cosmos-db v1.0.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0 github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U= github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o= github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= -github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230915113003-c7e0bd7b54d0 h1:BNa8miwG4Aquqcz7ItXIWCao9e6OM+2HKXAPn96oDO8= -github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230915113003-c7e0bd7b54d0/go.mod h1:fuDAZLtTNCqYD0tZSJTNkB6DRjqC3t640i4lT6LAWxo= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= @@ -203,6 +216,10 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -461,8 +478,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/jhump/protoreflect v1.15.2 h1:7YppbATX94jEt9KLAc5hICx4h6Yt3SaavhQRsIUEHP0= -github.com/jhump/protoreflect v1.15.2/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= @@ -594,6 +611,12 @@ github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9 github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= +github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -602,14 +625,16 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= -github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b h1:vab8deKC4QoIfm9fJM59iuNz1ELGsuLoYYpiF+pHiG8= @@ -688,6 +713,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -837,6 +864,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -916,6 +945,7 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1059,6 +1089,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= +golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1136,8 +1168,8 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI= +google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= diff --git a/client/v2/internal/flags/flags.go b/client/v2/internal/flags/flags.go new file mode 100644 index 00000000000..cd50a96be87 --- /dev/null +++ b/client/v2/internal/flags/flags.go @@ -0,0 +1,19 @@ +package flags + +// This defines flag names that can be used in autocli. +const ( + // FlagFrom is the flag to set the from address with which to sign the transaction. + FlagFrom = "from" + + // FlagOutput is the flag to set the output format. + FlagOutput = "output" + + // FlagNoIndent is the flag to not indent the output. + FlagNoIndent = "no-indent" +) + +// List of supported output formats +const ( + OutputFormatJSON = "json" + OutputFormatText = "text" +) diff --git a/codec/bench_test.go b/codec/bench_test.go index abbbbfccab2..d5b5fa132dc 100644 --- a/codec/bench_test.go +++ b/codec/bench_test.go @@ -12,16 +12,26 @@ import ( codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) +type bankSendWrapper struct { + *banktypes.MsgSend +} + +func (msg bankSendWrapper) GetSigners() []sdk.AccAddress { + fromAddress, _ := sdk.AccAddressFromBech32(msg.FromAddress) + return []sdk.AccAddress{fromAddress} +} + func BenchmarkLegacyGetSigners(b *testing.B) { _, _, addr := testdata.KeyTestPubAddr() - msg := &banktypes.MsgSend{ + msg := bankSendWrapper{&banktypes.MsgSend{ FromAddress: addr.String(), ToAddress: "", Amount: nil, - } + }} b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/codec/types/any.go b/codec/types/any.go index c08b08d855b..ad9ba3bdc48 100644 --- a/codec/types/any.go +++ b/codec/types/any.go @@ -4,6 +4,7 @@ import ( fmt "fmt" "github.com/cosmos/gogoproto/proto" + protov2 "google.golang.org/protobuf/proto" errorsmod "cosmossdk.io/errors" @@ -62,13 +63,21 @@ func NewAnyWithValue(v proto.Message) (*Any, error) { return nil, errorsmod.Wrap(sdkerrors.ErrPackAny, "Expecting non nil value to create a new Any") } - bz, err := proto.Marshal(v) + var ( + bz []byte + err error + ) + if msg, ok := v.(protov2.Message); ok { + bz, err = protov2.Marshal(msg) + } else { + bz, err = proto.Marshal(v) + } if err != nil { return nil, err } return &Any{ - TypeUrl: "/" + proto.MessageName(v), + TypeUrl: MsgTypeURL(v), Value: bz, cachedValue: v, }, nil @@ -93,8 +102,17 @@ func UnsafePackAny(x interface{}) *Any { // the packed value so that it can be retrieved from GetCachedValue without // unmarshaling func (any *Any) pack(x proto.Message) error { - any.TypeUrl = "/" + proto.MessageName(x) - bz, err := proto.Marshal(x) + any.TypeUrl = MsgTypeURL(x) + + var ( + bz []byte + err error + ) + if msg, ok := x.(protov2.Message); ok { + bz, err = protov2.Marshal(msg) + } else { + bz, err = proto.Marshal(x) + } if err != nil { return err } diff --git a/codec/types/interface_registry.go b/codec/types/interface_registry.go index 4790bf6ef66..99393fa2c57 100644 --- a/codec/types/interface_registry.go +++ b/codec/types/interface_registry.go @@ -189,7 +189,7 @@ func (registry *interfaceRegistry) EnsureRegistered(impl interface{}) error { // same typeURL. func (registry *interfaceRegistry) RegisterImplementations(iface interface{}, impls ...proto.Message) { for _, impl := range impls { - typeURL := "/" + proto.MessageName(impl) + typeURL := MsgTypeURL(impl) registry.registerImpl(iface, typeURL, impl) } } diff --git a/codec/types/util.go b/codec/types/util.go new file mode 100644 index 00000000000..b29fb33b503 --- /dev/null +++ b/codec/types/util.go @@ -0,0 +1,15 @@ +package types + +import ( + "github.com/cosmos/gogoproto/proto" + protov2 "google.golang.org/protobuf/proto" +) + +// MsgTypeURL returns the TypeURL of a `sdk.Msg`. +func MsgTypeURL(msg proto.Message) string { + if m, ok := msg.(protov2.Message); ok { + return "/" + string(m.ProtoReflect().Descriptor().FullName()) + } + + return "/" + proto.MessageName(msg) +} diff --git a/crypto/keyring/autocli.go b/crypto/keyring/autocli.go new file mode 100644 index 00000000000..0dd91ff60a4 --- /dev/null +++ b/crypto/keyring/autocli.go @@ -0,0 +1,86 @@ +package keyring + +import ( + signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// autoCLIKeyring represents the keyring interface used by the AutoCLI. +// It purposely does not import the AutoCLI package to avoid circular dependencies. +type autoCLIKeyring interface { + // List returns the names of all keys stored in the keyring. + List() ([]string, error) + + // LookupAddressByKeyName returns the address of the key with the given name. + LookupAddressByKeyName(name string) ([]byte, error) + + // GetPubKey returns the public key of the key with the given name. + GetPubKey(name string) (cryptotypes.PubKey, error) + + // Sign signs the given bytes with the key with the given name. + Sign(name string, msg []byte, signMode signingv1beta1.SignMode) ([]byte, error) +} + +// NewAutoCLIKeyring wraps the SDK keyring and make it compatible with the AutoCLI keyring interfaces. +func NewAutoCLIKeyring(kr Keyring) (autoCLIKeyring, error) { + return &autoCLIKeyringAdapter{kr}, nil +} + +type autoCLIKeyringAdapter struct { + Keyring +} + +func (a *autoCLIKeyringAdapter) List() ([]string, error) { + list, err := a.Keyring.List() + if err != nil { + return nil, err + } + + names := make([]string, len(list)) + for i, key := range list { + names[i] = key.Name + } + + return names, nil +} + +// LookupAddressByKeyName returns the address of a key stored in the keyring +func (a *autoCLIKeyringAdapter) LookupAddressByKeyName(name string) ([]byte, error) { + record, err := a.Keyring.Key(name) + if err != nil { + return nil, err + } + + addr, err := record.GetAddress() + if err != nil { + return nil, err + } + + return addr, nil +} + +func (a *autoCLIKeyringAdapter) GetPubKey(name string) (cryptotypes.PubKey, error) { + record, err := a.Keyring.Key(name) + if err != nil { + return nil, err + } + + return record.GetPubKey() +} + +func (a *autoCLIKeyringAdapter) Sign(name string, msg []byte, signMode signingv1beta1.SignMode) ([]byte, error) { + record, err := a.Keyring.Key(name) + if err != nil { + return nil, err + } + + sdkSignMode, err := authsigning.APISignModeToInternal(signMode) + if err != nil { + return nil, err + } + + signBytes, _, err := a.Keyring.Sign(record.Name, msg, sdkSignMode) + return signBytes, err +} diff --git a/crypto/keyring/keyring.go b/crypto/keyring/keyring.go index 91004aa91a4..2e92c484254 100644 --- a/crypto/keyring/keyring.go +++ b/crypto/keyring/keyring.go @@ -102,9 +102,6 @@ type Keyring interface { Exporter Migrator - - // Implements client/v2 keyring interface - LookupAddressByKeyName(name string) ([]byte, error) } // Signer is implemented by key stores that want to provide signing capabilities. @@ -622,21 +619,6 @@ func (ks keystore) SupportedAlgorithms() (SigningAlgoList, SigningAlgoList) { return ks.options.SupportedAlgos, ks.options.SupportedAlgosLedger } -// LookupAddressByKeyName returns the address of a key stored in the keyring -func (ks keystore) LookupAddressByKeyName(name string) ([]byte, error) { - record, err := ks.Key(name) - if err != nil { - return nil, err - } - - addr, err := record.GetAddress() - if err != nil { - return nil, err - } - - return addr, nil -} - // SignWithLedger signs a binary message with the ledger device referenced by an Info object // and returns the signed bytes and the public key. It returns an error if the device could // not be queried or it returned an error. diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 6678166ad5c..1b829136790 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -15,6 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/config" "github.com/cosmos/cosmos-sdk/codec" addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/server" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" @@ -109,8 +110,8 @@ func NewRootCmd() *cobra.Command { } autoCliOpts := tempApp.AutoCliOpts() - autoCliOpts.Keyring = initClientCtx.Keyring - autoCliOpts.ClientCtx = &initClientCtx + autoCliOpts.Keyring, _ = keyring.NewAutoCLIKeyring(initClientCtx.Keyring) + autoCliOpts.ClientCtx = initClientCtx if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil { panic(err) diff --git a/simapp/simd/cmd/root_v2.go b/simapp/simd/cmd/root_v2.go index d22230b0fac..170aa94b0fb 100644 --- a/simapp/simd/cmd/root_v2.go +++ b/simapp/simd/cmd/root_v2.go @@ -18,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/config" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/runtime" "github.com/cosmos/cosmos-sdk/server" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" @@ -34,7 +35,7 @@ func NewRootCmd() *cobra.Command { txConfigOpts tx.ConfigOptions autoCliOpts autocli.AppOptions moduleBasicManager module.BasicManager - initClientCtx *client.Context + clientCtx client.Context ) if err := depinject.Inject( @@ -51,7 +52,7 @@ func NewRootCmd() *cobra.Command { &txConfigOpts, &autoCliOpts, &moduleBasicManager, - &initClientCtx, + &clientCtx, ); err != nil { panic(err) } @@ -65,7 +66,6 @@ func NewRootCmd() *cobra.Command { cmd.SetOut(cmd.OutOrStdout()) cmd.SetErr(cmd.ErrOrStderr()) - clientCtx := *initClientCtx clientCtx = clientCtx.WithCmdContext(cmd.Context()) clientCtx, err := client.ReadPersistentCommandFlags(clientCtx, cmd.Flags()) if err != nil { @@ -89,7 +89,6 @@ func NewRootCmd() *cobra.Command { return err } clientCtx = clientCtx.WithTxConfig(txConfigWithTextual) - if err := client.SetCmdClientContextHandler(clientCtx, cmd); err != nil { return err } @@ -101,7 +100,7 @@ func NewRootCmd() *cobra.Command { }, } - initRootCmd(rootCmd, initClientCtx.TxConfig, initClientCtx.InterfaceRegistry, initClientCtx.Codec, moduleBasicManager) + initRootCmd(rootCmd, clientCtx.TxConfig, clientCtx.InterfaceRegistry, clientCtx.Codec, moduleBasicManager) if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil { panic(err) @@ -118,7 +117,7 @@ func ProvideClientContext( addressCodec address.Codec, validatorAddressCodec runtime.ValidatorAddressCodec, consensusAddressCodec runtime.ConsensusAddressCodec, -) *client.Context { +) client.Context { var err error clientCtx := client.Context{}. @@ -141,14 +140,14 @@ func ProvideClientContext( panic(err) } - return &clientCtx + return clientCtx } -func ProvideKeyring(clientCtx *client.Context, addressCodec address.Codec) (clientv2keyring.Keyring, error) { - kb, err := client.NewKeyringFromBackend(*clientCtx, clientCtx.Keyring.Backend()) +func ProvideKeyring(clientCtx client.Context, addressCodec address.Codec) (clientv2keyring.Keyring, error) { + kb, err := client.NewKeyringFromBackend(clientCtx, clientCtx.Keyring.Backend()) if err != nil { return nil, err } - return kb, nil + return keyring.NewAutoCLIKeyring(kb) } diff --git a/types/tx_msg.go b/types/tx_msg.go index 230092ad218..399dafd4cc4 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -9,6 +9,7 @@ import ( protov2 "google.golang.org/protobuf/proto" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) @@ -95,13 +96,7 @@ type TxDecoder func(txBytes []byte) (Tx, error) type TxEncoder func(tx Tx) ([]byte, error) // MsgTypeURL returns the TypeURL of a `sdk.Msg`. -func MsgTypeURL(msg proto.Message) string { - if m, ok := msg.(protov2.Message); ok { - return "/" + string(m.ProtoReflect().Descriptor().FullName()) - } - - return "/" + proto.MessageName(msg) -} +var MsgTypeURL = codectypes.MsgTypeURL // GetMsgFromTypeURL returns a `sdk.Msg` message type from a type URL func GetMsgFromTypeURL(cdc codec.Codec, input string) (Msg, error) { diff --git a/x/auth/signing/sig_verifiable_tx.go b/x/auth/signing/sig_verifiable_tx.go index 0b6da14aff5..c8a752e7e47 100644 --- a/x/auth/signing/sig_verifiable_tx.go +++ b/x/auth/signing/sig_verifiable_tx.go @@ -16,7 +16,7 @@ type SigVerifiableTx interface { } // Tx defines a transaction interface that supports all standard message, signature -// fee, memo, tips, and auxiliary interfaces. +// fee, memo and auxiliary interfaces. type Tx interface { SigVerifiableTx diff --git a/x/bank/types/msgs.go b/x/bank/types/msgs.go index 9ce68e48344..05a23c280e2 100644 --- a/x/bank/types/msgs.go +++ b/x/bank/types/msgs.go @@ -15,12 +15,6 @@ func NewMsgSend(fromAddr, toAddr sdk.AccAddress, amount sdk.Coins) *MsgSend { return &MsgSend{FromAddress: fromAddr.String(), ToAddress: toAddr.String(), Amount: amount} } -// GetSigners Implements Msg. -func (msg MsgSend) GetSigners() []sdk.AccAddress { - fromAddress, _ := sdk.AccAddressFromBech32(msg.FromAddress) - return []sdk.AccAddress{fromAddress} -} - // NewMsgMultiSend - construct arbitrary multi-in, multi-out send msg. func NewMsgMultiSend(in Input, out []Output) *MsgMultiSend { return &MsgMultiSend{Inputs: []Input{in}, Outputs: out} From 77f5068ed453c5106754b619d64a1fc504a0c26b Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 4 Oct 2023 16:00:11 +0200 Subject: [PATCH 2/3] fix tests --- client/v2/autocli/common_test.go | 86 +++++- client/v2/autocli/msg.go | 1 + client/v2/autocli/msg_test.go | 285 ++---------------- client/v2/autocli/query_test.go | 6 +- .../testdata/help-deprecated-msg.golden | 64 ---- .../v2/autocli/testdata/help-echo-msg.golden | 90 ++---- .../autocli/testdata/help-toplevel-msg.golden | 12 +- client/v2/autocli/testdata/msg-output.golden | 1 + client/v2/go.mod | 1 + client/v2/go.sum | 4 + 10 files changed, 150 insertions(+), 400 deletions(-) delete mode 100644 client/v2/autocli/testdata/help-deprecated-msg.golden create mode 100644 client/v2/autocli/testdata/msg-output.golden diff --git a/client/v2/autocli/common_test.go b/client/v2/autocli/common_test.go index 5926941d8be..f1472d486b7 100644 --- a/client/v2/autocli/common_test.go +++ b/client/v2/autocli/common_test.go @@ -12,6 +12,7 @@ import ( "google.golang.org/protobuf/reflect/protoregistry" "gotest.tools/v3/assert" + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" reflectionv2alpha1 "cosmossdk.io/api/cosmos/base/reflection/v2alpha1" "cosmossdk.io/client/v2/autocli/flag" "cosmossdk.io/client/v2/internal/testpb" @@ -22,6 +23,8 @@ import ( sdkkeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/bank" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) type fixture struct { @@ -47,7 +50,7 @@ func initFixture(t *testing.T) *fixture { clientConn, err := grpc.Dial(listener.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) assert.NilError(t, err) - encodingConfig := moduletestutil.MakeTestEncodingConfig() + encodingConfig := moduletestutil.MakeTestEncodingConfig(bank.AppModuleBasic{}) kr, err := sdkkeyring.New(sdk.KeyringServiceName(), sdkkeyring.BackendMemory, home, nil, encodingConfig.Codec) assert.NilError(t, err) @@ -55,7 +58,7 @@ func initFixture(t *testing.T) *fixture { assert.NilError(t, err) interfaceRegistry := encodingConfig.Codec.InterfaceRegistry() - interfaceRegistry.RegisterInterface(sdk.MsgTypeURL(&testpb.MsgRequest{}), (*sdk.Msg)(nil), &testpb.MsgRequest{}) + banktypes.RegisterInterfaces(interfaceRegistry) var initClientCtx client.Context initClientCtx = initClientCtx. @@ -144,3 +147,82 @@ func (t testEchoServer) Echo(_ context.Context, request *testpb.EchoRequest) (*t } var _ testpb.QueryServer = testEchoServer{} + +func TestEnhanceCommand(t *testing.T) { + b := &Builder{} + // Test that the command has a subcommand + cmd := &cobra.Command{Use: "test"} + cmd.AddCommand(&cobra.Command{Use: "test"}) + + for i := 0; i < 2; i++ { + cmdTp := cmdType(i) + + appOptions := AppOptions{ + ModuleOptions: map[string]*autocliv1.ModuleOptions{ + "test": {}, + }, + } + + err := b.enhanceCommandCommon(cmd, cmdTp, appOptions, map[string]*cobra.Command{}) + assert.NilError(t, err) + + cmd = &cobra.Command{Use: "test"} + + appOptions = AppOptions{ + ModuleOptions: map[string]*autocliv1.ModuleOptions{}, + } + customCommands := map[string]*cobra.Command{ + "test2": {Use: "test"}, + } + err = b.enhanceCommandCommon(cmd, cmdTp, appOptions, customCommands) + assert.NilError(t, err) + + cmd = &cobra.Command{Use: "test"} + appOptions = AppOptions{ + ModuleOptions: map[string]*autocliv1.ModuleOptions{ + "test": {Tx: nil}, + }, + } + err = b.enhanceCommandCommon(cmd, cmdTp, appOptions, map[string]*cobra.Command{}) + assert.NilError(t, err) + } +} + +func TestErrorBuildCommand(t *testing.T) { + fixture := initFixture(t) + b := fixture.b + b.AddQueryConnFlags = nil + b.AddTxConnFlags = nil + + commandDescriptor := &autocliv1.ServiceCommandDescriptor{ + Service: testpb.Msg_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "Send", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + { + ProtoField: "un-existent-proto-field", + }, + }, + }, + }, + } + + appOptions := AppOptions{ + ModuleOptions: map[string]*autocliv1.ModuleOptions{ + "test": { + Query: commandDescriptor, + Tx: commandDescriptor, + }, + }, + ClientCtx: b.ClientCtx, + } + + _, err := b.BuildMsgCommand(appOptions, nil) + assert.ErrorContains(t, err, "can't find field un-existent-proto-field") + + appOptions.ModuleOptions["test"].Tx = &autocliv1.ServiceCommandDescriptor{Service: "un-existent-service"} + appOptions.ModuleOptions["test"].Query = &autocliv1.ServiceCommandDescriptor{Service: "un-existent-service"} + _, err = b.BuildMsgCommand(appOptions, nil) + assert.ErrorContains(t, err, "can't find service un-existent-service") +} diff --git a/client/v2/autocli/msg.go b/client/v2/autocli/msg.go index c014d3aa500..a2bd5eb9051 100644 --- a/client/v2/autocli/msg.go +++ b/client/v2/autocli/msg.go @@ -125,6 +125,7 @@ func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor return err } clientCtx = clientCtx.WithTxConfig(txConfigWithTextual) + clientCtx.Output = cmd.OutOrStdout() // AutoCLI uses protov2 messages, while the SDK only supports proto v1 messages. // Here we use dynamicpb, to create a proto v1 compatible message. diff --git a/client/v2/autocli/msg_test.go b/client/v2/autocli/msg_test.go index 74d72933183..86d328aaa4a 100644 --- a/client/v2/autocli/msg_test.go +++ b/client/v2/autocli/msg_test.go @@ -2,135 +2,45 @@ package autocli import ( "fmt" - "strings" "testing" "github.com/spf13/cobra" - "google.golang.org/protobuf/encoding/protojson" "gotest.tools/v3/assert" "gotest.tools/v3/golden" autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1" "cosmossdk.io/client/v2/internal/testpb" ) var buildModuleMsgCommand = func(moduleName string, b *Builder) (*cobra.Command, error) { cmd := topLevelCmd(moduleName, fmt.Sprintf("Transactions commands for the %s module", moduleName)) - err := b.AddMsgServiceCommands(cmd, testCmdMsgDesc) + err := b.AddMsgServiceCommands(cmd, bankAutoCLI) return cmd, err } -var testCmdMsgDesc = &autocliv1.ServiceCommandDescriptor{ - Service: testpb.Msg_ServiceDesc.ServiceName, +var bankAutoCLI = &autocliv1.ServiceCommandDescriptor{ + Service: bankv1beta1.Msg_ServiceDesc.ServiceName, RpcCommandOptions: []*autocliv1.RpcCommandOptions{ { - RpcMethod: "Send", - Use: "send [pos1] [pos2] [pos3...]", - Version: "1.0", - Alias: []string{"s"}, - SuggestFor: []string{"send"}, - Example: "send 1 abc {}", - Short: "send msg the value provided by the user", - Long: "send msg the value provided by the user as a proto JSON object with populated with the provided fields and positional arguments", - PositionalArgs: []*autocliv1.PositionalArgDescriptor{ - { - ProtoField: "positional1", - }, - { - ProtoField: "positional2", - }, - { - ProtoField: "positional3_varargs", - Varargs: true, - }, - }, - FlagOptions: map[string]*autocliv1.FlagOptions{ - "u32": { - Name: "uint32", - Shorthand: "u", - Usage: "some random uint32", - }, - "i32": { - Usage: "some random int32", - DefaultValue: "3", - }, - "u64": { - Usage: "some random uint64", - DefaultValue: "5", - }, - "deprecated_field": { - Deprecated: "don't use this", - }, - "shorthand_deprecated_field": { - Shorthand: "d", - Deprecated: "bad idea", - }, - "hidden_bool": { - Hidden: true, - }, - }, - }, - }, - SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{ - // we test the sub-command functionality using the same service with different options - "deprecatedmsg": { - Service: testpb.Msg_ServiceDesc.ServiceName, - RpcCommandOptions: []*autocliv1.RpcCommandOptions{ - { - RpcMethod: "Send", - Deprecated: "dont use this", - Short: "deprecated subcommand", - }, - }, - }, - "skipmsg": { - Service: testpb.Msg_ServiceDesc.ServiceName, - RpcCommandOptions: []*autocliv1.RpcCommandOptions{ - { - RpcMethod: "Send", - Skip: true, - Short: "skip subcommand", - }, - }, + RpcMethod: "Send", + Use: "send [from_key_or_address] [to_address] [amount] [flags]", + Short: "Send coins from one account to another", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "from_address"}, {ProtoField: "to_address"}, {ProtoField: "amount"}}, }, }, + EnhanceCustomCommand: true, } -func TestMsgOptions(t *testing.T) { +func TestMsg(t *testing.T) { fixture := initFixture(t) out, err := runCmd(fixture.conn, fixture.b, buildModuleMsgCommand, "send", - "5", "6", "1foo", - "--uint32", "7", - "--u64", "8", - "--output", "json", - ) - assert.NilError(t, err) - - response := out.String() - var output testpb.MsgRequest - err = protojson.Unmarshal([]byte(response), &output) - assert.NilError(t, err) - assert.Equal(t, output.GetU32(), uint32(7)) - assert.Equal(t, output.GetPositional1(), int32(5)) - assert.Equal(t, output.GetPositional2(), "6") -} - -func TestMsgOutputFormat(t *testing.T) { - fixture := initFixture(t) - - out, err := runCmd(fixture.conn, fixture.b, buildModuleMsgCommand, - "send", "5", "6", "1foo", + "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", "1foo", + "--generate-only", "--output", "json", ) assert.NilError(t, err) - assert.Assert(t, strings.Contains(out.String(), "{")) - - out, err = runCmd(fixture.conn, fixture.b, buildModuleMsgCommand, - "send", "5", "6", "1foo", - "--output", "text", - ) - assert.NilError(t, err) - assert.Assert(t, strings.Contains(out.String(), "positional1: 5")) + golden.Assert(t, out.String(), "msg-output.golden") } func TestMsgOptionsError(t *testing.T) { @@ -138,92 +48,13 @@ func TestMsgOptionsError(t *testing.T) { _, err := runCmd(fixture.conn, fixture.b, buildModuleMsgCommand, "send", "5", - "--uint32", "7", - "--u64", "8", ) - assert.ErrorContains(t, err, "requires at least 2 arg(s)") + assert.ErrorContains(t, err, "accepts 3 arg(s)") _, err = runCmd(fixture.conn, fixture.b, buildModuleMsgCommand, - "send", "5", "6", `{"denom":"foo","amount":"1"}`, - "--uint32", "7", - "--u64", "abc", + "send", "foo", "bar", "invalid", ) - assert.ErrorContains(t, err, "invalid argument ") -} - -func TestDeprecatedMsg(t *testing.T) { - fixture := initFixture(t) - - out, err := runCmd(fixture.conn, fixture.b, buildModuleMsgCommand, - "send", "1", "abc", "--deprecated-field", "foo", - ) - assert.NilError(t, err) - assert.Assert(t, strings.Contains(out.String(), "--deprecated-field has been deprecated")) - - out, err = runCmd(fixture.conn, fixture.b, buildModuleMsgCommand, - "send", "1", "abc", "5stake", "-d", "foo", - ) - assert.NilError(t, err) - assert.Assert(t, strings.Contains(out.String(), "--shorthand-deprecated-field has been deprecated")) -} - -func TestEverythingMsg(t *testing.T) { - fixture := initFixture(t) - - out, err := runCmd(fixture.conn, fixture.b, buildModuleMsgCommand, - "send", - "1", - "abc", - "1234foo", - "4321foo", - "--output", "json", - "--a-bool", - "--an-enum", "two", - "--a-message", `{"bar":"abc", "baz":-3}`, - "--duration", "4h3s", - "--uint32", "27", - "--u64", "3267246890", - "--i32", "-253", - "--i64", "-234602347", - "--str", "def", - "--timestamp", "2019-01-02T00:01:02Z", - "--a-coin", "10000000foo", - "--an-address", "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", - "--bz", "c2RncXdlZndkZ3NkZw==", - "--page-count-total", - "--page-key", "MTIzNTQ4N3NnaGRhcw==", - "--page-limit", "1000", - "--page-offset", "10", - "--page-reverse", - "--bools", "true", - "--bools", "false,false,true", - "--enums", "one", - "--enums", "five", - "--enums", "two", - "--strings", "abc", - "--strings", "xyz", - "--strings", "xyz,qrs", - "--durations", "3s", - "--durations", "5s", - "--durations", "10h", - "--some-messages", "{}", - "--some-messages", `{"bar":"baz"}`, - "--some-messages", `{"baz":-1}`, - "--uints", "1,2,3", - "--uints", "4", - ) - assert.NilError(t, err) - - response := out.String() - var output testpb.MsgRequest - err = protojson.Unmarshal([]byte(response), &output) - assert.NilError(t, err) - assert.Equal(t, output.GetU32(), uint32(27)) - assert.Equal(t, output.GetU64(), uint64(3267246890)) - assert.Equal(t, output.GetPositional1(), int32(1)) - assert.Equal(t, output.GetPositional2(), "abc") - assert.Equal(t, output.GetABool(), true) - assert.Equal(t, output.GetAnEnum(), testpb.Enum_ENUM_TWO) + assert.ErrorContains(t, err, "invalid argument") } func TestHelpMsg(t *testing.T) { @@ -236,19 +67,18 @@ func TestHelpMsg(t *testing.T) { out, err = runCmd(fixture.conn, fixture.b, buildModuleMsgCommand, "send", "-h") assert.NilError(t, err) golden.Assert(t, out.String(), "help-echo-msg.golden") - - out, err = runCmd(fixture.conn, fixture.b, buildModuleMsgCommand, "deprecatedmsg", "send", "-h") - assert.NilError(t, err) - golden.Assert(t, out.String(), "help-deprecated-msg.golden") } -func TestBuildMsgCommand(t *testing.T) { +func TestBuildCustomMsgCommand(t *testing.T) { b := &Builder{} customCommandCalled := false appOptions := AppOptions{ ModuleOptions: map[string]*autocliv1.ModuleOptions{ "test": { - Tx: testCmdMsgDesc, + Tx: &autocliv1.ServiceCommandDescriptor{ + Service: testpb.Msg_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{}, + }, }, }, } @@ -264,44 +94,6 @@ func TestBuildMsgCommand(t *testing.T) { assert.Assert(t, customCommandCalled) } -func TestErrorBuildMsgCommand(t *testing.T) { - fixture := initFixture(t) - b := fixture.b - b.AddQueryConnFlags = nil - b.AddTxConnFlags = nil - - commandDescriptor := &autocliv1.ServiceCommandDescriptor{ - Service: testpb.Msg_ServiceDesc.ServiceName, - RpcCommandOptions: []*autocliv1.RpcCommandOptions{ - { - RpcMethod: "Send", - PositionalArgs: []*autocliv1.PositionalArgDescriptor{ - { - ProtoField: "un-existent-proto-field", - }, - }, - }, - }, - } - - appOptions := AppOptions{ - ModuleOptions: map[string]*autocliv1.ModuleOptions{ - "test": { - Tx: commandDescriptor, - }, - }, - ClientCtx: b.ClientCtx, - } - - _, err := b.BuildMsgCommand(appOptions, nil) - assert.ErrorContains(t, err, "can't find field un-existent-proto-field") - - nonExistentService := &autocliv1.ServiceCommandDescriptor{Service: "un-existent-service"} - appOptions.ModuleOptions["test"].Tx = nonExistentService - _, err = b.BuildMsgCommand(appOptions, nil) - assert.ErrorContains(t, err, "can't find service un-existent-service") -} - func TestNotFoundErrorsMsg(t *testing.T) { fixture := initFixture(t) b := fixture.b @@ -353,38 +145,3 @@ func TestNotFoundErrorsMsg(t *testing.T) { }) assert.ErrorContains(t, err, "can't find field un-existent-flag") } - -func TestEnhanceMessageCommand(t *testing.T) { - b := &Builder{} - // Test that the command has a subcommand - cmd := &cobra.Command{Use: "test"} - cmd.AddCommand(&cobra.Command{Use: "test"}) - - appOptions := AppOptions{ - ModuleOptions: map[string]*autocliv1.ModuleOptions{ - "test": {}, - }, - } - - err := b.enhanceCommandCommon(cmd, msgCmdType, appOptions, map[string]*cobra.Command{}) - assert.NilError(t, err) - - cmd = &cobra.Command{Use: "test"} - - appOptions.ModuleOptions = map[string]*autocliv1.ModuleOptions{} - customCommands := map[string]*cobra.Command{ - "test2": {Use: "test"}, - } - err = b.enhanceCommandCommon(cmd, msgCmdType, appOptions, customCommands) - assert.NilError(t, err) - - cmd = &cobra.Command{Use: "test"} - appOptions = AppOptions{ - ModuleOptions: map[string]*autocliv1.ModuleOptions{ - "test": {Tx: nil}, - }, - } - customCommands = map[string]*cobra.Command{} - err = b.enhanceCommandCommon(cmd, msgCmdType, appOptions, customCommands) - assert.NilError(t, err) -} diff --git a/client/v2/autocli/query_test.go b/client/v2/autocli/query_test.go index bc556af268a..b1945f83995 100644 --- a/client/v2/autocli/query_test.go +++ b/client/v2/autocli/query_test.go @@ -613,7 +613,7 @@ func TestOutputFormat(t *testing.T) { assert.Assert(t, strings.Contains(out.String(), " positional1: 1")) } -func TestHelp(t *testing.T) { +func TestHelpQuery(t *testing.T) { fixture := initFixture(t) out, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, "-h") @@ -633,7 +633,7 @@ func TestHelp(t *testing.T) { golden.Assert(t, out.String(), "help-skip.golden") } -func TestDeprecated(t *testing.T) { +func TestDeprecatedQuery(t *testing.T) { fixture := initFixture(t) out, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, "echo", @@ -670,7 +670,7 @@ func TestBuildCustomQueryCommand(t *testing.T) { assert.Assert(t, customCommandCalled) } -func TestNotFoundErrors(t *testing.T) { +func TestNotFoundErrorsQuery(t *testing.T) { fixture := initFixture(t) b := fixture.b b.AddQueryConnFlags = nil diff --git a/client/v2/autocli/testdata/help-deprecated-msg.golden b/client/v2/autocli/testdata/help-deprecated-msg.golden deleted file mode 100644 index 31d61d736cb..00000000000 --- a/client/v2/autocli/testdata/help-deprecated-msg.golden +++ /dev/null @@ -1,64 +0,0 @@ -Command "send" is deprecated, dont use this -deprecated subcommand - -Usage: - test deprecatedmsg send [flags] - -Flags: - --a-bool - --a-coin cosmos.base.v1beta1.Coin - --a-message testpb.AMessage (json) - --a-validator-address account address or key name - -a, --account-number uint The account number of the signing account (offline mode only) - --an-address account address or key name - --an-enum Enum (unspecified | one | two | five | neg-three) (default unspecified) - --aux Generate aux signer data instead of sending a tx - --bools bools (default []) - -b, --broadcast-mode string Transaction broadcasting mode (sync|async) (default "sync") - --bz binary - --chain-id string The network chain ID - --deprecated-field string - --dry-run ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible) - --duration duration - --durations duration (repeated) - --enums Enum (unspecified | one | two | five | neg-three) (repeated) - --fee-granter string Fee granter grants fees for the transaction - --fee-payer string Fee payer pays fees for the transaction instead of deducting from the signer - --fees string Fees to pay along with transaction; eg: 10uatom - --from string Name or address of private key with which to sign - --gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically. Note: "auto" option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of "fees". (default 200000) - --gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1) - --gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) - --generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name) - -h, --help help for send - --hidden-bool - --i32 int32 - --i64 int - --keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os") - --keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used - --ledger Use a connected Ledger device - --node string : to CometBFT rpc interface for this chain (default "tcp://localhost:26657") - --note string Note to add a description to the transaction (previously --memo) - --offline Offline mode (does not allow any online functionality) - -o, --output string Output format (text|json) (default "json") - --page-count-total - --page-key binary - --page-limit uint - --page-offset uint - --page-reverse - --positional1 int32 - --positional2 string - --positional3-varargs cosmos.base.v1beta1.Coin (repeated) - -s, --sequence uint The sequence number of the signing account (offline mode only) - --shorthand-deprecated-field string - --sign-mode string Choose sign mode (direct|amino-json|direct-aux), this is an advanced feature - --some-messages testpb.AMessage (json) (repeated) - --str string - --strings strings - --timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height - --timestamp timestamp (RFC 3339) - --tip string Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator - --u32 uint32 - --u64 uint - --uints uints (default []) - -y, --yes Skip tx broadcasting prompt confirmation diff --git a/client/v2/autocli/testdata/help-echo-msg.golden b/client/v2/autocli/testdata/help-echo-msg.golden index 0b5e9e69b75..e082cd565f7 100644 --- a/client/v2/autocli/testdata/help-echo-msg.golden +++ b/client/v2/autocli/testdata/help-echo-msg.golden @@ -1,66 +1,32 @@ -send msg the value provided by the user as a proto JSON object with populated with the provided fields and positional arguments +Send coins from one account to another Usage: - test send [pos1] [pos2] [pos3...] [flags] - -Aliases: - send, s - -Examples: -send 1 abc {} + test send [from_key_or_address] [to_address] [amount] [flags] Flags: - --a-bool - --a-coin cosmos.base.v1beta1.Coin - --a-message testpb.AMessage (json) - --a-validator-address account address or key name - -a, --account-number uint The account number of the signing account (offline mode only) - --an-address account address or key name - --an-enum Enum (unspecified | one | two | five | neg-three) (default unspecified) - --aux Generate aux signer data instead of sending a tx - --bools bools (default []) - -b, --broadcast-mode string Transaction broadcasting mode (sync|async) (default "sync") - --bz binary - --chain-id string The network chain ID - --deprecated-field string (DEPRECATED: don't use this) - --dry-run ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible) - --duration duration - --durations duration (repeated) - --enums Enum (unspecified | one | two | five | neg-three) (repeated) - --fee-granter string Fee granter grants fees for the transaction - --fee-payer string Fee payer pays fees for the transaction instead of deducting from the signer - --fees string Fees to pay along with transaction; eg: 10uatom - --from string Name or address of private key with which to sign - --gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically. Note: "auto" option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of "fees". (default 200000) - --gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1) - --gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) - --generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name) - -h, --help help for send - --i32 int32 some random int32 - --i64 int - --keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os") - --keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used - --ledger Use a connected Ledger device - --node string : to CometBFT rpc interface for this chain (default "tcp://localhost:26657") - --note string Note to add a description to the transaction (previously --memo) - --offline Offline mode (does not allow any online functionality) - -o, --output string Output format (text|json) (default "json") - --page-count-total - --page-key binary - --page-limit uint - --page-offset uint - --page-reverse - -s, --sequence uint The sequence number of the signing account (offline mode only) - -d, --shorthand-deprecated-field string (DEPRECATED: bad idea) - --sign-mode string Choose sign mode (direct|amino-json|direct-aux), this is an advanced feature - --some-messages testpb.AMessage (json) (repeated) - --str string - --strings strings - --timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height - --timestamp timestamp (RFC 3339) - --tip string Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator - --u64 uint some random uint64 - -u, --uint32 uint32 some random uint32 - --uints uints (default []) - -v, --version version for send - -y, --yes Skip tx broadcasting prompt confirmation + -a, --account-number uint The account number of the signing account (offline mode only) + --aux Generate aux signer data instead of sending a tx + -b, --broadcast-mode string Transaction broadcasting mode (sync|async) (default "sync") + --chain-id string The network chain ID + --dry-run ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible) + --fee-granter string Fee granter grants fees for the transaction + --fee-payer string Fee payer pays fees for the transaction instead of deducting from the signer + --fees string Fees to pay along with transaction; eg: 10uatom + --from string Name or address of private key with which to sign + --gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically. Note: "auto" option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of "fees". (default 200000) + --gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1) + --gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) + --generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name) + -h, --help help for send + --keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os") + --keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used + --ledger Use a connected Ledger device + --node string : to CometBFT rpc interface for this chain (default "tcp://localhost:26657") + --note string Note to add a description to the transaction (previously --memo) + --offline Offline mode (does not allow any online functionality) + -o, --output string Output format (text|json) (default "json") + -s, --sequence uint The sequence number of the signing account (offline mode only) + --sign-mode string Choose sign mode (direct|amino-json|direct-aux), this is an advanced feature + --timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height + --tip string Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator + -y, --yes Skip tx broadcasting prompt confirmation diff --git a/client/v2/autocli/testdata/help-toplevel-msg.golden b/client/v2/autocli/testdata/help-toplevel-msg.golden index 0eac1e006f1..51d33ed95e6 100644 --- a/client/v2/autocli/testdata/help-toplevel-msg.golden +++ b/client/v2/autocli/testdata/help-toplevel-msg.golden @@ -5,11 +5,13 @@ Usage: test [command] Available Commands: - completion Generate the autocompletion script for the specified shell - deprecatedmsg Tx commands for the testpb.Msg service - help Help about any command - send send msg the value provided by the user - skipmsg Tx commands for the testpb.Msg service + burn + completion Generate the autocompletion script for the specified shell + help Help about any command + multi-send + send Send coins from one account to another + set-send-enabled + update-params Flags: -h, --help help for test diff --git a/client/v2/autocli/testdata/msg-output.golden b/client/v2/autocli/testdata/msg-output.golden new file mode 100644 index 00000000000..73bddd4ad8a --- /dev/null +++ b/client/v2/autocli/testdata/msg-output.golden @@ -0,0 +1 @@ +{"body":{"messages":[{"@type":"/cosmos.bank.v1beta1.MsgSend","from_address":"cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk","to_address":"cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk","amount":[{"denom":"foo","amount":"1"}]}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""},"tip":null},"signatures":[]} diff --git a/client/v2/go.mod b/client/v2/go.mod index 011065cb198..1502b41bb69 100644 --- a/client/v2/go.mod +++ b/client/v2/go.mod @@ -69,6 +69,7 @@ require ( github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v1.1.0 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect diff --git a/client/v2/go.sum b/client/v2/go.sum index 819af0aca15..9389960eadd 100644 --- a/client/v2/go.sum +++ b/client/v2/go.sum @@ -785,6 +785,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.1 h1:Pip65OOl4iJ84WTpA4BKChvOufMhhbxED3BaihoZN4c= @@ -864,6 +865,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -934,6 +936,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1089,6 +1092,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From d0c4b434d4f1411e1ab8934f8936ea1b23154af1 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 4 Oct 2023 17:38:07 +0200 Subject: [PATCH 3/3] add tests for from flag --- client/v2/autocli/flag/builder.go | 11 ++++--- client/v2/autocli/msg.go | 13 ++++++++ client/v2/autocli/msg_test.go | 52 +++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/client/v2/autocli/flag/builder.go b/client/v2/autocli/flag/builder.go index f0accc2e097..6c1dbe3b148 100644 --- a/client/v2/autocli/flag/builder.go +++ b/client/v2/autocli/flag/builder.go @@ -93,7 +93,7 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m } fields := messageType.Descriptor().Fields() - signerFieldName := getSignerFieldName(messageType.Descriptor()) + signerFieldName := GetSignerFieldName(messageType.Descriptor()) isPositional := map[string]bool{} @@ -200,8 +200,9 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m flagOptsByFlagName := map[string]*autocliv1.FlagOptions{} for i := 0; i < fields.Len(); i++ { field := fields.Get(i) - // skips positional args and signer field - if isPositional[string(field.Name())] || string(field.Name()) == signerFieldName { + // skips positional args and signer field if already set + if isPositional[string(field.Name())] || + (string(field.Name()) == signerFieldName && messageBinder.SignerInfo.FieldName == flags.FlagFrom) { continue } @@ -412,9 +413,9 @@ func (b *Builder) resolveFlagTypeBasic(field protoreflect.FieldDescriptor) Type } } -// getSignerFieldName gets signer field name of a message. +// GetSignerFieldName gets signer field name of a message. // AutoCLI supports only one signer field per message. -func getSignerFieldName(descriptor protoreflect.MessageDescriptor) string { +func GetSignerFieldName(descriptor protoreflect.MessageDescriptor) string { signersFields := proto.GetExtension(descriptor.Options(), msgv1.E_Signer).([]string) if len(signersFields) == 0 { return "" diff --git a/client/v2/autocli/msg.go b/client/v2/autocli/msg.go index a2bd5eb9051..79cd25b5265 100644 --- a/client/v2/autocli/msg.go +++ b/client/v2/autocli/msg.go @@ -11,6 +11,7 @@ import ( "google.golang.org/protobuf/types/dynamicpb" autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + "cosmossdk.io/client/v2/autocli/flag" "github.com/cosmos/cosmos-sdk/client" clienttx "github.com/cosmos/cosmos-sdk/client/tx" @@ -127,6 +128,18 @@ func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor clientCtx = clientCtx.WithTxConfig(txConfigWithTextual) clientCtx.Output = cmd.OutOrStdout() + // set signer to signer field if empty + fd := input.Descriptor().Fields().ByName(protoreflect.Name(flag.GetSignerFieldName(input.Descriptor()))) + if addr := input.Get(fd).String(); addr == "" { + signerFromFlag := clientCtx.GetFromAddress() + signer, err := b.ClientCtx.AddressCodec.BytesToString(signerFromFlag.Bytes()) + if err != nil { + return fmt.Errorf("failed to set signer on message, got %v: %w", signerFromFlag, err) + } + + input.Set(fd, protoreflect.ValueOfString(signer)) + } + // AutoCLI uses protov2 messages, while the SDK only supports proto v1 messages. // Here we use dynamicpb, to create a proto v1 compatible message. // The SDK codec will handle protov2 -> protov1 (marshal) diff --git a/client/v2/autocli/msg_test.go b/client/v2/autocli/msg_test.go index 86d328aaa4a..96b930c33c5 100644 --- a/client/v2/autocli/msg_test.go +++ b/client/v2/autocli/msg_test.go @@ -19,6 +19,14 @@ var buildModuleMsgCommand = func(moduleName string, b *Builder) (*cobra.Command, return cmd, err } +func buildCustomModuleMsgCommand(cmdDescriptor *autocliv1.ServiceCommandDescriptor) func(moduleName string, b *Builder) (*cobra.Command, error) { + return func(moduleName string, b *Builder) (*cobra.Command, error) { + cmd := topLevelCmd(moduleName, fmt.Sprintf("Transactions commands for the %s module", moduleName)) + err := b.AddMsgServiceCommands(cmd, cmdDescriptor) + return cmd, err + } +} + var bankAutoCLI = &autocliv1.ServiceCommandDescriptor{ Service: bankv1beta1.Msg_ServiceDesc.ServiceName, RpcCommandOptions: []*autocliv1.RpcCommandOptions{ @@ -41,6 +49,50 @@ func TestMsg(t *testing.T) { ) assert.NilError(t, err) golden.Assert(t, out.String(), "msg-output.golden") + + out, err = runCmd(fixture.conn, fixture.b, buildCustomModuleMsgCommand(&autocliv1.ServiceCommandDescriptor{ + Service: bankv1beta1.Msg_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "Send", + Use: "send [from_key_or_address] [to_address] [amount] [flags]", + Short: "Send coins from one account to another", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "to_address"}, {ProtoField: "amount"}}, + // from_address should be automatically added + }, + }, + EnhanceCustomCommand: true, + }), "send", + "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", "1foo", + "--from", "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", + "--generate-only", + "--output", "json", + ) + assert.NilError(t, err) + golden.Assert(t, out.String(), "msg-output.golden") + + out, err = runCmd(fixture.conn, fixture.b, buildCustomModuleMsgCommand(&autocliv1.ServiceCommandDescriptor{ + Service: bankv1beta1.Msg_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "Send", + Use: "send [from_key_or_address] [to_address] [amount] [flags]", + Short: "Send coins from one account to another", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "to_address"}, {ProtoField: "amount"}}, + FlagOptions: map[string]*autocliv1.FlagOptions{ + "from_address": {Name: "sender"}, // use a custom flag for signer + }, + }, + }, + EnhanceCustomCommand: true, + }), "send", + "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", "1foo", + "--sender", "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", + "--generate-only", + "--output", "json", + ) + assert.NilError(t, err) + golden.Assert(t, out.String(), "msg-output.golden") } func TestMsgOptionsError(t *testing.T) {