Skip to content

Commit

Permalink
feat(x/tx): add custom type encoder (#19786)
Browse files Browse the repository at this point in the history
  • Loading branch information
pinosu authored Mar 27, 2024
1 parent def211d commit 39808e0
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 1 deletion.
6 changes: 6 additions & 0 deletions docs/build/building-modules/05-protobuf-annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,9 @@ Encoding instructs the amino json marshaler how to encode certain fields that ma
```proto reference
https://github.com/cosmos/cosmos-sdk/blob/e8f28bf5db18b8d6b7e0d94b542ce4cf48fed9d6/proto/cosmos/bank/v1beta1/genesis.proto#L23
```

Another example is how `bytes` is encoded when using the amino json encoding format. The `bytes_as_string` option tells the json marshaler [how to encode bytes as string](https://github.com/pinosu/cosmos-sdk/blob/9879ece09c58068402782fa2096199dc89a23d13/x/tx/signing/aminojson/json_marshal.go#L75).

```proto
(amino.encoding) = "bytes_as_string",
```
4 changes: 4 additions & 0 deletions x/tx/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ Ref: https://keepachangelog.com/en/1.0.0/

## [Unreleased]

### Features

* [#19786](https://github.com/cosmos/cosmos-sdk/pull/19786) Add bytes as string option to encoder.

### Improvements

* [#19845](https://github.com/cosmos/cosmos-sdk/pull/19845) Use hybrid resolver instead of only protov2 registry
Expand Down
16 changes: 16 additions & 0 deletions x/tx/signing/aminojson/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,22 @@ func nullSliceAsEmptyEncoder(enc *Encoder, v protoreflect.Value, w io.Writer) er
}
}

// cosmosBytesAsString replicates the behavior at:
// https://github.com/CosmWasm/wasmd/blob/08567ff20e372e4f4204a91ca64a371538742bed/x/wasm/types/tx.go#L20-L22
func cosmosBytesAsString(_ *Encoder, v protoreflect.Value, w io.Writer) error {
switch bz := v.Interface().(type) {
case []byte:
blob, err := json.RawMessage(bz).MarshalJSON()
if err != nil {
return err
}
_, err = w.Write(blob)
return err
default:
return fmt.Errorf("unsupported type %T", bz)
}
}

// keyFieldEncoder replicates the behavior at described at:
// https://github.com/cosmos/cosmos-sdk/blob/b49f948b36bc991db5be431607b475633aed697e/proto/cosmos/crypto/secp256k1/keys.proto#L16
// The message is treated if it were bytes directly without the key field specified.
Expand Down
51 changes: 51 additions & 0 deletions x/tx/signing/aminojson/encoder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package aminojson

import (
"bytes"
"testing"

"github.com/stretchr/testify/require"
"google.golang.org/protobuf/reflect/protoreflect"
"gotest.tools/v3/assert"
)

func TestCosmosBytesAsString(t *testing.T) {
cases := map[string]struct {
value protoreflect.Value
wantErr bool
wantOutput string
}{
"valid bytes - json": {
value: protoreflect.ValueOfBytes([]byte(`{"test":"value"}`)),
wantErr: false,
wantOutput: `{"test":"value"}`,
},
"valid bytes - string": {
value: protoreflect.ValueOfBytes([]byte(`foo`)),
wantErr: false,
wantOutput: `foo`,
},
"unsupported type - bool": {
value: protoreflect.ValueOfBool(true),
wantErr: true,
},
"unsupported type - int64": {
value: protoreflect.ValueOfInt64(1),
wantErr: true,
},
}

for name, tc := range cases {
t.Run(name, func(t *testing.T) {
var buf bytes.Buffer
err := cosmosBytesAsString(nil, tc.value, &buf)

if tc.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tc.wantOutput, buf.String())
})
}
}
3 changes: 2 additions & 1 deletion x/tx/signing/aminojson/json_marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ func NewEncoder(options EncoderOptions) Encoder {
"threshold_string": thresholdStringEncoder,
},
aminoFieldEncoders: map[string]FieldEncoder{
"legacy_coins": nullSliceAsEmptyEncoder,
"legacy_coins": nullSliceAsEmptyEncoder,
"bytes_as_string": cosmosBytesAsString,
},
protoTypeEncoders: map[string]MessageEncoder{
"google.protobuf.Timestamp": marshalTimestamp,
Expand Down

0 comments on commit 39808e0

Please sign in to comment.