feat(client/v2): get keyring from context (#19646)
This commit is contained in:
parent
b48fd66678
commit
ca195c1527
@ -36,7 +36,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
<!-- ## [v2.1.0-beta.1] to be tagged after v0.51 final or in SDK agnostic version -->
|
||||
<!-- ## [v2.1.0-rc.1] to be tagged after v0.51 final or in SDK agnostic version -->
|
||||
|
||||
### Features
|
||||
|
||||
@ -47,6 +47,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### Improvements
|
||||
|
||||
* [#19646](https://github.com/cosmos/cosmos-sdk/pull/19646) Use keyring from command context.
|
||||
* [#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.
|
||||
@ -62,6 +63,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### API Breaking Changes
|
||||
|
||||
* [#19646](https://github.com/cosmos/cosmos-sdk/pull/19646) Remove keyring from `autocli.AppOptions` and `flag.Builder` options.
|
||||
* [#17709](https://github.com/cosmos/cosmos-sdk/pull/17709) Address codecs have been removed from `autocli.AppOptions` and `flag.Builder`. Instead client/v2 uses the address codecs present in the context (introduced in [#17503](https://github.com/cosmos/cosmos-sdk/pull/17503)).
|
||||
|
||||
## [v2.0.0-beta.1] - 2023-11-07
|
||||
|
||||
@ -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.
|
||||
@ -255,7 +244,7 @@ The `encoding` flag lets you choose how the contents of the file should be encod
|
||||
|
||||
* `simd off-chain sign-file alice myFile.json`
|
||||
|
||||
* ```json
|
||||
* ```json
|
||||
{
|
||||
"@type": "/offchain.MsgSignArbitraryData",
|
||||
"appDomain": "simd",
|
||||
@ -266,7 +255,7 @@ The `encoding` flag lets you choose how the contents of the file should be encod
|
||||
|
||||
* `simd off-chain sign-file alice myFile.json --encoding base64`
|
||||
|
||||
* ```json
|
||||
* ```json
|
||||
{
|
||||
"@type": "/offchain.MsgSignArbitraryData",
|
||||
"appDomain": "simd",
|
||||
@ -277,7 +266,7 @@ The `encoding` flag lets you choose how the contents of the file should be encod
|
||||
|
||||
* `simd off-chain sign-file alice myFile.json --encoding hex`
|
||||
|
||||
* ```json
|
||||
* ```json
|
||||
{
|
||||
"@type": "/offchain.MsgSignArbitraryData",
|
||||
"appDomain": "simd",
|
||||
|
||||
@ -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/appmodule"
|
||||
"cosmossdk.io/depinject"
|
||||
|
||||
@ -35,9 +34,6 @@ type AppOptions struct {
|
||||
// module or need to be improved.
|
||||
ModuleOptions map[string]*autocliv1.ModuleOptions `optional:"true"`
|
||||
|
||||
// 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
|
||||
}
|
||||
@ -62,7 +58,6 @@ func (appOptions AppOptions) EnhanceRootCommand(rootCmd *cobra.Command) error {
|
||||
Builder: flag.Builder{
|
||||
TypeResolver: protoregistry.GlobalTypes,
|
||||
FileResolver: appOptions.ClientCtx.InterfaceRegistry,
|
||||
Keyring: appOptions.Keyring,
|
||||
AddressCodec: appOptions.ClientCtx.AddressCodec,
|
||||
ValidatorAddressCodec: appOptions.ClientCtx.ValidatorAddressCodec,
|
||||
ConsensusAddressCodec: appOptions.ClientCtx.ConsensusAddressCodec,
|
||||
|
||||
@ -53,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
|
||||
@ -72,17 +76,12 @@ func (b *Builder) buildMethodCommandCommon(descriptor protoreflect.MethodDescrip
|
||||
// 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.
|
||||
// marks the custom flag as required.
|
||||
if binder.SignerInfo.FieldName != flags.FlagFrom {
|
||||
if err := cmd.MarkFlagRequired(binder.SignerInfo.FieldName); err != nil {
|
||||
if binder.SignerInfo.FlagName != flags.FlagFrom {
|
||||
if err := cmd.MarkFlagRequired(binder.SignerInfo.FlagName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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 {
|
||||
if err := cmd.Flags().Set(flags.FlagFrom, cmd.Flag(binder.SignerInfo.FlagName).Value.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,9 +56,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)
|
||||
|
||||
@ -83,7 +80,6 @@ func initFixture(t *testing.T) *fixture {
|
||||
AddressCodec: clientCtx.AddressCodec,
|
||||
ValidatorAddressCodec: clientCtx.ValidatorAddressCodec,
|
||||
ConsensusAddressCodec: clientCtx.ConsensusAddressCodec,
|
||||
Keyring: akr,
|
||||
},
|
||||
GetClientConn: func(*cobra.Command) (grpc.ClientConnInterface, error) {
|
||||
return conn, nil
|
||||
|
||||
@ -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{}
|
||||
}
|
||||
|
||||
@ -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{}
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
@ -48,9 +47,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 address.ValidatorAddressCodec
|
||||
@ -90,10 +86,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 +110,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 +127,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 +139,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 +152,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 +181,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 +203,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 +223,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 +233,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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -274,7 +271,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 +288,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{}
|
||||
}
|
||||
|
||||
@ -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{}
|
||||
}
|
||||
|
||||
|
||||
@ -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{}
|
||||
}
|
||||
|
||||
|
||||
@ -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{},
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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},
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -14,7 +14,7 @@ import (
|
||||
|
||||
type pubkeyType struct{}
|
||||
|
||||
func (a pubkeyType) NewValue(_ context.Context, _ *Builder) Value {
|
||||
func (a pubkeyType) NewValue(_ *context.Context, _ *Builder) Value {
|
||||
return &pubkeyValue{}
|
||||
}
|
||||
|
||||
|
||||
@ -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 ×tampValue{}
|
||||
}
|
||||
|
||||
|
||||
48
client/v2/autocli/keyring/keyring.go
Normal file
48
client/v2/autocli/keyring/keyring.go
Normal 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 {
|
||||
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)
|
||||
}
|
||||
@ -110,9 +110,7 @@ func (b *Builder) AddMsgServiceCommands(cmd *cobra.Command, cmdDescriptor *autoc
|
||||
continue
|
||||
}
|
||||
|
||||
if methodCmd != nil {
|
||||
cmd.AddCommand(methodCmd)
|
||||
}
|
||||
cmd.AddCommand(methodCmd)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -135,7 +133,7 @@ func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor
|
||||
// handle gov proposals commands
|
||||
skipProposal, _ := cmd.Flags().GetBool(flags.FlagNoProposal)
|
||||
if options.GovProposal && !skipProposal {
|
||||
return b.handleGovProposal(options, cmd, input, clientCtx, addressCodec, fd)
|
||||
return b.handleGovProposal(cmd, input, clientCtx, addressCodec, fd)
|
||||
}
|
||||
|
||||
// set signer to signer field if empty
|
||||
@ -179,9 +177,7 @@ func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor
|
||||
}
|
||||
|
||||
// silence usage only for inner txs & queries commands
|
||||
if cmd != nil {
|
||||
cmd.SilenceUsage = true
|
||||
}
|
||||
cmd.SilenceUsage = true
|
||||
|
||||
// set gov proposal flags if command is a gov proposal
|
||||
if options.GovProposal {
|
||||
@ -194,7 +190,6 @@ func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor
|
||||
|
||||
// handleGovProposal sets the authority field of the message to the gov module address and creates a gov proposal.
|
||||
func (b *Builder) handleGovProposal(
|
||||
options *autocliv1.RpcCommandOptions,
|
||||
cmd *cobra.Command,
|
||||
input protoreflect.Message,
|
||||
clientCtx client.Context,
|
||||
|
||||
@ -59,6 +59,25 @@ func TestMsg(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
assertNormalizedJSONEqual(t, out.Bytes(), goldenLoad(t, "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)
|
||||
assertNormalizedJSONEqual(t, out.Bytes(), goldenLoad(t, "msg-output.golden"))
|
||||
|
||||
out, err = runCmd(fixture, buildCustomModuleMsgCommand(&autocliv1.ServiceCommandDescriptor{
|
||||
Service: bankv1beta1.Msg_ServiceDesc.ServiceName,
|
||||
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
|
||||
|
||||
@ -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
|
||||
|
||||
@ -19,7 +19,6 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/config"
|
||||
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"
|
||||
@ -121,7 +120,6 @@ func NewRootCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
autoCliOpts := tempApp.AutoCliOpts()
|
||||
autoCliOpts.Keyring, _ = keyring.NewAutoCLIKeyring(initClientCtx.Keyring)
|
||||
autoCliOpts.ClientCtx = initClientCtx
|
||||
|
||||
if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
|
||||
|
||||
@ -10,7 +10,6 @@ import (
|
||||
authv1 "cosmossdk.io/api/cosmos/auth/module/v1"
|
||||
stakingv1 "cosmossdk.io/api/cosmos/staking/module/v1"
|
||||
"cosmossdk.io/client/v2/autocli"
|
||||
clientv2keyring "cosmossdk.io/client/v2/autocli/keyring"
|
||||
"cosmossdk.io/core/address"
|
||||
"cosmossdk.io/core/legacy"
|
||||
"cosmossdk.io/depinject"
|
||||
@ -24,7 +23,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"
|
||||
@ -46,7 +44,6 @@ func NewRootCmd() *cobra.Command {
|
||||
),
|
||||
depinject.Provide(
|
||||
ProvideClientContext,
|
||||
ProvideKeyring,
|
||||
),
|
||||
),
|
||||
&autoCliOpts,
|
||||
@ -146,12 +143,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)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user