2018-03-30 16:15:06 +00:00
|
|
|
package vm
|
|
|
|
|
2018-04-10 09:45:31 +00:00
|
|
|
import (
|
2019-11-05 08:36:13 +00:00
|
|
|
"errors"
|
2018-04-10 09:45:31 +00:00
|
|
|
"fmt"
|
2019-12-18 16:49:56 +00:00
|
|
|
"sort"
|
2020-04-15 14:13:50 +00:00
|
|
|
|
2020-08-13 07:41:33 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
2020-12-29 10:45:49 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
2020-06-03 12:55:06 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
2018-04-10 09:45:31 +00:00
|
|
|
)
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2020-07-28 13:38:00 +00:00
|
|
|
// interopIDFuncPrice adds an ID to the InteropFuncPrice.
|
|
|
|
type interopIDFuncPrice struct {
|
|
|
|
ID uint32
|
|
|
|
Func func(vm *VM) error
|
2020-07-22 16:40:32 +00:00
|
|
|
Price int64
|
2020-12-29 10:45:49 +00:00
|
|
|
RequiredFlags callflag.CallFlag
|
2019-12-18 16:49:56 +00:00
|
|
|
}
|
|
|
|
|
2020-07-28 13:38:00 +00:00
|
|
|
var defaultVMInterops = []interopIDFuncPrice{
|
2020-08-14 10:50:52 +00:00
|
|
|
{ID: interopnames.ToID([]byte(interopnames.SystemBinaryDeserialize)),
|
2020-12-29 08:11:56 +00:00
|
|
|
Func: RuntimeDeserialize, Price: 1 << 14},
|
2020-08-14 10:50:52 +00:00
|
|
|
{ID: interopnames.ToID([]byte(interopnames.SystemBinarySerialize)),
|
2020-12-29 08:11:56 +00:00
|
|
|
Func: RuntimeSerialize, Price: 1 << 12},
|
2020-08-14 10:50:52 +00:00
|
|
|
{ID: interopnames.ToID([]byte(interopnames.SystemRuntimeLog)),
|
2020-12-29 10:45:49 +00:00
|
|
|
Func: runtimeLog, Price: 1 << 15, RequiredFlags: callflag.AllowNotify},
|
2020-08-14 10:50:52 +00:00
|
|
|
{ID: interopnames.ToID([]byte(interopnames.SystemRuntimeNotify)),
|
2020-12-29 10:45:49 +00:00
|
|
|
Func: runtimeNotify, Price: 1 << 15, RequiredFlags: callflag.AllowNotify},
|
2020-08-14 10:50:52 +00:00
|
|
|
{ID: interopnames.ToID([]byte(interopnames.SystemIteratorCreate)),
|
2020-12-29 08:11:56 +00:00
|
|
|
Func: IteratorCreate, Price: 1 << 4},
|
2021-01-12 09:30:21 +00:00
|
|
|
{ID: interopnames.ToID([]byte(interopnames.SystemIteratorNext)),
|
|
|
|
Func: IteratorNext, Price: 1 << 15},
|
|
|
|
{ID: interopnames.ToID([]byte(interopnames.SystemIteratorValue)),
|
|
|
|
Func: IteratorValue, Price: 1 << 4},
|
2019-12-18 16:49:56 +00:00
|
|
|
}
|
|
|
|
|
2020-07-28 13:38:00 +00:00
|
|
|
func init() {
|
|
|
|
sort.Slice(defaultVMInterops, func(i, j int) bool { return defaultVMInterops[i].ID < defaultVMInterops[j].ID })
|
2019-12-18 16:49:56 +00:00
|
|
|
}
|
|
|
|
|
2020-07-28 13:38:00 +00:00
|
|
|
func defaultSyscallHandler(v *VM, id uint32) error {
|
2019-12-18 16:49:56 +00:00
|
|
|
n := sort.Search(len(defaultVMInterops), func(i int) bool {
|
|
|
|
return defaultVMInterops[i].ID >= id
|
|
|
|
})
|
2020-07-28 13:38:00 +00:00
|
|
|
if n >= len(defaultVMInterops) || defaultVMInterops[n].ID != id {
|
|
|
|
return errors.New("syscall not found")
|
2019-12-18 16:49:56 +00:00
|
|
|
}
|
2020-07-28 13:38:00 +00:00
|
|
|
d := defaultVMInterops[n]
|
|
|
|
if !v.Context().callFlag.Has(d.RequiredFlags) {
|
|
|
|
return fmt.Errorf("missing call flags: %05b vs %05b", v.Context().callFlag, d.RequiredFlags)
|
|
|
|
}
|
|
|
|
return d.Func(v)
|
2019-12-18 16:49:56 +00:00
|
|
|
}
|
|
|
|
|
2020-06-10 08:49:39 +00:00
|
|
|
// runtimeLog handles the syscall "System.Runtime.Log" for printing and logging stuff.
|
2018-04-10 09:45:31 +00:00
|
|
|
func runtimeLog(vm *VM) error {
|
2020-07-29 08:18:51 +00:00
|
|
|
msg := vm.Estack().Pop().String()
|
|
|
|
fmt.Printf("NEO-GO-VM (log) > %s\n", msg)
|
2018-04-10 09:45:31 +00:00
|
|
|
return nil
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
|
|
|
|
2020-06-10 08:49:39 +00:00
|
|
|
// runtimeNotify handles the syscall "System.Runtime.Notify" for printing and logging stuff.
|
2018-04-10 09:45:31 +00:00
|
|
|
func runtimeNotify(vm *VM) error {
|
2020-07-29 08:18:51 +00:00
|
|
|
name := vm.Estack().Pop().String()
|
2018-04-10 09:45:31 +00:00
|
|
|
item := vm.Estack().Pop()
|
2020-07-29 08:18:51 +00:00
|
|
|
fmt.Printf("NEO-GO-VM (notify) > [%s] %s\n", name, item.Value())
|
2018-04-10 09:45:31 +00:00
|
|
|
return nil
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
2019-11-05 08:36:13 +00:00
|
|
|
|
2020-06-16 07:56:06 +00:00
|
|
|
// RuntimeSerialize handles System.Binary.Serialize syscall.
|
2019-11-05 14:10:52 +00:00
|
|
|
func RuntimeSerialize(vm *VM) error {
|
2019-11-05 08:36:13 +00:00
|
|
|
item := vm.Estack().Pop()
|
2020-06-03 12:55:06 +00:00
|
|
|
data, err := stackitem.SerializeItem(item.value)
|
2019-11-05 08:36:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2020-06-11 13:31:31 +00:00
|
|
|
} else if len(data) > stackitem.MaxSize {
|
2019-11-05 08:36:13 +00:00
|
|
|
return errors.New("too big item")
|
|
|
|
}
|
|
|
|
|
|
|
|
vm.Estack().PushVal(data)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-16 07:56:06 +00:00
|
|
|
// RuntimeDeserialize handles System.Binary.Deserialize syscall.
|
2019-11-05 14:10:52 +00:00
|
|
|
func RuntimeDeserialize(vm *VM) error {
|
2019-11-05 08:36:13 +00:00
|
|
|
data := vm.Estack().Pop().Bytes()
|
|
|
|
|
2020-06-03 12:55:06 +00:00
|
|
|
item, err := stackitem.DeserializeItem(data)
|
2019-11-05 08:36:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
vm.Estack().Push(&Element{value: item})
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2019-12-18 16:49:56 +00:00
|
|
|
|
|
|
|
// init sorts the global defaultVMInterops value.
|
|
|
|
func init() {
|
|
|
|
sort.Slice(defaultVMInterops, func(i, j int) bool {
|
|
|
|
return defaultVMInterops[i].ID < defaultVMInterops[j].ID
|
|
|
|
})
|
|
|
|
}
|
2019-11-13 11:34:03 +00:00
|
|
|
|
2021-01-12 09:30:21 +00:00
|
|
|
// IteratorNext handles syscall System.Enumerator.Next.
|
|
|
|
func IteratorNext(v *VM) error {
|
2019-11-13 11:34:03 +00:00
|
|
|
iop := v.Estack().Pop().Interop()
|
2021-01-12 09:30:21 +00:00
|
|
|
arr := iop.Value().(iterator)
|
2019-11-13 11:34:03 +00:00
|
|
|
v.Estack().PushVal(arr.Next())
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-01-12 09:30:21 +00:00
|
|
|
// IteratorValue handles syscall System.Enumerator.Value.
|
|
|
|
func IteratorValue(v *VM) error {
|
2019-11-13 11:34:03 +00:00
|
|
|
iop := v.Estack().Pop().Interop()
|
2021-01-12 09:30:21 +00:00
|
|
|
arr := iop.Value().(iterator)
|
2019-11-13 11:34:03 +00:00
|
|
|
v.Estack().Push(&Element{value: arr.Value()})
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-10 08:49:39 +00:00
|
|
|
// IteratorCreate handles syscall System.Iterator.Create.
|
2019-11-13 12:29:27 +00:00
|
|
|
func IteratorCreate(v *VM) error {
|
|
|
|
data := v.Estack().Pop()
|
2020-06-03 12:55:06 +00:00
|
|
|
var item stackitem.Item
|
2019-11-13 12:29:27 +00:00
|
|
|
switch t := data.value.(type) {
|
2020-06-03 12:55:06 +00:00
|
|
|
case *stackitem.Array, *stackitem.Struct:
|
|
|
|
item = stackitem.NewInterop(&arrayWrapper{
|
2019-11-13 12:29:27 +00:00
|
|
|
index: -1,
|
2020-06-03 12:55:06 +00:00
|
|
|
value: t.Value().([]stackitem.Item),
|
2019-12-26 11:34:15 +00:00
|
|
|
})
|
2020-06-03 12:55:06 +00:00
|
|
|
case *stackitem.Map:
|
2020-03-31 10:30:38 +00:00
|
|
|
item = NewMapIterator(t)
|
2019-11-13 12:29:27 +00:00
|
|
|
default:
|
2020-07-21 10:14:16 +00:00
|
|
|
data, err := t.TryBytes()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("non-iterable type %s", t.Type())
|
|
|
|
}
|
|
|
|
item = stackitem.NewInterop(&byteArrayWrapper{
|
|
|
|
index: -1,
|
|
|
|
value: data,
|
|
|
|
})
|
2019-11-13 12:29:27 +00:00
|
|
|
}
|
|
|
|
|
2019-12-26 11:34:15 +00:00
|
|
|
v.Estack().Push(&Element{value: item})
|
2019-11-13 12:29:27 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-12-26 11:34:15 +00:00
|
|
|
// NewMapIterator returns new interop item containing iterator over m.
|
2020-06-03 12:55:06 +00:00
|
|
|
func NewMapIterator(m *stackitem.Map) *stackitem.Interop {
|
|
|
|
return stackitem.NewInterop(&mapWrapper{
|
2019-12-26 11:34:15 +00:00
|
|
|
index: -1,
|
2020-06-03 12:55:06 +00:00
|
|
|
m: m.Value().([]stackitem.MapElement),
|
2019-12-26 11:34:15 +00:00
|
|
|
})
|
|
|
|
}
|