diff --git a/client/v2/autocli/common.go b/client/v2/autocli/common.go index 634947efb1..d7a462ff23 100644 --- a/client/v2/autocli/common.go +++ b/client/v2/autocli/common.go @@ -1,8 +1,10 @@ package autocli import ( - autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + "context" "fmt" + + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/spf13/cobra" "google.golang.org/protobuf/reflect/protoreflect" @@ -41,6 +43,7 @@ func (b *Builder) buildMethodCommandCommon(descriptor protoreflect.MethodDescrip Version: options.Version, } + cmd.SetContext(context.Background()) binder, err := b.AddMessageFlags(cmd.Context(), cmd.Flags(), inputType, options) if err != nil { return nil, err @@ -108,11 +111,9 @@ func (b *Builder) enhanceCommandCommon(cmd *cobra.Command, moduleOptions map[str func (b *Builder) outOrStdoutFormat(cmd *cobra.Command, out []byte) error { var err error outputType := cmd.Flag(flags.FlagOutput) - if outputType == nil { - return fmt.Errorf("output flag not found") - - } - if outputType.Value.String() == "text" { + // if the output type is text, convert the json to yaml + // if output type is json or nil, default to json + if outputType != nil && outputType.Value.String() == "text" { out, err = yaml.JSONToYAML(out) if err != nil { return err diff --git a/client/v2/autocli/common_test.go b/client/v2/autocli/common_test.go index 6d55d8315f..b7f8d65074 100644 --- a/client/v2/autocli/common_test.go +++ b/client/v2/autocli/common_test.go @@ -2,6 +2,9 @@ package autocli import ( "bytes" + "context" + 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" @@ -16,6 +19,7 @@ import ( func testExecCommon(t *testing.T, buildModuleCommand func(string, *Builder) (*cobra.Command, error), args ...string) *testClientConn { server := grpc.NewServer() testpb.RegisterQueryServer(server, &testEchoServer{}) + reflectionv2alpha1.RegisterReflectionServiceServer(server, &testReflectionServer{}) listener, err := net.Listen("tcp", "127.0.0.1:0") assert.NilError(t, err) go func() { @@ -41,6 +45,11 @@ func testExecCommon(t *testing.T, buildModuleCommand func(string, *Builder) (*co errorOut: &bytes.Buffer{}, } b := &Builder{ + Builder: flag.Builder{ + + GetClientConn: func() (grpc.ClientConnInterface, error) { + return conn, nil + }}, GetClientConn: func(*cobra.Command) (grpc.ClientConnInterface, error) { return conn, nil }, @@ -57,3 +66,17 @@ func testExecCommon(t *testing.T, buildModuleCommand func(string, *Builder) (*co cmd.Execute() return conn } + +type testReflectionServer struct { + reflectionv2alpha1.UnimplementedReflectionServiceServer +} + +func (t testReflectionServer) GetConfigurationDescriptor(_ context.Context, client *reflectionv2alpha1.GetConfigurationDescriptorRequest) (*reflectionv2alpha1.GetConfigurationDescriptorResponse, error) { + return &reflectionv2alpha1.GetConfigurationDescriptorResponse{ + Config: &reflectionv2alpha1.ConfigurationDescriptor{ + Bech32AccountAddressPrefix: "cosmos", + }, + }, nil +} + +var _ reflectionv2alpha1.ReflectionServiceServer = testReflectionServer{} diff --git a/client/v2/autocli/flag/address.go b/client/v2/autocli/flag/address.go index 163fbfc9ae..078aec467d 100644 --- a/client/v2/autocli/flag/address.go +++ b/client/v2/autocli/flag/address.go @@ -2,14 +2,31 @@ package flag import ( "context" - + reflectionv2alpha1 "cosmossdk.io/api/cosmos/base/reflection/v2alpha1" + "github.com/cosmos/cosmos-sdk/types" "google.golang.org/protobuf/reflect/protoreflect" ) type addressStringType struct{} -func (a addressStringType) NewValue(_ context.Context, _ *Builder) Value { - return &addressValue{} +func (a addressStringType) NewValue(ctx context.Context, b *Builder) Value { + + if b.AddressPrefix == "" { + 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.AddressPrefix = resp.Config.Bech32AccountAddressPrefix + } + return &addressValue{addressPrefix: b.AddressPrefix} } func (a addressStringType) DefaultValue() string { @@ -17,7 +34,8 @@ func (a addressStringType) DefaultValue() string { } type addressValue struct { - value string + value string + addressPrefix string } func (a addressValue) Get(protoreflect.Value) (protoreflect.Value, error) { @@ -28,9 +46,14 @@ func (a addressValue) String() string { return a.value } +// Set implements the flag.Value interface for addressValue it only supports bech32 addresses. func (a *addressValue) Set(s string) error { + _, err := types.GetFromBech32(s, a.addressPrefix) + if err != nil { + return err + } a.value = s - // TODO handle bech32 validation + return nil } diff --git a/client/v2/autocli/flag/builder.go b/client/v2/autocli/flag/builder.go index 2969314e84..d42746fe3b 100644 --- a/client/v2/autocli/flag/builder.go +++ b/client/v2/autocli/flag/builder.go @@ -1,6 +1,7 @@ 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" @@ -21,6 +22,11 @@ type Builder struct { messageFlagTypes map[protoreflect.FullName]Type scalarFlagTypes map[string]Type + + // AddressPrefix is the prefix for the address flag + AddressPrefix string + // reflectionClient is the reflection client for the address flag + GetClientConn func() (grpc.ClientConnInterface, error) } func (b *Builder) init() { @@ -34,6 +40,7 @@ func (b *Builder) init() { b.scalarFlagTypes = map[string]Type{} b.scalarFlagTypes["cosmos.AddressString"] = addressStringType{} } + } func (b *Builder) DefineMessageFlagType(messageName protoreflect.FullName, flagType Type) { diff --git a/client/v2/autocli/msg_test.go b/client/v2/autocli/msg_test.go index 2035b1d71d..e3628c80f2 100644 --- a/client/v2/autocli/msg_test.go +++ b/client/v2/autocli/msg_test.go @@ -179,7 +179,7 @@ func TestEverythingMsg(t *testing.T) { "--str", "def", "--timestamp", "2019-01-02T00:01:02Z", "--a-coin", `{"denom":"foo","amount":"100000"}`, - "--an-address", "cosmossdghdsfoi2134sdgh", + "--an-address", "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", "--bz", "c2RncXdlZndkZ3NkZw==", "--page-count-total", "--page-key", "MTIzNTQ4N3NnaGRhcw==", diff --git a/client/v2/autocli/query_test.go b/client/v2/autocli/query_test.go index 9677cd373a..4b1009c300 100644 --- a/client/v2/autocli/query_test.go +++ b/client/v2/autocli/query_test.go @@ -117,7 +117,7 @@ func TestEverything(t *testing.T) { "--str", "def", "--timestamp", "2019-01-02T00:01:02Z", "--a-coin", `{"denom":"foo","amount":"100000"}`, - "--an-address", "cosmossdghdsfoi2134sdgh", + "--an-address", "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", "--bz", "c2RncXdlZndkZ3NkZw==", "--page-count-total", "--page-key", "MTIzNTQ4N3NnaGRhcw==", @@ -157,6 +157,29 @@ func TestOptions(t *testing.T) { assert.Equal(t, uint64(5), lastReq.U64) // no opt default value got set } +func TestAddressValidation(t *testing.T) { + conn := testExecCommon(t, buildModuleQueryCommand, + "echo", + "1", "abc", `{"denom":"foo","amount":"1"}`, + "--an-address", "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", + ) + assert.Equal(t, "", conn.errorOut.String()) + + conn = testExecCommon(t, buildModuleQueryCommand, + "echo", + "1", "abc", `{"denom":"foo","amount":"1"}`, + "--an-address", "regen1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", + ) + assert.Assert(t, strings.Contains(conn.errorOut.String(), "Error: invalid argument")) + + conn = testExecCommon(t, buildModuleQueryCommand, + "echo", + "1", "abc", `{"denom":"foo","amount":"1"}`, + "--an-address", "cosmps1BAD_ENCODING", + ) + assert.Assert(t, strings.Contains(conn.errorOut.String(), "Error: invalid argument")) +} + func TestOutputFormat(t *testing.T) { conn := testExecCommon(t, buildModuleQueryCommand, "echo", diff --git a/tools/hubl/go.mod b/tools/hubl/go.mod index 16b09a51f3..94b4d7f0f7 100644 --- a/tools/hubl/go.mod +++ b/tools/hubl/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( cosmossdk.io/api v0.3.2-0.20230313131911-55bf5d4efbe7 - cosmossdk.io/client/v2 v2.0.0-20230314205129-d50cb1ef349f + cosmossdk.io/client/v2 v2.0.0-20230320224637-dca0e7374a1d cosmossdk.io/errors v1.0.0-beta.7 github.com/cockroachdb/errors v1.9.1 github.com/hashicorp/go-multierror v1.1.1 diff --git a/tools/hubl/go.sum b/tools/hubl/go.sum index b0b4d1697f..fcd024aa66 100644 --- a/tools/hubl/go.sum +++ b/tools/hubl/go.sum @@ -37,8 +37,8 @@ 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.3.2-0.20230313131911-55bf5d4efbe7 h1:4LrWK+uGP5IxznxtHHsHD+ZBs2+oZRH2loYOGjHLzZM= cosmossdk.io/api v0.3.2-0.20230313131911-55bf5d4efbe7/go.mod h1:yVns7mKgcsG+hZW/3C5FdJtC6QYWdFIcRlKb9+5HV5g= -cosmossdk.io/client/v2 v2.0.0-20230314205129-d50cb1ef349f h1:1RHTrQqqtfKuMoZayZhlVR4cSAUAFXV7aHUolVom6pc= -cosmossdk.io/client/v2 v2.0.0-20230314205129-d50cb1ef349f/go.mod h1:PHHjwtEPxeSQmCZlDCb12ZZf53Mz6T5z2bAEYtlM7D4= +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/collections v0.0.0-20230309163709-87da587416ba h1:S4PYij/tX3Op/hwenVEN9D+M27JRcwSwVqE3UA0BnwM= cosmossdk.io/collections v0.0.0-20230309163709-87da587416ba/go.mod h1:lpS+G8bGC2anqzWdndTzjnQnuMO/qAcgZUkGJp4i3rc= cosmossdk.io/core v0.6.1 h1:OBy7TI2W+/gyn2z40vVvruK3di+cAluinA6cybFbE7s= diff --git a/tools/hubl/internal/remote.go b/tools/hubl/internal/remote.go index a347b9a561..f1961a05d1 100644 --- a/tools/hubl/internal/remote.go +++ b/tools/hubl/internal/remote.go @@ -93,13 +93,14 @@ func RemoteCommand(config *Config, configDir string) ([]*cobra.Command, error) { Builder: flag.Builder{ 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