Skip to content

Commit 39808e0

Browse files
authored
feat(x/tx): add custom type encoder (#19786)
1 parent def211d commit 39808e0

5 files changed

Lines changed: 79 additions & 1 deletion

File tree

docs/build/building-modules/05-protobuf-annotations.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,9 @@ Encoding instructs the amino json marshaler how to encode certain fields that ma
125125
```proto reference
126126
https://github.com/cosmos/cosmos-sdk/blob/e8f28bf5db18b8d6b7e0d94b542ce4cf48fed9d6/proto/cosmos/bank/v1beta1/genesis.proto#L23
127127
```
128+
129+
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).
130+
131+
```proto
132+
(amino.encoding) = "bytes_as_string",
133+
```

x/tx/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
3131

3232
## [Unreleased]
3333

34+
### Features
35+
36+
* [#19786](https://github.com/cosmos/cosmos-sdk/pull/19786) Add bytes as string option to encoder.
37+
3438
### Improvements
3539

3640
* [#19845](https://github.com/cosmos/cosmos-sdk/pull/19845) Use hybrid resolver instead of only protov2 registry

x/tx/signing/aminojson/encoder.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,22 @@ func nullSliceAsEmptyEncoder(enc *Encoder, v protoreflect.Value, w io.Writer) er
8181
}
8282
}
8383

84+
// cosmosBytesAsString replicates the behavior at:
85+
// https://github.com/CosmWasm/wasmd/blob/08567ff20e372e4f4204a91ca64a371538742bed/x/wasm/types/tx.go#L20-L22
86+
func cosmosBytesAsString(_ *Encoder, v protoreflect.Value, w io.Writer) error {
87+
switch bz := v.Interface().(type) {
88+
case []byte:
89+
blob, err := json.RawMessage(bz).MarshalJSON()
90+
if err != nil {
91+
return err
92+
}
93+
_, err = w.Write(blob)
94+
return err
95+
default:
96+
return fmt.Errorf("unsupported type %T", bz)
97+
}
98+
}
99+
84100
// keyFieldEncoder replicates the behavior at described at:
85101
// https://github.com/cosmos/cosmos-sdk/blob/b49f948b36bc991db5be431607b475633aed697e/proto/cosmos/crypto/secp256k1/keys.proto#L16
86102
// The message is treated if it were bytes directly without the key field specified.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package aminojson
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
"google.golang.org/protobuf/reflect/protoreflect"
9+
"gotest.tools/v3/assert"
10+
)
11+
12+
func TestCosmosBytesAsString(t *testing.T) {
13+
cases := map[string]struct {
14+
value protoreflect.Value
15+
wantErr bool
16+
wantOutput string
17+
}{
18+
"valid bytes - json": {
19+
value: protoreflect.ValueOfBytes([]byte(`{"test":"value"}`)),
20+
wantErr: false,
21+
wantOutput: `{"test":"value"}`,
22+
},
23+
"valid bytes - string": {
24+
value: protoreflect.ValueOfBytes([]byte(`foo`)),
25+
wantErr: false,
26+
wantOutput: `foo`,
27+
},
28+
"unsupported type - bool": {
29+
value: protoreflect.ValueOfBool(true),
30+
wantErr: true,
31+
},
32+
"unsupported type - int64": {
33+
value: protoreflect.ValueOfInt64(1),
34+
wantErr: true,
35+
},
36+
}
37+
38+
for name, tc := range cases {
39+
t.Run(name, func(t *testing.T) {
40+
var buf bytes.Buffer
41+
err := cosmosBytesAsString(nil, tc.value, &buf)
42+
43+
if tc.wantErr {
44+
require.Error(t, err)
45+
return
46+
}
47+
require.NoError(t, err)
48+
assert.Equal(t, tc.wantOutput, buf.String())
49+
})
50+
}
51+
}

x/tx/signing/aminojson/json_marshal.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ func NewEncoder(options EncoderOptions) Encoder {
7272
"threshold_string": thresholdStringEncoder,
7373
},
7474
aminoFieldEncoders: map[string]FieldEncoder{
75-
"legacy_coins": nullSliceAsEmptyEncoder,
75+
"legacy_coins": nullSliceAsEmptyEncoder,
76+
"bytes_as_string": cosmosBytesAsString,
7677
},
7778
protoTypeEncoders: map[string]MessageEncoder{
7879
"google.protobuf.Timestamp": marshalTimestamp,

0 commit comments

Comments
 (0)