diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index ba62874f6..08e42b6ec 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -61,8 +61,6 @@ func TestSyscallExecution(t *testing.T) { sigs := "[]interop.Signature{" + sig + "}" sctx := "storage.Context{}" interops := map[string]syscallTestCase{ - "binary.Deserialize": {interopnames.SystemBinaryDeserialize, []string{b}, false}, - "binary.Serialize": {interopnames.SystemBinarySerialize, []string{"10"}, false}, "contract.Call": {interopnames.SystemContractCall, []string{u160, `"m"`, "1", "3"}, false}, "contract.CreateMultisigAccount": {interopnames.SystemContractCreateMultisigAccount, []string{"1", pubs}, false}, "contract.CreateStandardAccount": {interopnames.SystemContractCreateStandardAccount, []string{pub}, false}, diff --git a/pkg/core/interop/binary/encode.go b/pkg/core/interop/binary/encode.go deleted file mode 100644 index 75ab0d515..000000000 --- a/pkg/core/interop/binary/encode.go +++ /dev/null @@ -1,16 +0,0 @@ -package binary - -import ( - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/vm" -) - -// Serialize serializes top stack item into a ByteArray. -func Serialize(ic *interop.Context) error { - return vm.RuntimeSerialize(ic.VM) -} - -// Deserialize deserializes ByteArray from a stack into an item. -func Deserialize(ic *interop.Context) error { - return vm.RuntimeDeserialize(ic.VM) -} diff --git a/pkg/core/interop/binary/encode_test.go b/pkg/core/interop/binary/encode_test.go deleted file mode 100644 index 3318b3588..000000000 --- a/pkg/core/interop/binary/encode_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package binary - -import ( - "math/big" - "testing" - - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/io" - "github.com/nspcc-dev/neo-go/pkg/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - "github.com/stretchr/testify/require" -) - -func TestRuntimeSerialize(t *testing.T) { - t.Run("recursive", func(t *testing.T) { - arr := stackitem.NewArray(nil) - arr.Append(arr) - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(arr) - require.Error(t, Serialize(ic)) - }) - t.Run("big item", func(t *testing.T) { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(make([]byte, stackitem.MaxSize)) - require.Error(t, Serialize(ic)) - }) - t.Run("good", func(t *testing.T) { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(42) - require.NoError(t, Serialize(ic)) - - w := io.NewBufBinWriter() - stackitem.EncodeBinaryStackItem(stackitem.Make(42), w.BinWriter) - require.NoError(t, w.Err) - - encoded := w.Bytes() - require.Equal(t, encoded, ic.VM.Estack().Pop().Bytes()) - - ic.VM.Estack().PushVal(encoded) - require.NoError(t, Deserialize(ic)) - require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().BigInt()) - - t.Run("bad", func(t *testing.T) { - encoded[0] ^= 0xFF - ic.VM.Estack().PushVal(encoded) - require.Error(t, Deserialize(ic)) - }) - }) -} diff --git a/pkg/core/interop/interopnames/names.go b/pkg/core/interop/interopnames/names.go index 1858cbacc..127d05121 100644 --- a/pkg/core/interop/interopnames/names.go +++ b/pkg/core/interop/interopnames/names.go @@ -2,8 +2,6 @@ package interopnames // Names of all used interops. const ( - SystemBinaryDeserialize = "System.Binary.Deserialize" - SystemBinarySerialize = "System.Binary.Serialize" SystemCallbackCreate = "System.Callback.Create" SystemCallbackCreateFromMethod = "System.Callback.CreateFromMethod" SystemCallbackCreateFromSyscall = "System.Callback.CreateFromSyscall" @@ -46,8 +44,6 @@ const ( ) var names = []string{ - SystemBinaryDeserialize, - SystemBinarySerialize, SystemCallbackCreate, SystemCallbackCreateFromMethod, SystemCallbackCreateFromSyscall, diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index ff2d090f8..bd3084beb 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -316,6 +316,7 @@ func TestStorageDelete(t *testing.T) { // getTestContractState returns 2 contracts second of which is allowed to call the first. func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) { mgmtHash := bc.ManagementContractHash() + stdHash := bc.contracts.Std.Hash w := io.NewBufBinWriter() emit.Opcodes(w.BinWriter, opcode.ABORT) @@ -339,16 +340,20 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) { emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.SUB, opcode.CONVERT, opcode.Opcode(stackitem.BooleanT), opcode.RET) deployOff := w.Len() - emit.Opcodes(w.BinWriter, opcode.SWAP, opcode.JMPIF, 2+8+1+1+5+3) - emit.String(w.BinWriter, "create") // 8 bytes - emit.Int(w.BinWriter, 2) // 1 byte - emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte - emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize) // 5 bytes - emit.Opcodes(w.BinWriter, opcode.CALL, 3+8+1+1+5+3, opcode.RET) - emit.String(w.BinWriter, "update") // 8 bytes - emit.Int(w.BinWriter, 2) // 1 byte - emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte - emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize) // 5 bytes + emit.Opcodes(w.BinWriter, opcode.SWAP, opcode.JMPIF, 2+8+1+1+1+1+39+3) + emit.String(w.BinWriter, "create") // 8 bytes + emit.Int(w.BinWriter, 2) // 1 byte + emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte + emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`) + emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`) + emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes + emit.Opcodes(w.BinWriter, opcode.CALL, 3+8+1+1+1+1+39+3, opcode.RET) + emit.String(w.BinWriter, "update") // 8 bytes + emit.Int(w.BinWriter, 2) // 1 byte + emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte + emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`) + emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`) + emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes emit.Opcodes(w.BinWriter, opcode.CALL, 3, opcode.RET) putValOff := w.Len() emit.String(w.BinWriter, "initial") diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 58bc3840b..3e871e239 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -10,7 +10,6 @@ package core import ( "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/binary" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract" "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" @@ -31,8 +30,6 @@ func SpawnVM(ic *interop.Context) *vm.VM { // All lists are sorted, keep 'em this way, please. var systemInterops = []interop.Function{ - {Name: interopnames.SystemBinaryDeserialize, Func: binary.Deserialize, Price: 1 << 14, ParamCount: 1}, - {Name: interopnames.SystemBinarySerialize, Func: binary.Serialize, Price: 1 << 12, ParamCount: 1}, {Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15, RequiredFlags: callflag.ReadStates | callflag.AllowCall, ParamCount: 4}, {Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1}, diff --git a/pkg/core/native/std_test.go b/pkg/core/native/std_test.go index ea89b9530..f5a3bf15c 100644 --- a/pkg/core/native/std_test.go +++ b/pkg/core/native/std_test.go @@ -2,14 +2,17 @@ package native import ( "encoding/base64" + "encoding/hex" "math" "math/big" "testing" "github.com/mr-tron/base58" "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -190,3 +193,136 @@ func TestStdLibEncodeDecode(t *testing.T) { }) }) } + +func TestStdLibSerialize(t *testing.T) { + s := newStd() + ic := &interop.Context{VM: vm.New()} + + t.Run("recursive", func(t *testing.T) { + arr := stackitem.NewArray(nil) + arr.Append(arr) + require.Panics(t, func() { + _ = s.serialize(ic, []stackitem.Item{arr}) + }) + }) + t.Run("big item", func(t *testing.T) { + require.Panics(t, func() { + _ = s.serialize(ic, []stackitem.Item{stackitem.NewByteArray(make([]byte, stackitem.MaxSize))}) + }) + }) + t.Run("good", func(t *testing.T) { + var ( + actualSerialized stackitem.Item + actualDeserialized stackitem.Item + ) + require.NotPanics(t, func() { + actualSerialized = s.serialize(ic, []stackitem.Item{stackitem.Make(42)}) + }) + + w := io.NewBufBinWriter() + stackitem.EncodeBinaryStackItem(stackitem.Make(42), w.BinWriter) + require.NoError(t, w.Err) + + encoded := w.Bytes() + require.Equal(t, stackitem.Make(encoded), actualSerialized) + + require.NotPanics(t, func() { + actualDeserialized = s.deserialize(ic, []stackitem.Item{actualSerialized}) + }) + require.Equal(t, stackitem.Make(42), actualDeserialized) + + t.Run("bad", func(t *testing.T) { + encoded[0] ^= 0xFF + require.Panics(t, func() { + _ = s.deserialize(ic, []stackitem.Item{stackitem.Make(encoded)}) + }) + }) + }) +} + +func TestStdLibSerializeDeserialize(t *testing.T) { + s := newStd() + ic := &interop.Context{VM: vm.New()} + var actual stackitem.Item + + checkSerializeDeserialize := func(t *testing.T, value interface{}, expected stackitem.Item) { + require.NotPanics(t, func() { + actual = s.serialize(ic, []stackitem.Item{stackitem.Make(value)}) + }) + require.NotPanics(t, func() { + actual = s.deserialize(ic, []stackitem.Item{actual}) + }) + require.Equal(t, expected, actual) + } + + t.Run("Bool", func(t *testing.T) { + checkSerializeDeserialize(t, true, stackitem.NewBool(true)) + }) + t.Run("ByteArray", func(t *testing.T) { + checkSerializeDeserialize(t, []byte{1, 2, 3}, stackitem.NewByteArray([]byte{1, 2, 3})) + }) + t.Run("Integer", func(t *testing.T) { + checkSerializeDeserialize(t, 48, stackitem.NewBigInteger(big.NewInt(48))) + }) + t.Run("Array", func(t *testing.T) { + arr := stackitem.NewArray([]stackitem.Item{ + stackitem.Make(true), + stackitem.Make(123), + stackitem.NewMap()}) + checkSerializeDeserialize(t, arr, arr) + }) + t.Run("Struct", func(t *testing.T) { + st := stackitem.NewStruct([]stackitem.Item{ + stackitem.Make(true), + stackitem.Make(123), + stackitem.NewMap(), + }) + checkSerializeDeserialize(t, st, st) + }) + t.Run("Map", func(t *testing.T) { + item := stackitem.NewMap() + item.Add(stackitem.Make(true), stackitem.Make([]byte{1, 2, 3})) + item.Add(stackitem.Make([]byte{0}), stackitem.Make(false)) + checkSerializeDeserialize(t, item, item) + }) + t.Run("Serialize MapCompat", func(t *testing.T) { + resHex := "480128036b6579280576616c7565" + res, err := hex.DecodeString(resHex) + require.NoError(t, err) + + item := stackitem.NewMap() + item.Add(stackitem.Make([]byte("key")), stackitem.Make([]byte("value"))) + require.NotPanics(t, func() { + actual = s.serialize(ic, []stackitem.Item{stackitem.Make(item)}) + }) + bytes, err := actual.TryBytes() + require.NoError(t, err) + assert.Equal(t, res, bytes) + }) + t.Run("Serialize Interop", func(t *testing.T) { + require.Panics(t, func() { + actual = s.serialize(ic, []stackitem.Item{stackitem.NewInterop("kek")}) + }) + }) + t.Run("Serialize Array bad", func(t *testing.T) { + item := stackitem.NewArray([]stackitem.Item{stackitem.NewBool(true), stackitem.NewBool(true)}) + item.Value().([]stackitem.Item)[1] = item + require.Panics(t, func() { + actual = s.serialize(ic, []stackitem.Item{item}) + }) + }) + t.Run("Deserialize unknown", func(t *testing.T) { + data, err := stackitem.SerializeItem(stackitem.NewBigInteger(big.NewInt(123))) + require.NoError(t, err) + + data[0] = 0xFF + require.Panics(t, func() { + actual = s.deserialize(ic, []stackitem.Item{stackitem.Make(data)}) + }) + }) + t.Run("Deserialize not a byte array", func(t *testing.T) { + require.Panics(t, func() { + actual = s.deserialize(ic, []stackitem.Item{stackitem.NewInterop(nil)}) + }) + }) +} diff --git a/pkg/core/native_oracle_test.go b/pkg/core/native_oracle_test.go index 16f792c0c..2f692f705 100644 --- a/pkg/core/native_oracle_test.go +++ b/pkg/core/native_oracle_test.go @@ -29,7 +29,7 @@ import ( ) // getTestContractState returns test contract which uses oracles. -func getOracleContractState(h util.Uint160) *state.Contract { +func getOracleContractState(h util.Uint160, stdHash util.Uint160) *state.Contract { w := io.NewBufBinWriter() emit.Int(w.BinWriter, 5) emit.Opcodes(w.BinWriter, opcode.PACK) @@ -49,7 +49,9 @@ func getOracleContractState(h util.Uint160) *state.Contract { emit.Opcodes(w.BinWriter, opcode.ABORT) emit.Int(w.BinWriter, 4) // url, userData, code, result emit.Opcodes(w.BinWriter, opcode.PACK) - emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize) + emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`) + emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`) + emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes emit.String(w.BinWriter, "lastOracleResponse") emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext) emit.Syscall(w.BinWriter, interopnames.SystemStoragePut) @@ -117,7 +119,7 @@ func TestOracle_Request(t *testing.T) { bc := newTestChain(t) orc := bc.contracts.Oracle - cs := getOracleContractState(orc.Hash) + cs := getOracleContractState(orc.Hash, bc.contracts.Std.Hash) require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) gasForResponse := int64(2000_1234) diff --git a/pkg/core/oracle_test.go b/pkg/core/oracle_test.go index 6cb63922c..968296a3b 100644 --- a/pkg/core/oracle_test.go +++ b/pkg/core/oracle_test.go @@ -130,7 +130,7 @@ func TestOracle(t *testing.T) { orc1.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset) orc2.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset) - cs := getOracleContractState(bc.contracts.Oracle.Hash) + cs := getOracleContractState(bc.contracts.Oracle.Hash, bc.contracts.Std.Hash) require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) putOracleRequest(t, cs.Hash, bc, "http://get.1234", nil, "handle", []byte{}, 10_000_000) @@ -271,7 +271,7 @@ func TestOracleFull(t *testing.T) { orc.OnTransaction = func(tx *transaction.Transaction) { _ = mp.Add(tx, bc) } bc.SetOracle(orc) - cs := getOracleContractState(bc.contracts.Oracle.Hash) + cs := getOracleContractState(bc.contracts.Oracle.Hash, bc.contracts.Std.Hash) require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) go bc.Run() diff --git a/pkg/interop/binary/binary.go b/pkg/interop/binary/binary.go deleted file mode 100644 index a96f5f527..000000000 --- a/pkg/interop/binary/binary.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Package binary provides binary serialization routines. -*/ -package binary - -import ( - "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" -) - -// Serialize serializes any given item into a byte slice. It works for all -// regular VM types (not ones from interop package) and allows to save them in -// storage or pass into Notify and then Deserialize them on the next run or in -// the external event receiver. It uses `System.Binary.Serialize` syscall. -func Serialize(item interface{}) []byte { - return neogointernal.Syscall1("System.Binary.Serialize", item).([]byte) -} - -// Deserialize unpacks previously serialized value from a byte slice, it's the -// opposite of Serialize. It uses `System.Binary.Deserialize` syscall. -func Deserialize(b []byte) interface{} { - return neogointernal.Syscall1("System.Binary.Deserialize", b) -} diff --git a/pkg/vm/cli/cli_test.go b/pkg/vm/cli/cli_test.go index b32ce15a0..477e996ec 100644 --- a/pkg/vm/cli/cli_test.go +++ b/pkg/vm/cli/cli_test.go @@ -299,8 +299,8 @@ func TestRunWithDifferentArguments(t *testing.T) { func TestPrintOps(t *testing.T) { w := io.NewBufBinWriter() - emit.Opcodes(w.BinWriter, opcode.PUSH1) - emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize) + emit.String(w.BinWriter, "log") + emit.Syscall(w.BinWriter, interopnames.SystemRuntimeLog) emit.Instruction(w.BinWriter, opcode.PUSHDATA1, []byte{3, 1, 2, 3}) script := w.Bytes() e := newTestVMCLI(t) @@ -312,9 +312,9 @@ func TestPrintOps(t *testing.T) { e.checkNextLine(t, ".*no program loaded") e.checkNextLine(t, fmt.Sprintf("READY: loaded %d instructions", len(script))) e.checkNextLine(t, "INDEX.*OPCODE.*PARAMETER") - e.checkNextLine(t, "0.*PUSH1") - e.checkNextLine(t, "1.*SYSCALL.*System\\.Binary\\.Serialize") - e.checkNextLine(t, "6.*PUSHDATA1.*010203") + e.checkNextLine(t, "0.*PUSHDATA1.*6c6f67") + e.checkNextLine(t, "5.*SYSCALL.*System\\.Runtime\\.Log") + e.checkNextLine(t, "10.*PUSHDATA1.*010203") } func TestLoadAbort(t *testing.T) { diff --git a/pkg/vm/interop.go b/pkg/vm/interop.go index 129357886..d4e153c0a 100644 --- a/pkg/vm/interop.go +++ b/pkg/vm/interop.go @@ -19,10 +19,6 @@ type interopIDFuncPrice struct { } var defaultVMInterops = []interopIDFuncPrice{ - {ID: interopnames.ToID([]byte(interopnames.SystemBinaryDeserialize)), - Func: RuntimeDeserialize, Price: 1 << 14}, - {ID: interopnames.ToID([]byte(interopnames.SystemBinarySerialize)), - Func: RuntimeSerialize, Price: 1 << 12}, {ID: interopnames.ToID([]byte(interopnames.SystemRuntimeLog)), Func: runtimeLog, Price: 1 << 15, RequiredFlags: callflag.AllowNotify}, {ID: interopnames.ToID([]byte(interopnames.SystemRuntimeNotify)), @@ -68,35 +64,6 @@ func runtimeNotify(vm *VM) error { return nil } -// RuntimeSerialize handles System.Binary.Serialize syscall. -func RuntimeSerialize(vm *VM) error { - item := vm.Estack().Pop() - data, err := stackitem.SerializeItem(item.value) - if err != nil { - return err - } else if len(data) > stackitem.MaxSize { - return errors.New("too big item") - } - - vm.Estack().PushVal(data) - - return nil -} - -// RuntimeDeserialize handles System.Binary.Deserialize syscall. -func RuntimeDeserialize(vm *VM) error { - data := vm.Estack().Pop().Bytes() - - item, err := stackitem.DeserializeItem(data) - if err != nil { - return err - } - - vm.Estack().Push(&Element{value: item}) - - return nil -} - // init sorts the global defaultVMInterops value. func init() { sort.Slice(defaultVMInterops, func(i, j int) bool { diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 937dca127..b937a9282 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -3,7 +3,6 @@ package vm import ( "bytes" "encoding/binary" - "encoding/hex" "errors" "fmt" "math" @@ -531,168 +530,6 @@ func getSyscallProg(name string) (prog []byte) { return buf.Bytes() } -func getSerializeProg() (prog []byte) { - prog = append(prog, getSyscallProg(interopnames.SystemBinarySerialize)...) - prog = append(prog, getSyscallProg(interopnames.SystemBinaryDeserialize)...) - prog = append(prog, byte(opcode.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, (*stackitem.ByteArray)(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, (*stackitem.Bool)(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, (*stackitem.ByteArray)(nil), vm.estack.Top().value) - 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, (*stackitem.BigInteger)(nil), vm.estack.Top().value) - require.Equal(t, value, vm.estack.Top().BigInt().Int64()) -} - -func TestSerializeArray(t *testing.T) { - vm := load(getSerializeProg()) - item := stackitem.NewArray([]stackitem.Item{ - stackitem.Make(true), - stackitem.Make(123), - stackitem.NewMap(), - }) - - vm.estack.Push(&Element{value: item}) - - testSerialize(t, vm) - - require.IsType(t, (*stackitem.Array)(nil), vm.estack.Top().value) - require.Equal(t, item.Value().([]stackitem.Item), vm.estack.Top().Array()) -} - -func TestSerializeArrayBad(t *testing.T) { - vm := load(getSerializeProg()) - item := stackitem.NewArray(makeArrayOfType(2, stackitem.BooleanT)) - item.Value().([]stackitem.Item)[1] = item - - vm.estack.Push(&Element{value: item}) - - err := vm.Step() - require.Error(t, err) - require.True(t, vm.HasFailed()) -} - -func TestSerializeDupInteger(t *testing.T) { - prog := makeProgram( - opcode.PUSH0, opcode.NEWARRAY, opcode.INITSSLOT, 1, - opcode.DUP, opcode.PUSH2, opcode.DUP, opcode.STSFLD0, opcode.APPEND, - opcode.DUP, opcode.LDSFLD0, opcode.APPEND, - ) - vm := load(append(prog, getSerializeProg()...)) - - runVM(t, vm) -} - -func TestSerializeStruct(t *testing.T) { - vm := load(getSerializeProg()) - item := stackitem.NewStruct([]stackitem.Item{ - stackitem.Make(true), - stackitem.Make(123), - stackitem.NewMap(), - }) - - vm.estack.Push(&Element{value: item}) - - testSerialize(t, vm) - - require.IsType(t, (*stackitem.Struct)(nil), vm.estack.Top().value) - require.Equal(t, item.Value().([]stackitem.Item), vm.estack.Top().Array()) -} - -func TestDeserializeUnknown(t *testing.T) { - prog := append(getSyscallProg(interopnames.SystemBinaryDeserialize), byte(opcode.RET)) - - data, err := stackitem.SerializeItem(stackitem.NewBigInteger(big.NewInt(123))) - require.NoError(t, err) - - data[0] = 0xFF - - runWithArgs(t, prog, nil, data) -} - -func TestSerializeMap(t *testing.T) { - vm := load(getSerializeProg()) - item := stackitem.NewMap() - item.Add(stackitem.Make(true), stackitem.Make([]byte{1, 2, 3})) - item.Add(stackitem.Make([]byte{0}), stackitem.Make(false)) - - vm.estack.Push(&Element{value: item}) - - testSerialize(t, vm) - - require.IsType(t, (*stackitem.Map)(nil), vm.estack.Top().value) - require.Equal(t, item.Value(), vm.estack.Top().value.(*stackitem.Map).Value()) -} - -func TestSerializeMapCompat(t *testing.T) { - resHex := "480128036b6579280576616c7565" - res, err := hex.DecodeString(resHex) - require.NoError(t, err) - - // Create a map, push key and value, add KV to map, serialize. - buf := io.NewBufBinWriter() - emit.Opcodes(buf.BinWriter, opcode.NEWMAP) - emit.Opcodes(buf.BinWriter, opcode.DUP) - emit.Bytes(buf.BinWriter, []byte("key")) - emit.Bytes(buf.BinWriter, []byte("value")) - emit.Opcodes(buf.BinWriter, opcode.SETITEM) - emit.Syscall(buf.BinWriter, interopnames.SystemBinarySerialize) - require.NoError(t, buf.Err) - - vm := load(buf.Bytes()) - runVM(t, vm) - assert.Equal(t, res, vm.estack.Pop().Bytes()) -} - -func TestSerializeInterop(t *testing.T) { - vm := load(getSerializeProg()) - item := stackitem.NewInterop("kek") - - vm.estack.Push(&Element{value: item}) - - err := vm.Step() - require.Error(t, err) - require.True(t, vm.HasFailed()) -} - func getTestCallFlagsFunc(syscall []byte, flags callflag.CallFlag, result interface{}) func(t *testing.T) { return func(t *testing.T) { script := append([]byte{byte(opcode.SYSCALL)}, syscall...)