forked from TrueCloudLab/neoneo-go
core: remove System.Binary.[Serialize, Deserialize] syscalls
And move their tests to native StdLib.
This commit is contained in:
parent
5c9c168ee5
commit
14ade42101
13 changed files with 163 additions and 312 deletions
|
@ -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},
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
})
|
||||
})
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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)})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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...)
|
||||
|
|
Loading…
Reference in a new issue