diff --git a/pkg/core/interop/json/json.go b/pkg/core/interop/json/json.go new file mode 100644 index 000000000..c9f660fa3 --- /dev/null +++ b/pkg/core/interop/json/json.go @@ -0,0 +1,29 @@ +package json + +import ( + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" +) + +// Serialize handles System.JSON.Serialize syscall. +func Serialize(_ *interop.Context, v *vm.VM) error { + item := v.Estack().Pop().Item() + data, err := stackitem.ToJSON(item) + if err != nil { + return err + } + v.Estack().PushVal(data) + return nil +} + +// Deserialize handles System.JSON.Deserialize syscall. +func Deserialize(_ *interop.Context, v *vm.VM) error { + data := v.Estack().Pop().Bytes() + item, err := stackitem.FromJSON(data) + if err != nil { + return err + } + v.Estack().PushVal(item) + return nil +} diff --git a/pkg/core/interop/json/json_test.go b/pkg/core/interop/json/json_test.go new file mode 100644 index 000000000..b1d4bb34f --- /dev/null +++ b/pkg/core/interop/json/json_test.go @@ -0,0 +1,68 @@ +package json + +import ( + "encoding/binary" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +var ( + serializeID = emit.InteropNameToID([]byte("System.Json.Serialize")) + deserializeID = emit.InteropNameToID([]byte("System.Json.Deserialize")) +) + +func getInterop(id uint32) *vm.InteropFuncPrice { + switch id { + case serializeID: + return &vm.InteropFuncPrice{ + Func: func(v *vm.VM) error { return Serialize(nil, v) }, + } + case deserializeID: + return &vm.InteropFuncPrice{ + Func: func(v *vm.VM) error { return Deserialize(nil, v) }, + } + default: + return nil + } +} + +func getTestFunc(id uint32, arg interface{}, result interface{}) func(t *testing.T) { + prog := make([]byte, 5) + prog[0] = byte(opcode.SYSCALL) + binary.LittleEndian.PutUint32(prog[1:], id) + + return func(t *testing.T) { + v := vm.New() + v.RegisterInteropGetter(getInterop) + v.LoadScript(prog) + v.Estack().PushVal(arg) + if result == nil { + require.Error(t, v.Run()) + return + } + require.NoError(t, v.Run()) + require.Equal(t, stackitem.Make(result), v.Estack().Pop().Item()) + } +} + +func TestSerialize(t *testing.T) { + t.Run("Serialize", func(t *testing.T) { + t.Run("Good", getTestFunc(serializeID, 42, []byte("42"))) + t.Run("Bad", func(t *testing.T) { + arr := stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(make([]byte, stackitem.MaxSize/2)), + stackitem.NewByteArray(make([]byte, stackitem.MaxSize/2)), + }) + getTestFunc(serializeID, arr, nil)(t) + }) + }) + t.Run("Deserialize", func(t *testing.T) { + t.Run("Good", getTestFunc(deserializeID, []byte("42"), 42)) + t.Run("Bad", getTestFunc(deserializeID, []byte("{]"), nil)) + }) +} diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 326b5f3c4..437705ab6 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/core/interop/enumerator" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" + "github.com/nspcc-dev/neo-go/pkg/core/interop/json" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -105,6 +106,8 @@ var systemInterops = []interop.Function{ {Name: "System.Iterator.Key", Func: iterator.Key, Price: 400}, {Name: "System.Iterator.Keys", Func: iterator.Keys, Price: 400}, {Name: "System.Iterator.Values", Func: iterator.Values, Price: 400}, + {Name: "System.Json.Deserialize", Func: json.Deserialize, Price: 500000}, + {Name: "System.Json.Serialize", Func: json.Serialize, Price: 100000}, {Name: "System.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 200, RequiredFlags: smartcontract.AllowStates}, {Name: "System.Runtime.GetTime", Func: runtimeGetTime, Price: 1, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates},