diff --git a/client/v2/autocli/app.go b/client/v2/autocli/app.go index 833332b111..0172b4c3e8 100644 --- a/client/v2/autocli/app.go +++ b/client/v2/autocli/app.go @@ -1,6 +1,8 @@ package autocli import ( + "errors" + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" "cosmossdk.io/client/v2/autocli/flag" "cosmossdk.io/core/address" @@ -19,8 +21,8 @@ import ( // var autoCliOpts autocli.AppOptions // err := depinject.Inject(appConfig, &encodingConfig.InterfaceRegistry, &autoCliOpts) // -// If depinject isn't used, options can be provided manually or extracted from modules. One method for extracting autocli -// options is via the github.com/cosmos/cosmos-sdk/runtime/services.ExtractAutoCLIOptions function. +// If depinject isn't used, options can be provided manually or extracted from modules and the address codec can be provided by the auth keeper. +// One method for extracting autocli options is via the github.com/cosmos/cosmos-sdk/runtime/services.ExtractAutoCLIOptions function. type AppOptions struct { depinject.In @@ -34,8 +36,7 @@ type AppOptions struct { ModuleOptions map[string]*autocliv1.ModuleOptions `optional:"true"` // AddressCodec is the address codec to use for the app. - // If not provided the default address prefix will be fetched from the reflection client. - AddressCodec address.Codec `optional:"true"` + AddressCodec address.Codec } // EnhanceRootCommand enhances the provided root command with autocli AppOptions, @@ -57,9 +58,6 @@ func (appOptions AppOptions) EnhanceRootCommand(rootCmd *cobra.Command) error { builder := &Builder{ Builder: flag.Builder{ AddressCodec: appOptions.AddressCodec, - GetClientConn: func() (grpc.ClientConnInterface, error) { - return client.GetClientQueryContext(rootCmd) - }, }, GetClientConn: func(cmd *cobra.Command) (grpc.ClientConnInterface, error) { return client.GetClientQueryContext(cmd) @@ -72,6 +70,10 @@ func (appOptions AppOptions) EnhanceRootCommand(rootCmd *cobra.Command) error { } func (appOptions AppOptions) EnhanceRootCommandWithBuilder(rootCmd *cobra.Command, builder *Builder) error { + if builder.AddressCodec == nil { + return errors.New("address codec is required in builder") + } + // extract any custom commands from modules customQueryCmds, customMsgCmds := map[string]*cobra.Command{}, map[string]*cobra.Command{} for name, module := range appOptions.Modules { diff --git a/client/v2/autocli/common_test.go b/client/v2/autocli/common_test.go index 98730c3f99..4f605a4f48 100644 --- a/client/v2/autocli/common_test.go +++ b/client/v2/autocli/common_test.go @@ -6,15 +6,17 @@ import ( "net" "testing" - reflectionv2alpha1 "cosmossdk.io/api/cosmos/base/reflection/v2alpha1" - "cosmossdk.io/client/v2/autocli/flag" - "cosmossdk.io/client/v2/internal/testpb" - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/spf13/cobra" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "gotest.tools/v3/assert" - "google.golang.org/grpc/credentials/insecure" + reflectionv2alpha1 "cosmossdk.io/api/cosmos/base/reflection/v2alpha1" + "github.com/cosmos/cosmos-sdk/client/flags" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + + "cosmossdk.io/client/v2/autocli/flag" + "cosmossdk.io/client/v2/internal/testpb" ) func testExecCommon(t *testing.T, buildModuleCommand func(string, *Builder) (*cobra.Command, error), args ...string) *testClientConn { @@ -47,9 +49,7 @@ func testExecCommon(t *testing.T, buildModuleCommand func(string, *Builder) (*co } b := &Builder{ Builder: flag.Builder{ - GetClientConn: func() (grpc.ClientConnInterface, error) { - return conn, nil - }, + AddressCodec: addresscodec.NewBech32Codec("cosmos"), }, GetClientConn: func(*cobra.Command) (grpc.ClientConnInterface, error) { return conn, nil diff --git a/client/v2/autocli/flag/address.go b/client/v2/autocli/flag/address.go index 4897a7461e..5b5243528a 100644 --- a/client/v2/autocli/flag/address.go +++ b/client/v2/autocli/flag/address.go @@ -4,33 +4,14 @@ import ( "context" "fmt" - reflectionv2alpha1 "cosmossdk.io/api/cosmos/base/reflection/v2alpha1" - "cosmossdk.io/core/address" "google.golang.org/protobuf/reflect/protoreflect" - addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + "cosmossdk.io/core/address" ) type addressStringType struct{} func (a addressStringType) NewValue(ctx context.Context, b *Builder) Value { - if b.AddressCodec == nil { - conn, err := b.GetClientConn() - if err != nil { - panic(err) - } - reflectionClient := reflectionv2alpha1.NewReflectionServiceClient(conn) - resp, err := reflectionClient.GetConfigurationDescriptor(ctx, &reflectionv2alpha1.GetConfigurationDescriptorRequest{}) - if err != nil { - panic(err) - } - if resp == nil || resp.Config == nil { - panic("bech32 account address prefix is not set") - } - - b.AddressCodec = addresscodec.NewBech32Codec(resp.Config.Bech32AccountAddressPrefix) - } - return &addressValue{addressCodec: b.AddressCodec} } diff --git a/client/v2/autocli/flag/builder.go b/client/v2/autocli/flag/builder.go index ad2057ef46..1d9ec4f09a 100644 --- a/client/v2/autocli/flag/builder.go +++ b/client/v2/autocli/flag/builder.go @@ -1,7 +1,6 @@ package flag import ( - "google.golang.org/grpc" "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" @@ -27,9 +26,6 @@ type Builder struct { // AddressCodec is the address codec used for the address flag AddressCodec address.Codec - - // GetClientConn is the reflection client for the address flag - GetClientConn func() (grpc.ClientConnInterface, error) } func (b *Builder) init() { diff --git a/codec/address/bech32_codec.go b/codec/address/bech32_codec.go index d1daf93703..aaac921f35 100644 --- a/codec/address/bech32_codec.go +++ b/codec/address/bech32_codec.go @@ -1,6 +1,9 @@ package address import ( + "errors" + "strings" + "cosmossdk.io/core/address" errorsmod "cosmossdk.io/errors" @@ -21,13 +24,17 @@ func NewBech32Codec(prefix string) address.Codec { // StringToBytes encodes text to bytes func (bc Bech32Codec) StringToBytes(text string) ([]byte, error) { + if len(strings.TrimSpace(text)) == 0 { + return []byte{}, errors.New("empty address string is not allowed") + } + hrp, bz, err := bech32.DecodeAndConvert(text) if err != nil { return nil, err } if hrp != bc.Bech32Prefix { - return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "hrp does not match bech32Prefix") + return nil, errorsmod.Wrapf(sdkerrors.ErrLogic, "hrp does not match bech32 prefix: expected '%s' got '%s'", bc.Bech32Prefix, hrp) } if err := sdk.VerifyAddressFormat(bz); err != nil { diff --git a/tools/hubl/go.mod b/tools/hubl/go.mod index d9deeb464c..309588dd50 100644 --- a/tools/hubl/go.mod +++ b/tools/hubl/go.mod @@ -4,9 +4,10 @@ go 1.20 require ( cosmossdk.io/api v0.4.1 - cosmossdk.io/client/v2 v2.0.0-20230320224637-dca0e7374a1d + cosmossdk.io/client/v2 v2.0.0-20230426154441-2037a26d1235 cosmossdk.io/errors v1.0.0-beta.7 github.com/cockroachdb/errors v1.9.1 + github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230426154441-2037a26d1235 github.com/hashicorp/go-multierror v1.1.1 github.com/manifoldco/promptui v0.9.0 github.com/pelletier/go-toml/v2 v2.0.7 @@ -17,7 +18,7 @@ require ( require ( cosmossdk.io/collections v0.1.0 // indirect - cosmossdk.io/core v0.6.1 // indirect + cosmossdk.io/core v0.6.2-0.20230323161322-ccd8d40119e4 // indirect cosmossdk.io/depinject v1.0.0-alpha.3 // indirect cosmossdk.io/log v1.0.0 // indirect cosmossdk.io/math v1.0.0 // indirect @@ -42,7 +43,6 @@ require ( github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-db v1.0.0-rc.1 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect - github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230424095137-b73c17cb9cc8 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogoproto v1.4.8 // indirect github.com/cosmos/iavl v0.21.0-beta.1 // indirect diff --git a/tools/hubl/go.sum b/tools/hubl/go.sum index fb828fed8f..b4aa4add46 100644 --- a/tools/hubl/go.sum +++ b/tools/hubl/go.sum @@ -37,12 +37,12 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cosmossdk.io/api v0.4.1 h1:0ikaYM6GyxTYYcfBiyR8YnLCfhNnhKpEFnaSepCTmqg= cosmossdk.io/api v0.4.1/go.mod h1:jR7k5ok90LxW2lFUXvd8Vpo/dr4PpiyVegxdm7b1ZdE= -cosmossdk.io/client/v2 v2.0.0-20230320224637-dca0e7374a1d h1:9mBIeO0ZhCalqS3pJiZ1fs+Nn93E7rU4+Hv7QVINbNM= -cosmossdk.io/client/v2 v2.0.0-20230320224637-dca0e7374a1d/go.mod h1:qX4UABq4VI1ccJn4H4MIJx5/HvjRiaVaImovbnPXNXc= +cosmossdk.io/client/v2 v2.0.0-20230426154441-2037a26d1235 h1:6aGhtjUgmacucrKMC9ZdF9G96YoxZqkTC2ZyxaAg1GE= +cosmossdk.io/client/v2 v2.0.0-20230426154441-2037a26d1235/go.mod h1:ydI6QS3A+K2px6O8QpM0JtNaVV6lLeCJ5LVwtQXIMAg= cosmossdk.io/collections v0.1.0 h1:nzJGeiq32KnZroSrhB6rPifw4I85Cgmzw/YAmr4luv8= cosmossdk.io/collections v0.1.0/go.mod h1:xbauc0YsbUF8qKMVeBZl0pFCunxBIhKN/WlxpZ3lBuo= -cosmossdk.io/core v0.6.1 h1:OBy7TI2W+/gyn2z40vVvruK3di+cAluinA6cybFbE7s= -cosmossdk.io/core v0.6.1/go.mod h1:g3MMBCBXtxbDWBURDVnJE7XML4BG5qENhs0gzkcpuFA= +cosmossdk.io/core v0.6.2-0.20230323161322-ccd8d40119e4 h1:l1scDTT2VX18ZuR6P0irvT/bAP0h4297D/Lka5nz2vE= +cosmossdk.io/core v0.6.2-0.20230323161322-ccd8d40119e4/go.mod h1:J8R0E7soOpQFVqFiFd7EKepXCPpINa2n2t2EqbEsXnY= cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw= cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU= cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w= @@ -146,8 +146,8 @@ github.com/cosmos/cosmos-db v1.0.0-rc.1 h1:SjnT8B6WKMW9WEIX32qMhnEEKcI7ZP0+G1Sa9 github.com/cosmos/cosmos-db v1.0.0-rc.1/go.mod h1:Dnmk3flSf5lkwCqvvjNpoxjpXzhxnCAFzKHlbaForso= 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.20230424095137-b73c17cb9cc8 h1:zIl1WnrW5ZP1VwhpbwVBZtCntkNKYNIkg4233/dZ3BU= -github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230424095137-b73c17cb9cc8/go.mod h1:JicgV9n3SAu5uuoyDvQ2gSHYLyFvyRrIUYB5T2Q4HRw= +github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230426154441-2037a26d1235 h1:EQM4Ewp62TpQ141W0IJL84qJ8zyvGeqA75r++81JKGc= +github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230426154441-2037a26d1235/go.mod h1:1pnJEQxrWXGGijISBNKkisAuMlohZROsazmj+JYkBc0= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= diff --git a/tools/hubl/internal/config.go b/tools/hubl/internal/config.go index 75cd8483ba..136c6e7f60 100644 --- a/tools/hubl/internal/config.go +++ b/tools/hubl/internal/config.go @@ -15,6 +15,7 @@ type Config struct { type ChainConfig struct { GRPCEndpoints []GRPCEndpoint `toml:"trusted-grpc-endpoints"` + Bech32Prefix string `toml:"bech32-prefix"` } type GRPCEndpoint struct { diff --git a/tools/hubl/internal/load.go b/tools/hubl/internal/load.go index 43270e4bc2..06f89893db 100644 --- a/tools/hubl/internal/load.go +++ b/tools/hubl/internal/load.go @@ -7,8 +7,6 @@ import ( "os" "path" - autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" - reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" "github.com/cockroachdb/errors" "github.com/hashicorp/go-multierror" "google.golang.org/grpc" @@ -18,6 +16,10 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/types/descriptorpb" + + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + reflectionv2alpha1 "cosmossdk.io/api/cosmos/base/reflection/v2alpha1" + reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" ) const DefaultConfigDirName = ".hubl" @@ -25,20 +27,21 @@ const DefaultConfigDirName = ".hubl" type ChainInfo struct { client *grpc.ClientConn - ConfigDir string - Chain string - ModuleOptions map[string]*autocliv1.ModuleOptions + Context context.Context + ConfigDir string + Chain string + Config *ChainConfig + ProtoFiles *protoregistry.Files - Context context.Context - Config *ChainConfig + ModuleOptions map[string]*autocliv1.ModuleOptions } func NewChainInfo(configDir, chain string, config *ChainConfig) *ChainInfo { return &ChainInfo{ - ConfigDir: configDir, - Chain: chain, - Config: config, Context: context.Background(), + Config: config, + Chain: chain, + ConfigDir: configDir, } } @@ -123,22 +126,21 @@ func (c *ChainInfo) Load(reload bool) error { } autocliQueryClient := autocliv1.NewQueryClient(client) - appOptionsRes, err := autocliQueryClient.AppOptions(c.Context, &autocliv1.AppOptionsRequest{}) + appOptsRes, err := autocliQueryClient.AppOptions(c.Context, &autocliv1.AppOptionsRequest{}) if err != nil { - appOptionsRes = guessAutocli(c.ProtoFiles) + appOptsRes = guessAutocli(c.ProtoFiles) } - bz, err := proto.Marshal(appOptionsRes) + bz, err := proto.Marshal(appOptsRes) if err != nil { return err } - err = os.WriteFile(appOptsFilename, bz, 0o600) - if err != nil { + if err := os.WriteFile(appOptsFilename, bz, 0o600); err != nil { return err } - c.ModuleOptions = appOptionsRes.ModuleOptions + c.ModuleOptions = appOptsRes.ModuleOptions } else { bz, err := os.ReadFile(appOptsFilename) if err != nil { @@ -146,8 +148,7 @@ func (c *ChainInfo) Load(reload bool) error { } var appOptsRes autocliv1.AppOptionsResponse - err = proto.Unmarshal(bz, &appOptsRes) - if err != nil { + if err := proto.Unmarshal(bz, &appOptsRes); err != nil { return err } @@ -185,3 +186,18 @@ func (c *ChainInfo) OpenClient() (*grpc.ClientConn, error) { return nil, errors.Wrapf(res, "error loading gRPC client") } + +// getAddressPrefix returns the address prefix of the chain. +func getAddressPrefix(ctx context.Context, conn grpc.ClientConnInterface) (string, error) { + reflectionClient := reflectionv2alpha1.NewReflectionServiceClient(conn) + resp, err := reflectionClient.GetConfigurationDescriptor(ctx, &reflectionv2alpha1.GetConfigurationDescriptorRequest{}) + if err != nil { + return "", err + } + + if resp == nil || resp.Config == nil || resp.Config.Bech32AccountAddressPrefix == "" { + return "", errors.New("bech32 account address prefix is not set") + } + + return resp.Config.Bech32AccountAddressPrefix, nil +} diff --git a/tools/hubl/internal/remote.go b/tools/hubl/internal/remote.go index 346ff59364..0f4ef0684f 100644 --- a/tools/hubl/internal/remote.go +++ b/tools/hubl/internal/remote.go @@ -1,6 +1,7 @@ package internal import ( + "context" "fmt" "os" "path" @@ -12,6 +13,8 @@ import ( "google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/types/dynamicpb" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + "cosmossdk.io/client/v2/autocli" "cosmossdk.io/client/v2/autocli/flag" ) @@ -91,22 +94,22 @@ func RemoteCommand(config *Config, configDir string) ([]*cobra.Command, error) { builder := &autocli.Builder{ Builder: flag.Builder{ + AddressCodec: addresscodec.NewBech32Codec(chainConfig.Bech32Prefix), TypeResolver: &dynamicTypeResolver{chainInfo}, FileResolver: chainInfo.ProtoFiles, - GetClientConn: func() (grpc.ClientConnInterface, error) { - return chainInfo.OpenClient() - }, }, GetClientConn: func(command *cobra.Command) (grpc.ClientConnInterface, error) { return chainInfo.OpenClient() }, AddQueryConnFlags: func(command *cobra.Command) {}, } + var ( update bool reconfig bool insecure bool ) + chainCmd := &cobra.Command{ Use: chain, Short: fmt.Sprintf("Commands for the %s chain", chain), @@ -177,7 +180,19 @@ func reconfigure(cmd *cobra.Command, config *Config, configDir, chain string) er return err } + client, err := chainInfo.OpenClient() + if err != nil { + return err + } + + addressPrefix, err := getAddressPrefix(context.Background(), client) + if err != nil { + return err + } + + chainConfig.Bech32Prefix = addressPrefix config.Chains[chain] = chainConfig + if err := SaveConfig(configDir, config); err != nil { return err } diff --git a/x/auth/keeper/bech32_codec.go b/x/auth/keeper/bech32_codec.go index 926dd45f6b..dd0b2ab02e 100644 --- a/x/auth/keeper/bech32_codec.go +++ b/x/auth/keeper/bech32_codec.go @@ -34,7 +34,7 @@ func (bc bech32Codec) StringToBytes(text string) ([]byte, error) { } if hrp != bc.bech32Prefix { - return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "hrp does not match bech32Prefix") + return nil, errorsmod.Wrapf(sdkerrors.ErrLogic, "hrp does not match bech32 prefix: expected '%s' got '%s'", bc.bech32Prefix, hrp) } if err := sdk.VerifyAddressFormat(bz); err != nil {