From 25f77257ce5a9253dc07cf4058565cdbad9cb09a Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 5 Nov 2019 11:36:13 +0300 Subject: [PATCH 1/6] vm: implement Boolean and ByteArray item serialization --- pkg/vm/interop.go | 30 ++++++++++++++ pkg/vm/serialization.go | 90 +++++++++++++++++++++++++++++++++++++++++ pkg/vm/vm.go | 4 ++ pkg/vm/vm_test.go | 48 ++++++++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 pkg/vm/serialization.go diff --git a/pkg/vm/interop.go b/pkg/vm/interop.go index e2288b793..8e83e0f11 100644 --- a/pkg/vm/interop.go +++ b/pkg/vm/interop.go @@ -1,6 +1,7 @@ package vm import ( + "errors" "fmt" ) @@ -20,3 +21,32 @@ func runtimeNotify(vm *VM) error { fmt.Printf("NEO-GO-VM (notify) > %s\n", item.Value()) return nil } + +// runtimeSerialize handles syscalls System.Runtime.Serialize and Neo.Runtime.Serialize. +func runtimeSerialize(vm *VM) error { + item := vm.Estack().Pop() + data, err := serializeItem(item.value) + if err != nil { + return err + } else if len(data) > MaxItemSize { + return errors.New("too big item") + } + + vm.Estack().PushVal(data) + + return nil +} + +// runtimeDeserialize handles syscalls System.Runtime.Deserialize and Neo.Runtime.Deserialize. +func runtimeDeserialize(vm *VM) error { + data := vm.Estack().Pop().Bytes() + + item, err := deserializeItem(data) + if err != nil { + return err + } + + vm.Estack().Push(&Element{value: item}) + + return nil +} diff --git a/pkg/vm/serialization.go b/pkg/vm/serialization.go new file mode 100644 index 000000000..5918745ef --- /dev/null +++ b/pkg/vm/serialization.go @@ -0,0 +1,90 @@ +package vm + +import ( + "errors" + + "github.com/CityOfZion/neo-go/pkg/io" +) + +type stackItemType byte + +const ( + byteArrayT stackItemType = 0x00 + booleanT stackItemType = 0x01 + integerT stackItemType = 0x02 + arrayT stackItemType = 0x80 + structT stackItemType = 0x81 + mapT stackItemType = 0x82 +) + +func serializeItem(item StackItem) ([]byte, error) { + w := io.NewBufBinWriter() + serializeItemTo(item, w.BinWriter) + if w.Err != nil { + return nil, w.Err + } + return w.Bytes(), nil +} + +func serializeItemTo(item StackItem, w *io.BinWriter) { + switch t := item.(type) { + case *ByteArrayItem: + w.WriteLE(byte(byteArrayT)) + w.WriteBytes(t.value) + case *BoolItem: + w.WriteLE(byte(booleanT)) + w.WriteLE(t.value) + case *BigIntegerItem: + w.Err = errors.New("not implemented") + case *InteropItem: + w.Err = errors.New("not supported") + case *ArrayItem: + w.Err = errors.New("not implemented") + case *StructItem: + w.Err = errors.New("not implemented") + case *MapItem: + w.Err = errors.New("not implemented") + } +} + +func deserializeItem(data []byte) (StackItem, error) { + r := io.NewBinReaderFromBuf(data) + item := deserializeItemFrom(r) + if r.Err != nil { + return nil, r.Err + } + return item, nil +} + +func deserializeItemFrom(r *io.BinReader) StackItem { + var t byte + r.ReadLE(&t) + if r.Err != nil { + return nil + } + + switch stackItemType(t) { + case byteArrayT: + data := r.ReadBytes() + return NewByteArrayItem(data) + case booleanT: + var b bool + r.ReadLE(&b) + return NewBoolItem(b) + case integerT: + r.Err = errors.New("not implemented") + return nil + case arrayT: + r.Err = errors.New("not implemented") + return nil + case structT: + r.Err = errors.New("not implemented") + return nil + case mapT: + r.Err = errors.New("not implemented") + return nil + default: + r.Err = errors.New("unknown type") + return nil + } +} diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 568e47110..0fb917883 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -86,6 +86,10 @@ func New() *VM { // Register native interop hooks. vm.RegisterInteropFunc("Neo.Runtime.Log", runtimeLog, 1) vm.RegisterInteropFunc("Neo.Runtime.Notify", runtimeNotify, 1) + vm.RegisterInteropFunc("Neo.Runtime.Serialize", runtimeSerialize, 1) + vm.RegisterInteropFunc("System.Runtime.Serialize", runtimeSerialize, 1) + vm.RegisterInteropFunc("Neo.Runtime.Deserialize", runtimeDeserialize, 1) + vm.RegisterInteropFunc("System.Runtime.Deserialize", runtimeDeserialize, 1) return vm } diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 7eeb04a63..a8d94f1e1 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -197,6 +197,54 @@ func TestPushData4Good(t *testing.T) { assert.Equal(t, []byte{1, 2, 3}, vm.estack.Pop().Bytes()) } +func getSyscallProg(name string) (prog []byte) { + prog = []byte{byte(SYSCALL)} + prog = append(prog, byte(len(name))) + prog = append(prog, name...) + + return +} + +func getSerializeProg() (prog []byte) { + prog = append(prog, getSyscallProg("Neo.Runtime.Serialize")...) + prog = append(prog, getSyscallProg("Neo.Runtime.Deserialize")...) + prog = append(prog, byte(RET)) + + return +} + +func testSerialize(t *testing.T, vm *VM) { + err := vm.Step() + require.NoError(t, err) + require.Equal(t, 1, vm.estack.Len()) + require.IsType(t, (*ByteArrayItem)(nil), vm.estack.Top().value) + + err = vm.Step() + require.NoError(t, err) + require.Equal(t, 1, vm.estack.Len()) +} + +func TestSerializeBool(t *testing.T) { + vm := load(getSerializeProg()) + vm.estack.PushVal(true) + + testSerialize(t, vm) + + require.IsType(t, (*BoolItem)(nil), vm.estack.Top().value) + require.Equal(t, true, vm.estack.Top().Bool()) +} + +func TestSerializeByteArray(t *testing.T) { + vm := load(getSerializeProg()) + value := []byte{1, 2, 3} + vm.estack.PushVal(value) + + testSerialize(t, vm) + + require.IsType(t, (*ByteArrayItem)(nil), vm.estack.Top().value) + require.Equal(t, value, vm.estack.Top().Bytes()) +} + func callNTimes(n uint16) []byte { return makeProgram( PUSHBYTES2, Instruction(n), Instruction(n>>8), // little-endian From cd690803cf969ca06db49fa320cf6443f80a43b1 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 5 Nov 2019 12:07:08 +0300 Subject: [PATCH 2/6] vm: implement BigInteger item serialization --- pkg/vm/serialization.go | 12 +++++++++--- pkg/vm/stack.go | 2 +- pkg/vm/stack_item.go | 7 +++++++ pkg/vm/vm_test.go | 11 +++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/pkg/vm/serialization.go b/pkg/vm/serialization.go index 5918745ef..ffeaf05a5 100644 --- a/pkg/vm/serialization.go +++ b/pkg/vm/serialization.go @@ -2,8 +2,10 @@ package vm import ( "errors" + "math/big" "github.com/CityOfZion/neo-go/pkg/io" + "github.com/CityOfZion/neo-go/pkg/util" ) type stackItemType byte @@ -35,7 +37,8 @@ func serializeItemTo(item StackItem, w *io.BinWriter) { w.WriteLE(byte(booleanT)) w.WriteLE(t.value) case *BigIntegerItem: - w.Err = errors.New("not implemented") + w.WriteLE(byte(integerT)) + w.WriteBytes(t.Bytes()) case *InteropItem: w.Err = errors.New("not supported") case *ArrayItem: @@ -72,8 +75,11 @@ func deserializeItemFrom(r *io.BinReader) StackItem { r.ReadLE(&b) return NewBoolItem(b) case integerT: - r.Err = errors.New("not implemented") - return nil + data := r.ReadBytes() + num := new(big.Int).SetBytes(util.ArrayReverse(data)) + return &BigIntegerItem{ + value: num, + } case arrayT: r.Err = errors.New("not implemented") return nil diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index 13a6c0f75..1a230b299 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -122,7 +122,7 @@ func (e *Element) Bytes() []byte { case *ByteArrayItem: return t.value case *BigIntegerItem: - return util.ArrayReverse(t.value.Bytes()) // neoVM returns in LE + return t.Bytes() // neoVM returns in LE case *BoolItem: if t.value { return []byte{1} diff --git a/pkg/vm/stack_item.go b/pkg/vm/stack_item.go index 925676a67..c2faabf0f 100644 --- a/pkg/vm/stack_item.go +++ b/pkg/vm/stack_item.go @@ -6,6 +6,8 @@ import ( "fmt" "math/big" "reflect" + + "github.com/CityOfZion/neo-go/pkg/util" ) // A StackItem represents the "real" value that is pushed on the stack. @@ -131,6 +133,11 @@ func NewBigIntegerItem(value int) *BigIntegerItem { } } +// Bytes converts i to a slice of bytes. +func (i *BigIntegerItem) Bytes() []byte { + return util.ArrayReverse(i.value.Bytes()) +} + // Value implements StackItem interface. func (i *BigIntegerItem) Value() interface{} { return i.value diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index a8d94f1e1..2e116b5b7 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -245,6 +245,17 @@ func TestSerializeByteArray(t *testing.T) { require.Equal(t, value, vm.estack.Top().Bytes()) } +func TestSerializeInteger(t *testing.T) { + vm := load(getSerializeProg()) + value := int64(123) + vm.estack.PushVal(value) + + testSerialize(t, vm) + + require.IsType(t, (*BigIntegerItem)(nil), vm.estack.Top().value) + require.Equal(t, value, vm.estack.Top().BigInt().Int64()) +} + func callNTimes(n uint16) []byte { return makeProgram( PUSHBYTES2, Instruction(n), Instruction(n>>8), // little-endian From e1d019e08723ed1c67ee8eec78b05225faa10952 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 5 Nov 2019 11:57:55 +0300 Subject: [PATCH 3/6] vm: implement Map item serialization --- pkg/vm/serialization.go | 30 +++++++++++++++++++++++++----- pkg/vm/vm_test.go | 14 ++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/pkg/vm/serialization.go b/pkg/vm/serialization.go index ffeaf05a5..6c54c658f 100644 --- a/pkg/vm/serialization.go +++ b/pkg/vm/serialization.go @@ -21,14 +21,20 @@ const ( func serializeItem(item StackItem) ([]byte, error) { w := io.NewBufBinWriter() - serializeItemTo(item, w.BinWriter) + serializeItemTo(item, w.BinWriter, make(map[StackItem]bool)) if w.Err != nil { return nil, w.Err } return w.Bytes(), nil } -func serializeItemTo(item StackItem, w *io.BinWriter) { +func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) { + if seen[item] { + w.Err = errors.New("recursive structures are not supported") + return + } + seen[item] = true + switch t := item.(type) { case *ByteArrayItem: w.WriteLE(byte(byteArrayT)) @@ -46,7 +52,12 @@ func serializeItemTo(item StackItem, w *io.BinWriter) { case *StructItem: w.Err = errors.New("not implemented") case *MapItem: - w.Err = errors.New("not implemented") + w.WriteLE(byte(mapT)) + w.WriteVarUint(uint64(len(t.value))) + for k, v := range t.value { + serializeItemTo(v, w, seen) + serializeItemTo(makeStackItem(k), w, seen) + } } } @@ -87,8 +98,17 @@ func deserializeItemFrom(r *io.BinReader) StackItem { r.Err = errors.New("not implemented") return nil case mapT: - r.Err = errors.New("not implemented") - return nil + size := int(r.ReadVarUint()) + m := NewMapItem() + for i := 0; i < size; i++ { + value := deserializeItemFrom(r) + key := deserializeItemFrom(r) + if r.Err != nil { + break + } + m.Add(key, value) + } + return m default: r.Err = errors.New("unknown type") return nil diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 2e116b5b7..589bc0521 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -256,6 +256,20 @@ func TestSerializeInteger(t *testing.T) { require.Equal(t, value, vm.estack.Top().BigInt().Int64()) } +func TestSerializeMap(t *testing.T) { + vm := load(getSerializeProg()) + item := NewMapItem() + item.Add(makeStackItem(true), makeStackItem([]byte{1, 2, 3})) + item.Add(makeStackItem([]byte{0}), makeStackItem(false)) + + vm.estack.Push(&Element{value: item}) + + testSerialize(t, vm) + + require.IsType(t, (*MapItem)(nil), vm.estack.Top().value) + require.Equal(t, item.value, vm.estack.Top().value.(*MapItem).value) +} + func callNTimes(n uint16) []byte { return makeProgram( PUSHBYTES2, Instruction(n), Instruction(n>>8), // little-endian From 59f9c2bddc14ba8ed46e4fd56fff3a830c3116c1 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 5 Nov 2019 12:25:15 +0300 Subject: [PATCH 4/6] vm: implement Array and Struct item serialization --- pkg/vm/serialization.go | 34 +++++++++++++++++++++---------- pkg/vm/vm_test.go | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/pkg/vm/serialization.go b/pkg/vm/serialization.go index 6c54c658f..6fd71b5cf 100644 --- a/pkg/vm/serialization.go +++ b/pkg/vm/serialization.go @@ -47,10 +47,19 @@ func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) { w.WriteBytes(t.Bytes()) case *InteropItem: w.Err = errors.New("not supported") - case *ArrayItem: - w.Err = errors.New("not implemented") - case *StructItem: - w.Err = errors.New("not implemented") + case *ArrayItem, *StructItem: + _, isArray := t.(*ArrayItem) + if isArray { + w.WriteLE(byte(arrayT)) + } else { + w.WriteLE(byte(structT)) + } + + arr := t.Value().([]StackItem) + w.WriteVarUint(uint64(len(arr))) + for i := range arr { + serializeItemTo(arr[i], w, seen) + } case *MapItem: w.WriteLE(byte(mapT)) w.WriteVarUint(uint64(len(t.value))) @@ -91,12 +100,17 @@ func deserializeItemFrom(r *io.BinReader) StackItem { return &BigIntegerItem{ value: num, } - case arrayT: - r.Err = errors.New("not implemented") - return nil - case structT: - r.Err = errors.New("not implemented") - return nil + case arrayT, structT: + size := int(r.ReadVarUint()) + arr := make([]StackItem, size) + for i := 0; i < size; i++ { + arr[i] = deserializeItemFrom(r) + } + + if stackItemType(t) == arrayT { + return &ArrayItem{value: arr} + } + return &StructItem{value: arr} case mapT: size := int(r.ReadVarUint()) m := NewMapItem() diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 589bc0521..0af1144a0 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -256,6 +256,50 @@ func TestSerializeInteger(t *testing.T) { require.Equal(t, value, vm.estack.Top().BigInt().Int64()) } +func TestSerializeArray(t *testing.T) { + vm := load(getSerializeProg()) + item := NewArrayItem([]StackItem{ + makeStackItem(true), + makeStackItem(123), + NewMapItem(), + }) + + vm.estack.Push(&Element{value: item}) + + testSerialize(t, vm) + + require.IsType(t, (*ArrayItem)(nil), vm.estack.Top().value) + require.Equal(t, item.value, vm.estack.Top().Array()) +} + +func TestSerializeArrayBad(t *testing.T) { + vm := load(getSerializeProg()) + item := NewArrayItem(makeArrayOfFalses(2)) + item.value[1] = item + + vm.estack.Push(&Element{value: item}) + + err := vm.Step() + require.Error(t, err) + require.True(t, vm.HasFailed()) +} + +func TestSerializeStruct(t *testing.T) { + vm := load(getSerializeProg()) + item := NewStructItem([]StackItem{ + makeStackItem(true), + makeStackItem(123), + NewMapItem(), + }) + + vm.estack.Push(&Element{value: item}) + + testSerialize(t, vm) + + require.IsType(t, (*StructItem)(nil), vm.estack.Top().value) + require.Equal(t, item.value, vm.estack.Top().Array()) +} + func TestSerializeMap(t *testing.T) { vm := load(getSerializeProg()) item := NewMapItem() From 412582dc7881a5568e2e286ca2710518ce6b4cee Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 5 Nov 2019 17:04:14 +0300 Subject: [PATCH 5/6] vm: add more serialization tests --- pkg/vm/vm_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 0af1144a0..599b930f6 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -300,6 +300,19 @@ func TestSerializeStruct(t *testing.T) { require.Equal(t, item.value, vm.estack.Top().Array()) } +func TestDeserializeUnknown(t *testing.T) { + prog := append(getSyscallProg("Neo.Runtime.Deserialize"), byte(RET)) + vm := load(prog) + + data, err := serializeItem(NewBigIntegerItem(123)) + require.NoError(t, err) + + data[0] = 0xFF + vm.estack.PushVal(data) + + checkVMFailed(t, vm) +} + func TestSerializeMap(t *testing.T) { vm := load(getSerializeProg()) item := NewMapItem() @@ -314,6 +327,17 @@ func TestSerializeMap(t *testing.T) { require.Equal(t, item.value, vm.estack.Top().value.(*MapItem).value) } +func TestSerializeInterop(t *testing.T) { + vm := load(getSerializeProg()) + item := NewInteropItem("kek") + + vm.estack.Push(&Element{value: item}) + + err := vm.Step() + require.Error(t, err) + require.True(t, vm.HasFailed()) +} + func callNTimes(n uint16) []byte { return makeProgram( PUSHBYTES2, Instruction(n), Instruction(n>>8), // little-endian From ed758458d5f3fc33e42e7a71bcce28198a5e22dd Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 5 Nov 2019 17:10:52 +0300 Subject: [PATCH 6/6] vm: implement serialization interops in core package --- pkg/core/interop_neo.go | 10 ++++++++++ pkg/core/interops.go | 8 ++++---- pkg/vm/interop.go | 8 ++++---- pkg/vm/vm.go | 8 ++++---- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 0dc9a1a75..e1211cdfb 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -736,3 +736,13 @@ func (ic *interopContext) assetRenew(v *vm.VM) error { v.Estack().PushVal(expiration) return nil } + +// runtimeSerialize serializes top stack item into a ByteArray. +func (ic *interopContext) runtimeSerialize(v *vm.VM) error { + return vm.RuntimeSerialize(v) +} + +// runtimeDeserialize deserializes ByteArray from a stack into an item. +func (ic *interopContext) runtimeDeserialize(v *vm.VM) error { + return vm.RuntimeDeserialize(v) +} diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 9f7782a86..1df944eda 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -64,8 +64,8 @@ func (ic *interopContext) getSystemInteropMap() map[string]vm.InteropFuncPrice { "System.Storage.PutEx": {Func: ic.storagePutEx, Price: 0}, "System.StorageContext.AsReadOnly": {Func: ic.storageContextAsReadOnly, Price: 1}, "System.Transaction.GetHash": {Func: ic.txGetHash, Price: 1}, - // "System.Runtime.Deserialize": {Func: ic.runtimeDeserialize, Price: 1}, - // "System.Runtime.Serialize": {Func: ic.runtimeSerialize, Price: 1}, + "System.Runtime.Deserialize": {Func: ic.runtimeDeserialize, Price: 1}, + "System.Runtime.Serialize": {Func: ic.runtimeSerialize, Price: 1}, } } @@ -148,8 +148,8 @@ func (ic *interopContext) getNeoInteropMap() map[string]vm.InteropFuncPrice { // "Neo.Iterator.Key": {Func: ic.iteratorKey, Price: 1}, // "Neo.Iterator.Keys": {Func: ic.iteratorKeys, Price: 1}, // "Neo.Iterator.Values": {Func: ic.iteratorValues, Price: 1}, - // "Neo.Runtime.Deserialize": {Func: ic.runtimeDeserialize, Price: 1}, - // "Neo.Runtime.Serialize": {Func: ic.runtimeSerialize, Price: 1}, + "Neo.Runtime.Deserialize": {Func: ic.runtimeDeserialize, Price: 1}, + "Neo.Runtime.Serialize": {Func: ic.runtimeSerialize, Price: 1}, // "Neo.Storage.Find": {Func: ic.storageFind, Price: 1}, // "Neo.Witness.GetVerificationScript": {Func: ic.witnessGetVerificationScript, Price: 100}, diff --git a/pkg/vm/interop.go b/pkg/vm/interop.go index 8e83e0f11..c96477f6f 100644 --- a/pkg/vm/interop.go +++ b/pkg/vm/interop.go @@ -22,8 +22,8 @@ func runtimeNotify(vm *VM) error { return nil } -// runtimeSerialize handles syscalls System.Runtime.Serialize and Neo.Runtime.Serialize. -func runtimeSerialize(vm *VM) error { +// RuntimeSerialize handles syscalls System.Runtime.Serialize and Neo.Runtime.Serialize. +func RuntimeSerialize(vm *VM) error { item := vm.Estack().Pop() data, err := serializeItem(item.value) if err != nil { @@ -37,8 +37,8 @@ func runtimeSerialize(vm *VM) error { return nil } -// runtimeDeserialize handles syscalls System.Runtime.Deserialize and Neo.Runtime.Deserialize. -func runtimeDeserialize(vm *VM) error { +// RuntimeDeserialize handles syscalls System.Runtime.Deserialize and Neo.Runtime.Deserialize. +func RuntimeDeserialize(vm *VM) error { data := vm.Estack().Pop().Bytes() item, err := deserializeItem(data) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 0fb917883..0043afa11 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -86,10 +86,10 @@ func New() *VM { // Register native interop hooks. vm.RegisterInteropFunc("Neo.Runtime.Log", runtimeLog, 1) vm.RegisterInteropFunc("Neo.Runtime.Notify", runtimeNotify, 1) - vm.RegisterInteropFunc("Neo.Runtime.Serialize", runtimeSerialize, 1) - vm.RegisterInteropFunc("System.Runtime.Serialize", runtimeSerialize, 1) - vm.RegisterInteropFunc("Neo.Runtime.Deserialize", runtimeDeserialize, 1) - vm.RegisterInteropFunc("System.Runtime.Deserialize", runtimeDeserialize, 1) + vm.RegisterInteropFunc("Neo.Runtime.Serialize", RuntimeSerialize, 1) + vm.RegisterInteropFunc("System.Runtime.Serialize", RuntimeSerialize, 1) + vm.RegisterInteropFunc("Neo.Runtime.Deserialize", RuntimeDeserialize, 1) + vm.RegisterInteropFunc("System.Runtime.Deserialize", RuntimeDeserialize, 1) return vm }