feat(client/v2): get keyring from context (backport #19646) (#20727)

Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
mergify[bot] 2024-06-19 20:39:31 +00:00 committed by GitHub
parent 5db395bcca
commit 5d349c2661
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 272 additions and 173 deletions

View File

@ -434,14 +434,6 @@ jobs:
run: |
cd simapp
go test -mod=readonly -timeout 30m -tags='app_v1 norace ledger test_ledger_mock rocksdb_build' ./...
- name: sonarcloud
if: ${{ env.GIT_DIFF && !github.event.pull_request.draft && env.SONAR_TOKEN != null }}
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
projectBaseDir: simapp/
test-collections:
runs-on: ubuntu-latest

View File

@ -36,16 +36,21 @@ Ref: https://keepachangelog.com/en/1.0.0/
## [Unreleased]
## [v2.0.0-beta.2] - 2024-XX-XX
## [v2.0.0-beta.2] - 2024-06-19
### Features
* [#19039](https://github.com/cosmos/cosmos-sdk/pull/19039) Add support for pubkey in autocli.
### Improvements
* [#19646](https://github.com/cosmos/cosmos-sdk/pull/19646) Use keyring from command context.
* (deps) [#19810](https://github.com/cosmos/cosmos-sdk/pull/19810) Upgrade SDK version due to prometheus breaking change.
* (deps) [#19810](https://github.com/cosmos/cosmos-sdk/pull/19810) Bump `cosmossdk.io/store` to v1.1.0.
* [#20083](https://github.com/cosmos/cosmos-sdk/pull/20083) Integrate latest version of cosmos-proto and improve version filtering.
* [#19618](https://github.com/cosmos/cosmos-sdk/pull/19618) Marshal enum as string in queries.
* [#19060](https://github.com/cosmos/cosmos-sdk/pull/19060) Use client context from root (or enhanced) command in autocli commands.
* Note, the given command must have a `client.Context` in its context.
* Note, the given command must have a `client.Context` in its context.
* [#19216](https://github.com/cosmos/cosmos-sdk/pull/19216) Do not overwrite TxConfig, use directly the one provided in context. TxConfig should always be set in the `client.Context` in `root.go` of an app.
* [#20266](https://github.com/cosmos/cosmos-sdk/pull/20266) Add ability to override the short description in AutoCLI-generated top-level commands.
@ -56,6 +61,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [#19060](https://github.com/cosmos/cosmos-sdk/pull/19060) Simplify key flag parsing logic in flag handler.
* [#20033](https://github.com/cosmos/cosmos-sdk/pull/20033) Respect output format from client ctx.
### API Breaking Changes
* [#19646](https://github.com/cosmos/cosmos-sdk/pull/19646) Remove keyring from `autocli.AppOptions` and `flag.Builder` options.
## [v2.0.0-beta.1] - 2023-11-07
This is the first tagged version of client/v2.

View File

@ -75,10 +75,10 @@ if err := rootCmd.Execute(); err != nil {
### 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.
`autocli` uses a keyring for key name resolving names and signing transactions.
:::tip
This provides a better UX as it allows to resolve key names directly from the keyring in all transactions and commands.
AutoCLI provides a better UX than normal CLI as it allows to resolve key names directly from the keyring in all transactions and commands.
```sh
<appd> q bank balances alice
@ -87,8 +87,9 @@ 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 keyring should be provided in the `appOptions` struct as follows, and can be gotten from the client context:
The keyring used for resolving names and signing transactions is provided via the `client.Context`.
The keyring is then converted to the `client/v2/autocli/keyring` interface.
If no keyring is provided, the `autocli` generated command will not be able to sign transactions, but will still be able to query the chain.
:::tip
The Cosmos SDK keyring and Hubl keyring both implement the `client/v2/autocli/keyring` interface, thanks to the following wrapper:
@ -99,18 +100,6 @@ keyring.NewAutoCLIKeyring(kb)
:::
:::warning
When using AutoCLI the keyring will only be created once and before any command flag parsing.
:::
```go
// Set the keyring in the appOptions
appOptions.Keyring = keyring
err := autoCliOpts.EnhanceRootCommand(rootCmd)
...
```
## Signing
`autocli` supports signing transactions with the keyring.

View File

@ -7,7 +7,6 @@ import (
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
"cosmossdk.io/client/v2/autocli/flag"
"cosmossdk.io/client/v2/autocli/keyring"
"cosmossdk.io/core/address"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/depinject"
@ -42,9 +41,6 @@ type AppOptions struct {
ValidatorAddressCodec runtime.ValidatorAddressCodec
ConsensusAddressCodec runtime.ConsensusAddressCodec
// 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
}
@ -72,7 +68,6 @@ func (appOptions AppOptions) EnhanceRootCommand(rootCmd *cobra.Command) error {
AddressCodec: appOptions.AddressCodec,
ValidatorAddressCodec: appOptions.ValidatorAddressCodec,
ConsensusAddressCodec: appOptions.ConsensusAddressCodec,
Keyring: appOptions.Keyring,
},
GetClientConn: func(cmd *cobra.Command) (grpc.ClientConnInterface, error) {
return client.GetClientQueryContext(cmd)

View File

@ -2,6 +2,7 @@ package autocli
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"google.golang.org/protobuf/reflect/protoreflect"
@ -52,14 +53,18 @@ func (b *Builder) buildMethodCommandCommon(descriptor protoreflect.MethodDescrip
Version: options.Version,
}
binder, err := b.AddMessageFlags(cmd.Context(), cmd.Flags(), inputType, options)
// we need to use a pointer to the context as the correct context is set in the RunE function
// however we need to set the flags before the RunE function is called
ctx := cmd.Context()
binder, err := b.AddMessageFlags(&ctx, cmd.Flags(), inputType, options)
if err != nil {
return nil, err
}
cmd.Args = binder.CobraArgs
cmd.RunE = func(cmd *cobra.Command, args []string) error {
ctx = cmd.Context()
input, err := binder.BuildMessage(args)
if err != nil {
return err
@ -67,23 +72,16 @@ func (b *Builder) buildMethodCommandCommon(descriptor protoreflect.MethodDescrip
// 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)
// marks the custom flag as required.
if binder.SignerInfo.FlagName != flags.FlagFrom {
if err := cmd.MarkFlagRequired(binder.SignerInfo.FlagName); err != nil {
return err
}
if err := cmd.Flags().Set(flags.FlagFrom, signer); err != nil {
if err := cmd.Flags().Set(flags.FlagFrom, cmd.Flag(binder.SignerInfo.FlagName).Value.String()); err != nil {
return err
}
}
@ -251,6 +249,6 @@ func (b *Builder) outOrStdoutFormat(cmd *cobra.Command, out []byte) error {
}
}
_, err = fmt.Fprintln(cmd.OutOrStdout(), string(out))
return err
cmd.Println(strings.TrimSpace(string(out)))
return nil
}

View File

@ -55,9 +55,6 @@ func initFixture(t *testing.T) *fixture {
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()
banktypes.RegisterInterfaces(interfaceRegistry)
@ -76,7 +73,6 @@ func initFixture(t *testing.T) *fixture {
Builder: flag.Builder{
TypeResolver: protoregistry.GlobalTypes,
FileResolver: protoregistry.GlobalFiles,
Keyring: akr,
AddressCodec: addresscodec.NewBech32Codec("cosmos"),
ValidatorAddressCodec: addresscodec.NewBech32Codec("cosmosvaloper"),
ConsensusAddressCodec: addresscodec.NewBech32Codec("cosmosvalcons"),

View File

@ -9,16 +9,18 @@ import (
"cosmossdk.io/client/v2/autocli/keyring"
"cosmossdk.io/core/address"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
sdkkeyring "github.com/cosmos/cosmos-sdk/crypto/keyring"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
)
type addressStringType struct{}
func (a addressStringType) NewValue(_ context.Context, b *Builder) Value {
return &addressValue{addressCodec: b.AddressCodec, keyring: b.Keyring}
func (a addressStringType) NewValue(ctx *context.Context, b *Builder) Value {
return &addressValue{addressCodec: b.AddressCodec, ctx: ctx}
}
func (a addressStringType) DefaultValue() string {
@ -27,8 +29,8 @@ func (a addressStringType) DefaultValue() string {
type validatorAddressStringType struct{}
func (a validatorAddressStringType) NewValue(_ context.Context, b *Builder) Value {
return &addressValue{addressCodec: b.ValidatorAddressCodec, keyring: b.Keyring}
func (a validatorAddressStringType) NewValue(ctx *context.Context, b *Builder) Value {
return &addressValue{addressCodec: b.ValidatorAddressCodec, ctx: ctx}
}
func (a validatorAddressStringType) DefaultValue() string {
@ -36,9 +38,10 @@ func (a validatorAddressStringType) DefaultValue() string {
}
type addressValue struct {
value string
ctx *context.Context
addressCodec address.Codec
keyring keyring.Keyring
value string
}
func (a addressValue) Get(protoreflect.Value) (protoreflect.Value, error) {
@ -51,7 +54,9 @@ func (a addressValue) String() string {
// Set implements the flag.Value interface for addressValue.
func (a *addressValue) Set(s string) error {
addr, err := a.keyring.LookupAddressByKeyName(s)
// we get the keyring on set, as in NewValue the context is the parent context (before RunE)
keyring := getKeyringFromCtx(a.ctx)
addr, err := keyring.LookupAddressByKeyName(s)
if err == nil {
addrStr, err := a.addressCodec.BytesToString(addr)
if err != nil {
@ -62,9 +67,11 @@ func (a *addressValue) Set(s string) error {
return nil
}
// failed all validation, just accept the input.
// TODO(@julienrbrt), for final client/v2 2.0.0 revert the logic and
// do a better keyring instantiation.
_, err = a.addressCodec.StringToBytes(s)
if err != nil {
return fmt.Errorf("invalid account address or key name: %w", err)
}
a.value = s
return nil
@ -76,11 +83,11 @@ func (a addressValue) Type() string {
type consensusAddressStringType struct{}
func (a consensusAddressStringType) NewValue(ctx context.Context, b *Builder) Value {
func (a consensusAddressStringType) NewValue(ctx *context.Context, b *Builder) Value {
return &consensusAddressValue{
addressValue: addressValue{
addressCodec: b.ConsensusAddressCodec,
keyring: b.Keyring,
ctx: ctx,
},
}
}
@ -102,7 +109,9 @@ func (a consensusAddressValue) String() string {
}
func (a *consensusAddressValue) Set(s string) error {
addr, err := a.keyring.LookupAddressByKeyName(s)
// we get the keyring on set, as in NewValue the context is the parent context (before RunE)
keyring := getKeyringFromCtx(a.ctx)
addr, err := keyring.LookupAddressByKeyName(s)
if err == nil {
addrStr, err := a.addressCodec.BytesToString(addr)
if err != nil {
@ -127,11 +136,7 @@ func (a *consensusAddressValue) Set(s string) error {
var pk cryptotypes.PubKey
err2 := cdc.UnmarshalInterfaceJSON([]byte(s), &pk)
if err2 != nil {
// failed all validation, just accept the input.
// TODO(@julienrbrt), for final client/v2 2.0.0 revert the logic and
// do a better keyring instantiation.
a.value = s
return nil
return fmt.Errorf("input isn't a pubkey (%w) or is an invalid account address (%w)", err, err2)
}
a.value, err = a.addressCodec.BytesToString(pk.Address())
@ -141,3 +146,21 @@ func (a *consensusAddressValue) Set(s string) error {
return nil
}
func getKeyringFromCtx(ctx *context.Context) keyring.Keyring {
dctx := *ctx
if dctx != nil {
if clientCtx := dctx.Value(client.ClientContextKey); clientCtx != nil {
k, err := sdkkeyring.NewAutoCLIKeyring(clientCtx.(*client.Context).Keyring)
if err != nil {
panic(fmt.Errorf("failed to create keyring: %w", err))
}
return k
} else if k := dctx.Value(keyring.KeyringContextKey); k != nil {
return k.(*keyring.KeyringImpl)
}
}
return keyring.NoKeyring{}
}

View File

@ -14,7 +14,7 @@ type binaryType struct{}
var _ Value = (*fileBinaryValue)(nil)
func (f binaryType) NewValue(context.Context, *Builder) Value {
func (f binaryType) NewValue(*context.Context, *Builder) Value {
return &fileBinaryValue{}
}

View File

@ -16,7 +16,6 @@ import (
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"
@ -28,6 +27,7 @@ const (
AddressStringScalarType = "cosmos.AddressString"
ValidatorAddressStringScalarType = "cosmos.ValidatorAddressString"
ConsensusAddressStringScalarType = "cosmos.ConsensusAddressString"
PubkeyScalarType = "cosmos.Pubkey"
)
// Builder manages options for building pflag flags for protobuf messages.
@ -49,9 +49,6 @@ type Builder struct {
messageFlagTypes map[protoreflect.FullName]Type
scalarFlagTypes map[string]Type
// Keyring is the keyring to use for client/v2.
Keyring keyring.Keyring
// Address Codecs are the address codecs to use for client/v2.
AddressCodec address.Codec
ValidatorAddressCodec runtime.ValidatorAddressCodec
@ -71,6 +68,7 @@ func (b *Builder) init() {
b.scalarFlagTypes[AddressStringScalarType] = addressStringType{}
b.scalarFlagTypes[ValidatorAddressStringScalarType] = validatorAddressStringType{}
b.scalarFlagTypes[ConsensusAddressStringScalarType] = consensusAddressStringType{}
b.scalarFlagTypes[PubkeyScalarType] = pubkeyType{}
}
}
@ -90,10 +88,6 @@ func (b *Builder) ValidateAndComplete() error {
return errors.New("consensus address codec is required in flag builder")
}
if b.Keyring == nil {
b.Keyring = keyring.NoKeyring{}
}
if b.TypeResolver == nil {
return errors.New("type resolver is required in flag builder")
}
@ -118,12 +112,12 @@ func (b *Builder) DefineScalarFlagType(scalarName string, flagType Type) {
}
// 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) {
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.
func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, messageType protoreflect.MessageType, commandOptions *autocliv1.RpcCommandOptions, options namingOptions) (*MessageBinder, error) {
func (b *Builder) addMessageFlags(ctx *context.Context, flagSet *pflag.FlagSet, messageType protoreflect.MessageType, commandOptions *autocliv1.RpcCommandOptions, options namingOptions) (*MessageBinder, error) {
messageBinder := &MessageBinder{
messageType: messageType,
// positional args are also parsed using a FlagSet so that we can reuse all the same parsers
@ -135,7 +129,7 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m
isPositional := map[string]bool{}
lengthPositionalArgsOptions := len(commandOptions.PositionalArgs)
positionalArgsLen := len(commandOptions.PositionalArgs)
for i, arg := range commandOptions.PositionalArgs {
isPositional[arg.ProtoField] = true
@ -147,17 +141,12 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m
}
}
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())
}
if arg.Optional && arg.Varargs {
return nil, fmt.Errorf("positional argument %s can't be both optional and varargs", arg.ProtoField)
}
if arg.Varargs {
if i != lengthPositionalArgsOptions-1 {
if i != positionalArgsLen-1 {
return nil, fmt.Errorf("varargs positional argument %s must be the last argument", arg.ProtoField)
}
@ -165,13 +154,18 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m
}
if arg.Optional {
if i != lengthPositionalArgsOptions-1 {
if i != positionalArgsLen-1 {
return nil, fmt.Errorf("optional positional argument %s must be the last argument", arg.ProtoField)
}
messageBinder.hasOptional = true
}
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())
}
_, hasValue, err := b.addFieldFlag(
ctx,
messageBinder.positionalFlagSet,
@ -189,19 +183,20 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m
})
}
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 {
messageBinder.CobraArgs = cobra.ExactArgs(lengthPositionalArgsOptions)
messageBinder.mandatoryArgUntil = lengthPositionalArgsOptions
switch {
case messageBinder.hasVarargs:
messageBinder.CobraArgs = cobra.MinimumNArgs(positionalArgsLen - 1)
messageBinder.mandatoryArgUntil = positionalArgsLen - 1
case messageBinder.hasOptional:
messageBinder.CobraArgs = cobra.RangeArgs(positionalArgsLen-1, positionalArgsLen)
messageBinder.mandatoryArgUntil = positionalArgsLen - 1
default:
messageBinder.CobraArgs = cobra.ExactArgs(positionalArgsLen)
messageBinder.mandatoryArgUntil = positionalArgsLen
}
// validate flag options
for name := range commandOptions.FlagOptions {
for name, opts := range commandOptions.FlagOptions {
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())
}
@ -210,14 +205,15 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m
if name == signerFieldName {
messageBinder.SignerInfo = SignerInfo{
FieldName: name,
IsFlag: false,
IsFlag: true,
FlagName: opts.Name,
}
}
}
// 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 signerFieldName != "" && messageBinder.SignerInfo == (SignerInfo{}) {
if commandOptions.FlagOptions == nil {
commandOptions.FlagOptions = make(map[string]*autocliv1.FlagOptions)
}
@ -229,8 +225,9 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m
}
messageBinder.SignerInfo = SignerInfo{
FieldName: flags.FlagFrom,
FieldName: signerFieldName,
IsFlag: true,
FlagName: flags.FlagFrom,
}
}
@ -238,13 +235,15 @@ 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)
fieldName := string(field.Name())
// skips positional args and signer field if already set
if isPositional[string(field.Name())] ||
(string(field.Name()) == signerFieldName && messageBinder.SignerInfo.FieldName == flags.FlagFrom) {
if isPositional[fieldName] ||
(fieldName == signerFieldName && messageBinder.SignerInfo.FlagName == flags.FlagFrom) {
continue
}
flagOpts := commandOptions.FlagOptions[string(field.Name())]
flagOpts := commandOptions.FlagOptions[fieldName]
name, hasValue, err := b.addFieldFlag(ctx, flagSet, field, flagOpts, options)
flagOptsByFlagName[name] = flagOpts
if err != nil {
@ -274,7 +273,7 @@ func (b *Builder) addMessageFlags(ctx context.Context, flagSet *pflag.FlagSet, m
}
// bindPageRequest create a flag for pagination
func (b *Builder) bindPageRequest(ctx context.Context, flagSet *pflag.FlagSet, field protoreflect.FieldDescriptor) (HasValue, error) {
func (b *Builder) bindPageRequest(ctx *context.Context, flagSet *pflag.FlagSet, field protoreflect.FieldDescriptor) (HasValue, error) {
return b.addMessageFlags(
ctx,
flagSet,
@ -291,7 +290,7 @@ type namingOptions struct {
}
// addFieldFlag adds a flag for the provided field to the flag set.
func (b *Builder) addFieldFlag(ctx context.Context, flagSet *pflag.FlagSet, field protoreflect.FieldDescriptor, opts *autocliv1.FlagOptions, options namingOptions) (name string, hasValue HasValue, err error) {
func (b *Builder) addFieldFlag(ctx *context.Context, flagSet *pflag.FlagSet, field protoreflect.FieldDescriptor, opts *autocliv1.FlagOptions, options namingOptions) (name string, hasValue HasValue, err error) {
if opts == nil {
opts = &autocliv1.FlagOptions{}
}

View File

@ -17,7 +17,7 @@ type coinValue struct {
value *basev1beta1.Coin
}
func (c coinType) NewValue(context.Context, *Builder) Value {
func (c coinType) NewValue(*context.Context, *Builder) Value {
return &coinValue{}
}

View File

@ -10,7 +10,7 @@ import (
type durationType struct{}
func (d durationType) NewValue(context.Context, *Builder) Value {
func (d durationType) NewValue(*context.Context, *Builder) Value {
return &durationValue{}
}

View File

@ -14,7 +14,7 @@ type enumType struct {
enum protoreflect.EnumDescriptor
}
func (b enumType) NewValue(context.Context, *Builder) Value {
func (b enumType) NewValue(*context.Context, *Builder) Value {
val := &enumValue{
enum: b.enum,
valMap: map[string]protoreflect.EnumValueDescriptor{},

View File

@ -9,9 +9,8 @@ import (
// Type specifies a custom flag type.
type Type interface {
// NewValue returns a new pflag.Value which must also implement either
// SimpleValue or ListValue.
NewValue(context.Context, *Builder) Value
// NewValue returns a new pflag.Value which must also implement either SimpleValue or ListValue.
NewValue(*context.Context, *Builder) Value
// DefaultValue is the default value for this type.
DefaultValue() string

View File

@ -20,7 +20,7 @@ type jsonMessageFlagType struct {
messageDesc protoreflect.MessageDescriptor
}
func (j jsonMessageFlagType) NewValue(_ context.Context, builder *Builder) Value {
func (j jsonMessageFlagType) NewValue(_ *context.Context, builder *Builder) Value {
return &jsonMessageFlagValue{
messageType: util.ResolveMessageType(builder.TypeResolver, j.messageDesc),
jsonMarshalOptions: protojson.MarshalOptions{Resolver: builder.TypeResolver},

View File

@ -52,7 +52,7 @@ type compositeListType struct {
simpleType Type
}
func (t compositeListType) NewValue(ctx context.Context, opts *Builder) Value {
func (t compositeListType) NewValue(ctx *context.Context, opts *Builder) Value {
return &compositeListValue{
simpleType: t.simpleType,
values: nil,
@ -68,7 +68,7 @@ func (t compositeListType) DefaultValue() string {
type compositeListValue struct {
simpleType Type
values []protoreflect.Value
ctx context.Context
ctx *context.Context
opts *Builder
}

View File

@ -180,7 +180,7 @@ type compositeMapValue[T comparable] struct {
keyType string
valueType Type
values map[T]protoreflect.Value
ctx context.Context
ctx *context.Context
opts *Builder
}
@ -188,7 +188,7 @@ func (m compositeMapType[T]) DefaultValue() string {
return ""
}
func (m compositeMapType[T]) NewValue(ctx context.Context, opts *Builder) Value {
func (m compositeMapType[T]) NewValue(ctx *context.Context, opts *Builder) Value {
return &compositeMapValue[T]{
keyValueResolver: m.keyValueResolver,
valueType: m.valueType,

View File

@ -15,7 +15,9 @@ import (
type SignerInfo struct {
PositionalArgIndex int
IsFlag bool
FieldName string
FieldName string
FlagName string // flag name (always set if IsFlag is true)
}
// MessageBinder binds multiple flags in a flag set to a protobuf message.
@ -25,12 +27,12 @@ type MessageBinder struct {
positionalFlagSet *pflag.FlagSet
positionalArgs []fieldBinding
flagBindings []fieldBinding
messageType protoreflect.MessageType
hasVarargs bool
hasOptional bool
mandatoryArgUntil int
flagBindings []fieldBinding
messageType protoreflect.MessageType
}
// BuildMessage builds and returns a new message for the bound flags.

View File

@ -0,0 +1,60 @@
package flag
import (
"context"
"fmt"
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
)
type pubkeyType struct{}
func (a pubkeyType) NewValue(_ *context.Context, _ *Builder) Value {
return &pubkeyValue{}
}
func (a pubkeyType) DefaultValue() string {
return ""
}
type pubkeyValue struct {
value *types.Any
}
func (a pubkeyValue) Get(protoreflect.Value) (protoreflect.Value, error) {
return protoreflect.ValueOf(a.value), nil
}
func (a pubkeyValue) String() string {
return a.value.String()
}
func (a *pubkeyValue) Set(s string) error {
registry := types.NewInterfaceRegistry()
cryptocodec.RegisterInterfaces(registry)
cdc := codec.NewProtoCodec(registry)
var pk cryptotypes.PubKey
err := cdc.UnmarshalInterfaceJSON([]byte(s), &pk)
if err != nil {
return fmt.Errorf("input isn't a pubkey: %w", err)
}
any, err := types.NewAnyWithValue(pk)
if err != nil {
return fmt.Errorf("error converting to any type")
}
a.value = any
return nil
}
func (a pubkeyValue) Type() string {
return "pubkey"
}

View File

@ -10,7 +10,7 @@ import (
type timestampType struct{}
func (t timestampType) NewValue(context.Context, *Builder) Value {
func (t timestampType) NewValue(*context.Context, *Builder) Value {
return &timestampValue{}
}

View File

@ -0,0 +1,48 @@
package keyring
import (
"context"
signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
"github.com/cosmos/cosmos-sdk/crypto/types"
)
// KeyringContextKey is the key used to store the keyring in the context.
// The keyring must be wrapped using the KeyringImpl.
var KeyringContextKey struct{}
var _ Keyring = &KeyringImpl{}
type KeyringImpl struct { //nolint:revive // stuttering is fine
k Keyring
}
// NewKeyringInContext returns a new context with the keyring set.
func NewKeyringInContext(ctx context.Context, k Keyring) context.Context {
return context.WithValue(ctx, KeyringContextKey, NewKeyringImpl(k))
}
func NewKeyringImpl(k Keyring) *KeyringImpl {
return &KeyringImpl{k: k}
}
// GetPubKey implements Keyring.
func (k *KeyringImpl) GetPubKey(name string) (types.PubKey, error) {
return k.k.GetPubKey(name)
}
// List implements Keyring.
func (k *KeyringImpl) List() ([]string, error) {
return k.k.List()
}
// LookupAddressByKeyName implements Keyring.
func (k *KeyringImpl) LookupAddressByKeyName(name string) ([]byte, error) {
return k.k.LookupAddressByKeyName(name)
}
// Sign implements Keyring.
func (k *KeyringImpl) Sign(name string, msg []byte, signMode signingv1beta1.SignMode) ([]byte, error) {
return k.k.Sign(name, msg, signMode)
}

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"github.com/cockroachdb/errors"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
@ -60,7 +59,7 @@ func (b *Builder) AddMsgServiceCommands(cmd *cobra.Command, cmdDescriptor *autoc
descriptor, err := b.FileResolver.FindDescriptorByName(protoreflect.FullName(cmdDescriptor.Service))
if err != nil {
return errors.Errorf("can't find service %s: %v", cmdDescriptor.Service, err)
return fmt.Errorf("can't find service %s: %w", cmdDescriptor.Service, err)
}
service := descriptor.(protoreflect.ServiceDescriptor)
methods := service.Methods()
@ -102,9 +101,7 @@ func (b *Builder) AddMsgServiceCommands(cmd *cobra.Command, cmdDescriptor *autoc
continue
}
if methodCmd != nil {
cmd.AddCommand(methodCmd)
}
cmd.AddCommand(methodCmd)
}
return nil
@ -112,7 +109,7 @@ 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) {
cmd, err := b.buildMethodCommandCommon(descriptor, options, func(cmd *cobra.Command, input protoreflect.Message) error {
execFunc := func(cmd *cobra.Command, input protoreflect.Message) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
@ -121,11 +118,11 @@ func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor
clientCtx = clientCtx.WithCmdContext(cmd.Context())
clientCtx = clientCtx.WithOutput(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 == "" {
addressCodec := b.Builder.AddressCodec
addressCodec := b.Builder.AddressCodec
// set signer to signer field if empty
if addr := input.Get(fd).String(); addr == "" {
scalarType, ok := flag.GetScalarType(fd)
if ok {
// override address codec if validator or consensus address
@ -153,16 +150,19 @@ func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor
proto.Merge(msg, input.Interface())
return clienttx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
})
}
cmd, err := b.buildMethodCommandCommon(descriptor, options, execFunc)
if err != nil {
return nil, err
}
if b.AddTxConnFlags != nil {
b.AddTxConnFlags(cmd)
}
// silence usage only for inner txs & queries commands
if cmd != nil {
cmd.SilenceUsage = true
}
cmd.SilenceUsage = true
return cmd, err
return cmd, nil
}

View File

@ -55,6 +55,25 @@ func TestMsg(t *testing.T) {
assert.NilError(t, err)
golden.Assert(t, out.String(), "msg-output.golden")
out, err = runCmd(fixture, 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: "from_address"}, {ProtoField: "to_address"}, {ProtoField: "amount"}},
},
},
EnhanceCustomCommand: true,
}), "send",
"cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", "1foo",
"--generate-only",
"--output", "json",
)
assert.NilError(t, err)
golden.Assert(t, out.String(), "msg-output.golden")
out, err = runCmd(fixture, buildCustomModuleMsgCommand(&autocliv1.ServiceCommandDescriptor{
Service: bankv1beta1.Msg_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{

View File

@ -684,7 +684,7 @@ func TestNotFoundErrorsQuery(t *testing.T) {
b.AddQueryConnFlags = nil
b.AddTxConnFlags = nil
buildModuleQueryCommand := func(moduleName string, cmdDescriptor *autocliv1.ServiceCommandDescriptor) (*cobra.Command, error) {
buildModuleQueryCommand := func(_ string, cmdDescriptor *autocliv1.ServiceCommandDescriptor) (*cobra.Command, error) {
cmd := topLevelCmd(context.Background(), "query", "Querying subcommands")
err := b.AddMsgServiceCommands(cmd, cmdDescriptor)
return cmd, err

View File

@ -4,7 +4,7 @@ go 1.21
require (
cosmossdk.io/api v0.7.5
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240124105859-5ad1805d0e79
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240619200301-495e6d11c307
cosmossdk.io/collections v0.4.0 // indirect
cosmossdk.io/core v0.11.0
cosmossdk.io/depinject v1.0.0-alpha.4

View File

@ -186,8 +186,8 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ=
cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38=
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240124105859-5ad1805d0e79 h1:Hr1t0fCq1nbFC7hLs0Xvy9WAiH7Iti5iVLXMM5C37F0=
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240124105859-5ad1805d0e79/go.mod h1:8pN6LSVReNnIxrC2QGcvuIJ/m1pJN6FNYn2kAYtYftI=
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240619200301-495e6d11c307 h1:S9mS1WsJKevoDY7uU5hTMw0R8SBPn0xstKIL6hJrZGc=
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240619200301-495e6d11c307/go.mod h1:8QAyewD7rDWeJGqedFBpeqJ9XLIJAkt1TDhCf1gsN9o=
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.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo=

View File

@ -13,7 +13,6 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/config"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/server"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
@ -100,8 +99,6 @@ func NewRootCmd() *cobra.Command {
// add keyring to autocli opts
autoCliOpts := tempApp.AutoCliOpts()
initClientCtx, _ = config.ReadDefaultValuesFromDefaultClientConfig(initClientCtx)
autoCliOpts.Keyring, _ = keyring.NewAutoCLIKeyring(initClientCtx.Keyring)
autoCliOpts.ClientCtx = initClientCtx
if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {

View File

@ -8,8 +8,6 @@ import (
"github.com/spf13/cobra"
"cosmossdk.io/client/v2/autocli"
clientv2keyring "cosmossdk.io/client/v2/autocli/keyring"
"cosmossdk.io/core/address"
"cosmossdk.io/depinject"
"cosmossdk.io/log"
"cosmossdk.io/simapp"
@ -18,7 +16,6 @@ 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/server"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
"github.com/cosmos/cosmos-sdk/types/module"
@ -43,7 +40,6 @@ func NewRootCmd() *cobra.Command {
),
depinject.Provide(
ProvideClientContext,
ProvideKeyring,
),
),
&autoCliOpts,
@ -121,12 +117,3 @@ func ProvideClientContext(
return clientCtx
}
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 keyring.NewAutoCLIKeyring(kb)
}

View File

@ -1,14 +0,0 @@
sonar.projectKey=cosmos-sdk-simapp
sonar.organization=cosmos
sonar.projectName=Cosmos SDK - SimApp
sonar.project.monorepo.enabled=true
sonar.sources=.
sonar.exclusions=**/*_test.go
sonar.tests=.
sonar.test.inclusions=**/*_test.go
sonar.go.coverage.reportPaths=coverage.out
sonar.sourceEncoding=UTF-8
sonar.scm.provider=git

View File

@ -37,7 +37,7 @@ require (
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.6 // indirect
cloud.google.com/go/storage v1.36.0 // indirect
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240124105859-5ad1805d0e79 // indirect
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240619200301-495e6d11c307 // indirect
cosmossdk.io/collections v0.4.0 // indirect
cosmossdk.io/x/circuit v0.1.1 // indirect
filippo.io/edwards25519 v1.0.0 // indirect

View File

@ -186,8 +186,8 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ=
cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38=
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240124105859-5ad1805d0e79 h1:Hr1t0fCq1nbFC7hLs0Xvy9WAiH7Iti5iVLXMM5C37F0=
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240124105859-5ad1805d0e79/go.mod h1:8pN6LSVReNnIxrC2QGcvuIJ/m1pJN6FNYn2kAYtYftI=
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240619200301-495e6d11c307 h1:S9mS1WsJKevoDY7uU5hTMw0R8SBPn0xstKIL6hJrZGc=
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240619200301-495e6d11c307/go.mod h1:8QAyewD7rDWeJGqedFBpeqJ9XLIJAkt1TDhCf1gsN9o=
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.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo=