Skip to content

Commit 207d66d

Browse files
authored
fix: panic in store.SprintStoreOps (#1036)
2 ways to reproduce : - Using a filetest: ``` go run ./gnovm/cmd/gno test -verbose ./examples/gno.land/p/demo/tests/ -run file/z0 ? ./examples/gno.land/p/demo/tests/subtests [no test files] === RUN file/z0_filetest.gno panic: expected JSON object but got "20" goroutine 1 [running]: github.com/gnolang/gno/tm2/pkg/amino.(*Codec).MustMarshalJSON(...) /home/tom/src/gno/tm2/pkg/amino/amino.go:818 github.com/gnolang/gno/tm2/pkg/amino.MustMarshalJSON({0xda1d40?, 0xc0001305a0?}) /home/tom/src/gno/tm2/pkg/amino/amino.go:140 +0x53 github.com/gnolang/gno/gnovm/pkg/gnolang.StoreOp.String({0x0, {0x1026bf0, 0xc0001305a0}, {0x0, 0x0}}) /home/tom/src/gno/gnovm/pkg/gnolang/store.go:654 +0xb2 github.com/gnolang/gno/gnovm/pkg/gnolang.(*defaultStore).SprintStoreOps(0xc0000166c0) /home/tom/src/gno/gnovm/pkg/gnolang/store.go:687 +0xff github.com/gnolang/gno/gnovm/tests.RunFileTest({0xc000357158, 0x11}, {0xc0003691a0, 0x2e}, {0xc0001499e8, 0x1, 0x1013280?}) /home/tom/src/gno/gnovm/tests/file.go:325 +0x44e main.gnoTestPkg({0xc000116500, 0x20}, {0x0?, 0x0, 0x1?}, {0xc00035b720, 0x1, 0xc000129af8?}, 0xc00034c8c0, 0xc0002948c0) /home/tom/src/gno/gnovm/cmd/gno/test.go:337 +0x876 main.execTest(0xc00034c8c0, {0xc00035b590, 0x1, 0x1}, 0xc000036220?) /home/tom/src/gno/gnovm/cmd/gno/test.go:208 +0xb67 main.newTestCmd.func1({0x0?, 0x0?}, {0xc00035b590?, 0xc00035f118?, 0x0?}) /home/tom/src/gno/gnovm/cmd/gno/test.go:51 +0x32 github.com/gnolang/gno/tm2/pkg/commands.(*Command).Run(0x5?, {0x1018768?, 0xc00003a130?}) /home/tom/src/gno/tm2/pkg/commands/command.go:233 +0x1ac github.com/gnolang/gno/tm2/pkg/commands.(*Command).Run(0xc00035ee70?, {0x1018768?, 0xc00003a130?}) /home/tom/src/gno/tm2/pkg/commands/command.go:237 +0x154 github.com/gnolang/gno/tm2/pkg/commands.(*Command).ParseAndRun(0x4059dc?, {0x1018768, 0xc00003a130}, {0xc0000361f0?, 0x401240?, 0x0?}) /home/tom/src/gno/tm2/pkg/commands/command.go:118 +0x4f main.main() /home/tom/src/gno/gnovm/cmd/gno/main.go:14 +0x75 exit status 2 ``` - using a go unit test: ``` $ go test ./gnovm/pkg/gnolang/ -v -run Amino === RUN TestAminoMustMarshalJSONPanics "20" --- FAIL: TestAminoMustMarshalJSONPanics (0.00s) panic: expected JSON object but got "20" [recovered] panic: expected JSON object but got "20" goroutine 6 [running]: testing.tRunner.func1.2({0xc15460, 0xc0002d2fc0}) /usr/lib/go/src/testing/testing.go:1526 +0x24e testing.tRunner.func1() /usr/lib/go/src/testing/testing.go:1529 +0x39f panic({0xc15460, 0xc0002d2fc0}) /usr/lib/go/src/runtime/panic.go:884 +0x213 github.com/gnolang/gno/tm2/pkg/amino.(*Codec).MustMarshalJSON(...) /home/tom/src/gno/tm2/pkg/amino/amino.go:818 github.com/gnolang/gno/tm2/pkg/amino.MustMarshalJSON({0xc59ec0?, 0xc0001c42d0?}) /home/tom/src/gno/tm2/pkg/amino/amino.go:140 +0x53 github.com/gnolang/gno/gnovm/pkg/gnolang.TestAminoMustMarshalJSONPanics(0x0?) /home/tom/src/gno/gnovm/pkg/gnolang/values_test.go:20 +0x134 testing.tRunner(0xc000300000, 0xcd8f40) /usr/lib/go/src/testing/testing.go:1576 +0x10b created by testing.(*T).Run /usr/lib/go/src/testing/testing.go:1629 +0x3ea FAIL github.com/gnolang/gno/gnovm/pkg/gnolang 0.008s FAIL ``` Additional notes: - when `// Realm:` instruction is present, it triggers a comparison between the store and the content of the instruction. - when the store content is serialized before the comparison, this panic happens in the amino code - this doesn't affect all `// Realm:` instruction, for instance `examples/gno.land/p/demo/avl/z_0_filetest.gno` is still working well <!-- please provide a detailed description of the changes made in this pull request. --> <details><summary>Contributors' checklist...</summary> - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md). </details>
1 parent 4bd1789 commit 207d66d

File tree

3 files changed

+58
-20
lines changed

3 files changed

+58
-20
lines changed

gnovm/pkg/gnolang/package_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package gnolang
2+
3+
import (
4+
"math/big"
5+
"reflect"
6+
"testing"
7+
8+
"github.com/jaekwon/testify/require"
9+
10+
"github.com/gnolang/gno/tm2/pkg/amino"
11+
)
12+
13+
// Tries to reproduce the bug #1036 on all registered types
14+
func TestAminoJSONRegisteredTypes(t *testing.T) {
15+
for _, typ := range Package.Types {
16+
// Instantiate registered type
17+
x := reflect.New(typ.Type).Interface()
18+
19+
// Call MarshalAmino directly on 'x'
20+
_, err := amino.MarshalJSON(x)
21+
require.NoError(t, err, "marshal type %s", typ.Type.Name())
22+
23+
// Call MarshalAmino on a struct that embeds 'x' in a field of type any
24+
xx := struct {
25+
X any
26+
}{X: x}
27+
_, err = amino.MarshalJSON(xx)
28+
require.NoError(t, err, "marshal type %s from struct", typ.Type.Name())
29+
}
30+
31+
// Check unmarshaling (can't reuse package.Types because some internal values
32+
// must be filled properly
33+
bi := BigintValue{V: big.NewInt(1)}
34+
bz := amino.MustMarshalJSON(bi)
35+
amino.MustUnmarshalJSON(bz, &bi)
36+
// Check unmarshaling with an embedding struct
37+
x := struct{ X any }{X: bi}
38+
bz = amino.MustMarshalJSON(x)
39+
amino.MustUnmarshalJSON(bz, &x)
40+
}

tm2/pkg/amino/json_decode.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ func (cdc *Codec) decodeReflectJSONInterface(bz []byte, iinfo *TypeInfo, rv refl
188188
}
189189

190190
// Extract the value bytes.
191-
if cinfo.IsJSONAnyValueType {
191+
if cinfo.IsJSONAnyValueType || (cinfo.IsAminoMarshaler && cinfo.ReprType.IsJSONAnyValueType) {
192192
bz = value
193193
} else {
194194
bz, err = deriveJSONObject(bz, typeURL)

tm2/pkg/amino/json_encode.go

+17-19
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ func (cdc *Codec) encodeReflectJSONInterface(w io.Writer, iinfo *TypeInfo, rv re
163163
err = errors.New("JSON bytes cannot be empty")
164164
return
165165
}
166-
if cinfo.IsJSONAnyValueType {
166+
if cinfo.IsJSONAnyValueType || (cinfo.IsAminoMarshaler && cinfo.ReprType.IsJSONAnyValueType) {
167167
// Sanity check
168168
if value[0] == '{' || value[len(value)-1] == '}' {
169169
err = errors.New("unexpected JSON object %s", value)
@@ -397,24 +397,22 @@ func isJSONAnyValueType(rt reflect.Type) bool {
397397
// {@type,value}, the latter specifically
398398
// {@type:"/google.protobuf.Any",value:{@type,value}).
399399
return true
400-
} else {
401-
// Otherwise, it depends on the kind.
402-
switch rt.Kind() {
403-
case
404-
reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
405-
reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16,
406-
reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64,
407-
// Primitive types get special {@type,value} treatment. In
408-
// binary form, most of these types would be encoded
409-
// wrapped in an implicit struct, except for lists (both of
410-
// bytes and of anything else), and for strings...
411-
reflect.Array, reflect.Slice, reflect.String:
412-
// ...which are all non-objects that must be encoded as
413-
// {@type,value}.
414-
return true
415-
default:
416-
return false
417-
}
400+
}
401+
// Otherwise, it depends on the kind.
402+
switch rt.Kind() {
403+
case
404+
reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
405+
reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16,
406+
reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64,
407+
// Primitive types get special {@type,value} treatment. In
408+
// binary form, most of these types would be encoded
409+
// wrapped in an implicit struct, except for lists (both of
410+
// bytes and of anything else), and for strings...
411+
reflect.Array, reflect.Slice, reflect.String:
412+
// ...which are all non-objects that must be encoded as
413+
// {@type,value}.
414+
return true
415+
default:
418416
return false
419417
}
420418
}

0 commit comments

Comments
 (0)