Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(x/tx): add aminoNameAsTypeURL option in aminojson encoder #21712

Merged
merged 12 commits into from
Sep 18, 2024
4 changes: 4 additions & 0 deletions client/v2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [#18626](https://github.com/cosmos/cosmos-sdk/pull/18626) Support for off-chain signing and verification of a file.
* [#18461](https://github.com/cosmos/cosmos-sdk/pull/18461) Support governance proposals.

### Improvements

* [#21712](https://github.com/cosmos/cosmos-sdk/pull/21712) Marshal `type` field as proto message url in queries instead of amino name.

### API Breaking Changes

* [#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)).
Expand Down
11 changes: 6 additions & 5 deletions client/v2/autocli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,12 @@ func (b *Builder) BuildQueryMethodCommand(ctx context.Context, descriptor protor
methodName := fmt.Sprintf("/%s/%s", serviceDescriptor.FullName(), descriptor.Name())
outputType := util.ResolveMessageType(b.TypeResolver, descriptor.Output())
encoderOptions := aminojson.EncoderOptions{
Indent: " ",
EnumAsString: true,
DoNotSortFields: true,
TypeResolver: b.TypeResolver,
FileResolver: b.FileResolver,
Indent: " ",
EnumAsString: true,
DoNotSortFields: true,
AminoNameAsTypeURL: true,
TypeResolver: b.TypeResolver,
FileResolver: b.FileResolver,
}

cmd, err := b.buildMethodCommandCommon(descriptor, options, func(cmd *cobra.Command, input protoreflect.Message) error {
Expand Down
1 change: 1 addition & 0 deletions x/tx/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Since v0.13.0, x/tx follows Cosmos SDK semver: https://github.com/cosmos/cosmos-

### Improvements

* [#21712](https://github.com/cosmos/cosmos-sdk/pull/21712) Add `AminoNameAsTypeURL` option to Amino JSON encoder.
* [#21073](https://github.com/cosmos/cosmos-sdk/pull/21073) In Context use sync.Map `getSignersFuncs` map from concurrent writes, we also need to call Validate when using the legacy app.

## [v0.13.3](https://github.com/cosmos/cosmos-sdk/releases/tag/x/tx/v0.13.3) - 2024-04-22
Expand Down
4 changes: 2 additions & 2 deletions x/tx/signing/aminojson/any.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func marshalAny(enc *Encoder, message protoreflect.Message, writer io.Writer) er
protoMessage = valueMsg.ProtoReflect()
}

return enc.beginMarshal(protoMessage, writer, true)
return enc.beginMarshal(protoMessage, writer, true, enc.aminoNameAsTypeURL)
}

const (
Expand All @@ -73,5 +73,5 @@ func marshalDynamic(enc *Encoder, message protoreflect.Message, writer io.Writer
return err
}

return enc.beginMarshal(valueMsg.ProtoReflect(), writer, true)
return enc.beginMarshal(valueMsg.ProtoReflect(), writer, true, enc.aminoNameAsTypeURL)
}
27 changes: 18 additions & 9 deletions x/tx/signing/aminojson/json_marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ type EncoderOptions struct {
// EnumAsString when set will encode enums as strings instead of integers.
// Caution: Enabling this option produce different sign bytes.
EnumAsString bool
// AminoNameAsTypeURL when set will use the amino name as the type URL in the JSON output.
// It is useful when using the Amino JSON encoder for non Amino purposes,
// such as JSON RPC.
AminoNameAsTypeURL bool
// TypeResolver is used to resolve protobuf message types by TypeURL when marshaling any packed messages.
TypeResolver signing.TypeResolver
// FileResolver is used to resolve protobuf file descriptors TypeURL when TypeResolver fails.
Expand All @@ -50,6 +54,7 @@ type Encoder struct {
doNotSortFields bool
indent string
enumsAsString bool
aminoNameAsTypeURL bool
}

// NewEncoder returns a new Encoder capable of serializing protobuf messages to JSON using the Amino JSON encoding
Expand Down Expand Up @@ -80,11 +85,12 @@ func NewEncoder(options EncoderOptions) Encoder {
"google.protobuf.Duration": marshalDuration,
"google.protobuf.Any": marshalAny,
},
fileResolver: options.FileResolver,
typeResolver: options.TypeResolver,
doNotSortFields: options.DoNotSortFields,
indent: options.Indent,
enumsAsString: options.EnumAsString,
fileResolver: options.FileResolver,
typeResolver: options.TypeResolver,
doNotSortFields: options.DoNotSortFields,
indent: options.Indent,
enumsAsString: options.EnumAsString,
aminoNameAsTypeURL: options.AminoNameAsTypeURL,
}
return enc
}
Expand Down Expand Up @@ -163,7 +169,7 @@ func (enc Encoder) DefineTypeEncoding(typeURL string, encoder MessageEncoder) En
// Marshal serializes a protobuf message to JSON.
func (enc Encoder) Marshal(message proto.Message) ([]byte, error) {
buf := &bytes.Buffer{}
err := enc.beginMarshal(message.ProtoReflect(), buf, false)
err := enc.beginMarshal(message.ProtoReflect(), buf, false, enc.aminoNameAsTypeURL)
if err != nil {
return nil, err
}
Expand All @@ -180,15 +186,18 @@ func (enc Encoder) Marshal(message proto.Message) ([]byte, error) {
return buf.Bytes(), nil
}

func (enc Encoder) beginMarshal(msg protoreflect.Message, writer io.Writer, isAny bool) error {
func (enc Encoder) beginMarshal(msg protoreflect.Message, writer io.Writer, isAny, useTypeUrl bool) error {
var (
name string
named bool
)

if isAny {
switch {
case useTypeUrl:
name, named = getMessageTypeURL(msg), true
case isAny:
name, named = getMessageAminoNameAny(msg), true
} else {
default:
name, named = getMessageAminoName(msg)
}

Expand Down
59 changes: 59 additions & 0 deletions x/tx/signing/aminojson/json_marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,62 @@ func TestEnumAsString(t *testing.T) {
}
}`, string(bz))
}

func TestAminoNameAsTypeURL(t *testing.T) {
encoder := aminojson.NewEncoder(aminojson.EncoderOptions{Indent: " ", AminoNameAsTypeURL: true})

msg := &testpb.ABitOfEverything{
Message: &testpb.NestedMessage{
Foo: "test",
Bar: 0, // this is the default value and should be omitted from output
},
Enum: testpb.AnEnum_ONE,
Repeated: []int32{3, -7, 2, 6, 4},
Str: `abcxyz"foo"def`,
Bool: true,
Bytes: []byte{0, 1, 2, 3},
I32: -15,
F32: 1001,
U32: 1200,
Si32: -376,
Sf32: -1000,
I64: 14578294827584932,
F64: 9572348124213523654,
U64: 4759492485,
Si64: -59268425823934,
Sf64: -659101379604211154,
}

bz, err := encoder.Marshal(msg)
require.NoError(t, err)
fmt.Println(string(bz))
require.Equal(t, `{
"type": "/testpb.ABitOfEverything",
"value": {
"bool": true,
"bytes": "AAECAw==",
"enum": 1,
"f32": 1001,
"f64": "9572348124213523654",
"i32": -15,
"i64": "14578294827584932",
"message": {
"foo": "test"
},
"repeated": [
3,
-7,
2,
6,
4
],
"sf32": -1000,
"sf64": "-659101379604211154",
"si32": -376,
"si64": "-59268425823934",
"str": "abcxyz\"foo\"def",
"u32": 1200,
"u64": "4759492485"
}
}`, string(bz))
}
6 changes: 5 additions & 1 deletion x/tx/signing/aminojson/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,18 @@ func getMessageAminoName(msg protoreflect.Message) (string, bool) {

// getMessageAminoName returns the amino name of a message if it has been set by the `amino.name` option.
// If the message does not have an amino name, then it returns the msg url.
// If it cannot get the msg url, then it returns false.
func getMessageAminoNameAny(msg protoreflect.Message) string {
messageOptions := msg.Descriptor().Options()
if proto.HasExtension(messageOptions, amino.E_Name) {
name := proto.GetExtension(messageOptions, amino.E_Name)
return name.(string)
}

return getMessageTypeURL(msg)
}

// getMessageTypeURL returns the msg url.
func getMessageTypeURL(msg protoreflect.Message) string {
msgURL := "/" + string(msg.Descriptor().FullName())
if msgURL != "/" {
return msgURL
Expand Down
Loading