diff --git a/pkg/core/interop/runtime/engine.go b/pkg/core/interop/runtime/engine.go new file mode 100644 index 000000000..7dac81171 --- /dev/null +++ b/pkg/core/interop/runtime/engine.go @@ -0,0 +1,93 @@ +package runtime + +import ( + "fmt" + + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "go.uber.org/zap" +) + +const ( + // MaxEventNameLen is the maximum length of a name for event. + MaxEventNameLen = 32 + // MaxNotificationSize is the maximum length of a runtime log message. + MaxNotificationSize = 1024 +) + +// GetExecutingScriptHash returns executing script hash. +func GetExecutingScriptHash(ic *interop.Context) error { + return ic.VM.PushContextScriptHash(0) +} + +// GetCallingScriptHash returns calling script hash. +func GetCallingScriptHash(ic *interop.Context) error { + return ic.VM.PushContextScriptHash(1) +} + +// GetEntryScriptHash returns entry script hash. +func GetEntryScriptHash(ic *interop.Context) error { + return ic.VM.PushContextScriptHash(ic.VM.Istack().Len() - 1) +} + +// Platform returns the name of the platform. +func Platform(ic *interop.Context) error { + ic.VM.Estack().PushVal([]byte("NEO")) + return nil +} + +// GetTrigger returns the script trigger. +func GetTrigger(ic *interop.Context) error { + ic.VM.Estack().PushVal(byte(ic.Trigger)) + return nil +} + +// Notify should pass stack item to the notify plugin to handle it, but +// in neo-go the only meaningful thing to do here is to log. +func Notify(ic *interop.Context) error { + name := ic.VM.Estack().Pop().String() + if len(name) > MaxEventNameLen { + return fmt.Errorf("event name must be less than %d", MaxEventNameLen) + } + elem := ic.VM.Estack().Pop() + args := elem.Array() + // But it has to be serializable, otherwise we either have some broken + // (recursive) structure inside or an interop item that can't be used + // outside of the interop subsystem anyway. + bytes, err := stackitem.SerializeItem(elem.Item()) + if err != nil { + return fmt.Errorf("bad notification: %w", err) + } + if len(bytes) > MaxNotificationSize { + return fmt.Errorf("notification size shouldn't exceed %d", MaxNotificationSize) + } + ne := state.NotificationEvent{ + ScriptHash: ic.VM.GetCurrentScriptHash(), + Name: name, + Item: stackitem.DeepCopy(stackitem.NewArray(args)).(*stackitem.Array), + } + ic.Notifications = append(ic.Notifications, ne) + return nil +} + +// Log logs the message passed. +func Log(ic *interop.Context) error { + state := ic.VM.Estack().Pop().String() + if len(state) > MaxNotificationSize { + return fmt.Errorf("message length shouldn't exceed %v", MaxNotificationSize) + } + msg := fmt.Sprintf("%q", state) + ic.Log.Info("runtime log", + zap.Stringer("script", ic.VM.GetCurrentScriptHash()), + zap.String("logs", msg)) + return nil +} + +// GetTime returns timestamp of the block being verified, or the latest +// one in the blockchain if no block is given to Context. +func GetTime(ic *interop.Context) error { + header := ic.Block.Header() + ic.VM.Estack().PushVal(header.Timestamp) + return nil +} diff --git a/pkg/core/interop/runtime/engine_test.go b/pkg/core/interop/runtime/engine_test.go new file mode 100644 index 000000000..18be7d1ef --- /dev/null +++ b/pkg/core/interop/runtime/engine_test.go @@ -0,0 +1,175 @@ +package runtime + +import ( + "encoding/json" + "math/big" + "math/rand" + "testing" + + "github.com/nspcc-dev/neo-go/internal/random" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/crypto/hash" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" +) + +func checkStack(t *testing.T, v *vm.VM, args ...interface{}) { + require.Equal(t, len(args), v.Estack().Len()) + for i := range args { + require.Equal(t, stackitem.Make(args[i]), v.Estack().Pop().Item(), "%d", i) + } +} + +func TestGetTrigger(t *testing.T) { + triggers := []trigger.Type{trigger.Application, trigger.Verification} + for _, tr := range triggers { + ic := &interop.Context{Trigger: tr, VM: vm.New()} + require.NoError(t, GetTrigger(ic)) + checkStack(t, ic.VM, int64(tr)) + } +} + +func TestPlatform(t *testing.T) { + ic := &interop.Context{VM: vm.New()} + require.NoError(t, Platform(ic)) + checkStack(t, ic.VM, "NEO") +} + +func TestGetTime(t *testing.T) { + b := block.New(netmode.UnitTestNet, false) + b.Timestamp = rand.Uint64() + ic := &interop.Context{VM: vm.New(), Block: b} + require.NoError(t, GetTime(ic)) + checkStack(t, ic.VM, new(big.Int).SetUint64(b.Timestamp)) +} + +func TestGetScriptHash(t *testing.T) { + scripts := []struct { + s []byte + h util.Uint160 + }{ + {[]byte{1, 2, 3, 4}, hash.Hash160([]byte{1, 2, 3, 4})}, + {[]byte{1, 2, 3}, util.Uint160{4, 8, 15, 16}}, + {[]byte{1, 2}, hash.Hash160([]byte{1, 2})}, + {[]byte{1}, hash.Hash160([]byte{1})}, + } + + ic := &interop.Context{VM: vm.New()} + ic.VM.LoadScriptWithFlags(scripts[0].s, smartcontract.All) + require.NoError(t, GetEntryScriptHash(ic)) + checkStack(t, ic.VM, scripts[0].h.BytesBE()) + require.NoError(t, GetCallingScriptHash(ic)) + checkStack(t, ic.VM, util.Uint160{}.BytesBE()) + require.NoError(t, GetExecutingScriptHash(ic)) + checkStack(t, ic.VM, scripts[0].h.BytesBE()) + + ic.VM.LoadScriptWithHash(scripts[1].s, scripts[1].h, smartcontract.All) + require.NoError(t, GetEntryScriptHash(ic)) + checkStack(t, ic.VM, scripts[0].h.BytesBE()) + require.NoError(t, GetCallingScriptHash(ic)) + checkStack(t, ic.VM, scripts[0].h.BytesBE()) + require.NoError(t, GetExecutingScriptHash(ic)) + checkStack(t, ic.VM, scripts[1].h.BytesBE()) + + ic.VM.LoadScript(scripts[2].s) + require.NoError(t, GetEntryScriptHash(ic)) + checkStack(t, ic.VM, scripts[0].h.BytesBE()) + require.NoError(t, GetCallingScriptHash(ic)) + checkStack(t, ic.VM, scripts[1].h.BytesBE()) + require.NoError(t, GetExecutingScriptHash(ic)) + checkStack(t, ic.VM, scripts[2].h.BytesBE()) + + ic.VM.LoadScript(scripts[3].s) + require.NoError(t, GetEntryScriptHash(ic)) + checkStack(t, ic.VM, scripts[0].h.BytesBE()) + require.NoError(t, GetCallingScriptHash(ic)) + checkStack(t, ic.VM, scripts[2].h.BytesBE()) + require.NoError(t, GetExecutingScriptHash(ic)) + checkStack(t, ic.VM, scripts[3].h.BytesBE()) +} + +func TestLog(t *testing.T) { + newL := func(l zapcore.Level) (*zap.Logger, *zaptest.Buffer) { + enc := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) + w := &zaptest.Buffer{} + zc := zapcore.NewCore(enc, w, l) + return zap.New(zc, zap.ErrorOutput(w)), w + } + h := random.Uint160() + + t.Run("big message", func(t *testing.T) { + ic := &interop.Context{Log: zap.NewNop(), VM: vm.New()} + ic.VM.LoadScriptWithHash([]byte{1}, h, smartcontract.All) + ic.VM.Estack().PushVal(string(make([]byte, MaxNotificationSize+1))) + require.Error(t, Log(ic)) + }) + + t.Run("good", func(t *testing.T) { + log, buf := newL(zapcore.InfoLevel) + ic := &interop.Context{Log: log, VM: vm.New()} + ic.VM.LoadScriptWithHash([]byte{1}, h, smartcontract.All) + ic.VM.Estack().PushVal("hello") + require.NoError(t, Log(ic)) + + ls := buf.Lines() + require.Equal(t, 1, len(ls)) + + var logMsg map[string]interface{} + require.NoError(t, json.Unmarshal([]byte(ls[0]), &logMsg)) + require.Equal(t, "info", logMsg["level"]) + require.Equal(t, "runtime log", logMsg["msg"]) + require.Equal(t, h.StringBE(), logMsg["script"]) + require.Equal(t, `"hello"`, logMsg["logs"]) + + }) +} + +func TestNotify(t *testing.T) { + h := random.Uint160() + newIC := func(name string, args interface{}) *interop.Context { + ic := &interop.Context{VM: vm.New()} + ic.VM.LoadScriptWithHash([]byte{1}, h, smartcontract.NoneFlag) + ic.VM.Estack().PushVal(args) + ic.VM.Estack().PushVal(name) + return ic + } + t.Run("big name", func(t *testing.T) { + ic := newIC(string(make([]byte, MaxEventNameLen+1)), []byte{42}) + require.Error(t, Notify(ic)) + }) + t.Run("recursive struct", func(t *testing.T) { + arr := stackitem.NewArray([]stackitem.Item{stackitem.Null{}}) + arr.Append(arr) + ic := newIC("event", arr) + require.Error(t, Notify(ic)) + }) + t.Run("big notification", func(t *testing.T) { + bs := stackitem.NewByteArray(make([]byte, MaxNotificationSize+1)) + arr := stackitem.NewArray([]stackitem.Item{bs}) + ic := newIC("event", arr) + require.Error(t, Notify(ic)) + }) + t.Run("good", func(t *testing.T) { + arr := stackitem.NewArray([]stackitem.Item{stackitem.Make(42)}) + ic := newIC("good event", arr) + require.NoError(t, Notify(ic)) + require.Equal(t, 1, len(ic.Notifications)) + + ev := ic.Notifications[0] + require.Equal(t, "good event", ev.Name) + require.Equal(t, h, ev.ScriptHash) + require.Equal(t, arr, ev.Item) + // Check deep copy. + arr.Value().([]stackitem.Item)[0] = stackitem.Null{} + require.NotEqual(t, arr, ev.Item) + }) +} diff --git a/pkg/core/interop/runtime/util_test.go b/pkg/core/interop/runtime/util_test.go new file mode 100644 index 000000000..e48238d56 --- /dev/null +++ b/pkg/core/interop/runtime/util_test.go @@ -0,0 +1,116 @@ +package runtime + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/internal/random" + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +func TestGasLeft(t *testing.T) { + t.Run("no limit", func(t *testing.T) { + ic := &interop.Context{VM: vm.New()} + ic.VM.GasLimit = -1 + ic.VM.AddGas(58) + require.NoError(t, GasLeft(ic)) + checkStack(t, ic.VM, -1) + }) + t.Run("with limit", func(t *testing.T) { + ic := &interop.Context{VM: vm.New()} + ic.VM.GasLimit = 100 + ic.VM.AddGas(58) + require.NoError(t, GasLeft(ic)) + checkStack(t, ic.VM, 42) + }) +} + +func TestRuntimeGetNotifications(t *testing.T) { + v := vm.New() + ic := &interop.Context{ + VM: v, + Notifications: []state.NotificationEvent{ + {ScriptHash: util.Uint160{1}, Name: "Event1", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{11})})}, + {ScriptHash: util.Uint160{2}, Name: "Event2", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{22})})}, + {ScriptHash: util.Uint160{1}, Name: "Event1", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{33})})}, + }, + } + + t.Run("NoFilter", func(t *testing.T) { + v.Estack().PushVal(stackitem.Null{}) + require.NoError(t, GetNotifications(ic)) + + arr := v.Estack().Pop().Array() + require.Equal(t, len(ic.Notifications), len(arr)) + for i := range arr { + elem := arr[i].Value().([]stackitem.Item) + require.Equal(t, ic.Notifications[i].ScriptHash.BytesBE(), elem[0].Value()) + name, err := stackitem.ToString(elem[1]) + require.NoError(t, err) + require.Equal(t, ic.Notifications[i].Name, name) + require.Equal(t, ic.Notifications[i].Item, elem[2]) + } + }) + + t.Run("WithFilter", func(t *testing.T) { + h := util.Uint160{2}.BytesBE() + v.Estack().PushVal(h) + require.NoError(t, GetNotifications(ic)) + + arr := v.Estack().Pop().Array() + require.Equal(t, 1, len(arr)) + elem := arr[0].Value().([]stackitem.Item) + require.Equal(t, h, elem[0].Value()) + name, err := stackitem.ToString(elem[1]) + require.NoError(t, err) + require.Equal(t, ic.Notifications[1].Name, name) + require.Equal(t, ic.Notifications[1].Item, elem[2]) + }) + + t.Run("Bad", func(t *testing.T) { + t.Run("not bytes", func(t *testing.T) { + v.Estack().PushVal(stackitem.NewInterop(util.Uint160{1})) + require.Error(t, GetNotifications(ic)) + }) + t.Run("not uint160", func(t *testing.T) { + v.Estack().PushVal([]byte{1, 2, 3}) + require.Error(t, GetNotifications(ic)) + }) + t.Run("too many notifications", func(t *testing.T) { + for i := 0; i <= vm.MaxStackSize; i++ { + ic.Notifications = append(ic.Notifications, state.NotificationEvent{ + ScriptHash: util.Uint160{3}, + Name: "Event3", + Item: stackitem.NewArray(nil), + }) + } + v.Estack().PushVal(stackitem.Null{}) + require.Error(t, GetNotifications(ic)) + }) + }) +} + +func TestRuntimeGetInvocationCounter(t *testing.T) { + ic := &interop.Context{VM: vm.New()} + h := random.Uint160() + ic.VM.Invocations[h] = 42 + + t.Run("No invocations", func(t *testing.T) { + h1 := h + h1[0] ^= 0xFF + ic.VM.LoadScriptWithHash([]byte{1}, h1, smartcontract.NoneFlag) + // do not return an error in this case. + require.NoError(t, GetInvocationCounter(ic)) + checkStack(t, ic.VM, 1) + }) + t.Run("NonZero", func(t *testing.T) { + ic.VM.LoadScriptWithHash([]byte{1}, h, smartcontract.NoneFlag) + require.NoError(t, GetInvocationCounter(ic)) + checkStack(t, ic.VM, 42) + }) +} diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 1eb23d3c9..816c89728 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -37,12 +37,6 @@ import ( * TestRuntimeDeserialize */ -func TestGetTrigger(t *testing.T) { - _, _, context, chain := createVMAndPushBlock(t) - defer chain.Close() - require.NoError(t, runtimeGetTrigger(context)) -} - func TestStorageFind(t *testing.T) { v, contractState, context, chain := createVMAndContractState(t) defer chain.Close() diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index da9a36e2f..50e2f377b 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -18,7 +18,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - "go.uber.org/zap" ) const ( @@ -27,10 +26,6 @@ const ( // MaxStorageValueLen is the maximum length of a value for storage items. // It is set to be the maximum value for uint16. MaxStorageValueLen = 65535 - // MaxEventNameLen is the maximum length of a name for event. - MaxEventNameLen = 32 - // MaxNotificationSize is the maximum length of a runtime log message. - MaxNotificationSize = 1024 ) // StorageContext contains storing id and read/write flag, it's used as @@ -231,82 +226,6 @@ func engineGetScriptContainer(ic *interop.Context) error { return nil } -// engineGetExecutingScriptHash returns executing script hash. -func engineGetExecutingScriptHash(ic *interop.Context) error { - return ic.VM.PushContextScriptHash(0) -} - -// engineGetCallingScriptHash returns calling script hash. -func engineGetCallingScriptHash(ic *interop.Context) error { - return ic.VM.PushContextScriptHash(1) -} - -// engineGetEntryScriptHash returns entry script hash. -func engineGetEntryScriptHash(ic *interop.Context) error { - return ic.VM.PushContextScriptHash(ic.VM.Istack().Len() - 1) -} - -// runtimePlatform returns the name of the platform. -func runtimePlatform(ic *interop.Context) error { - ic.VM.Estack().PushVal([]byte("NEO")) - return nil -} - -// runtimeGetTrigger returns the script trigger. -func runtimeGetTrigger(ic *interop.Context) error { - ic.VM.Estack().PushVal(byte(ic.Trigger)) - return nil -} - -// runtimeNotify should pass stack item to the notify plugin to handle it, but -// in neo-go the only meaningful thing to do here is to log. -func runtimeNotify(ic *interop.Context) error { - name := ic.VM.Estack().Pop().String() - if len(name) > MaxEventNameLen { - return fmt.Errorf("event name must be less than %d", MaxEventNameLen) - } - elem := ic.VM.Estack().Pop() - args := elem.Array() - // But it has to be serializable, otherwise we either have some broken - // (recursive) structure inside or an interop item that can't be used - // outside of the interop subsystem anyway. - bytes, err := stackitem.SerializeItem(elem.Item()) - if err != nil { - return fmt.Errorf("bad notification: %w", err) - } - if len(bytes) > MaxNotificationSize { - return fmt.Errorf("notification size shouldn't exceed %d", MaxNotificationSize) - } - ne := state.NotificationEvent{ - ScriptHash: ic.VM.GetCurrentScriptHash(), - Name: name, - Item: stackitem.DeepCopy(stackitem.NewArray(args)).(*stackitem.Array), - } - ic.Notifications = append(ic.Notifications, ne) - return nil -} - -// runtimeLog logs the message passed. -func runtimeLog(ic *interop.Context) error { - state := ic.VM.Estack().Pop().String() - if len(state) > MaxNotificationSize { - return fmt.Errorf("message length shouldn't exceed %v", MaxNotificationSize) - } - msg := fmt.Sprintf("%q", state) - ic.Log.Info("runtime log", - zap.Stringer("script", ic.VM.GetCurrentScriptHash()), - zap.String("logs", msg)) - return nil -} - -// runtimeGetTime returns timestamp of the block being verified, or the latest -// one in the blockchain if no block is given to Context. -func runtimeGetTime(ic *interop.Context) error { - header := ic.Block.Header() - ic.VM.Estack().PushVal(header.Timestamp) - return nil -} - // storageDelete deletes stored key-value pair. func storageDelete(ic *interop.Context) error { stcInterface := ic.VM.Estack().Pop().Value() diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index b0f0c1e62..42c62e6bf 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -244,77 +244,6 @@ func TestContractCreateAccount(t *testing.T) { }) } -func TestRuntimeGasLeft(t *testing.T) { - v, ic, chain := createVM(t) - defer chain.Close() - - v.GasLimit = 100 - v.AddGas(58) - require.NoError(t, runtime.GasLeft(ic)) - require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64()) -} - -func TestRuntimeGetNotifications(t *testing.T) { - v, ic, chain := createVM(t) - defer chain.Close() - - ic.Notifications = []state.NotificationEvent{ - {ScriptHash: util.Uint160{1}, Name: "Event1", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{11})})}, - {ScriptHash: util.Uint160{2}, Name: "Event2", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{22})})}, - {ScriptHash: util.Uint160{1}, Name: "Event1", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{33})})}, - } - - t.Run("NoFilter", func(t *testing.T) { - v.Estack().PushVal(stackitem.Null{}) - require.NoError(t, runtime.GetNotifications(ic)) - - arr := v.Estack().Pop().Array() - require.Equal(t, len(ic.Notifications), len(arr)) - for i := range arr { - elem := arr[i].Value().([]stackitem.Item) - require.Equal(t, ic.Notifications[i].ScriptHash.BytesBE(), elem[0].Value()) - name, err := stackitem.ToString(elem[1]) - require.NoError(t, err) - require.Equal(t, ic.Notifications[i].Name, name) - require.Equal(t, ic.Notifications[i].Item, elem[2]) - } - }) - - t.Run("WithFilter", func(t *testing.T) { - h := util.Uint160{2}.BytesBE() - v.Estack().PushVal(h) - require.NoError(t, runtime.GetNotifications(ic)) - - arr := v.Estack().Pop().Array() - require.Equal(t, 1, len(arr)) - elem := arr[0].Value().([]stackitem.Item) - require.Equal(t, h, elem[0].Value()) - name, err := stackitem.ToString(elem[1]) - require.NoError(t, err) - require.Equal(t, ic.Notifications[1].Name, name) - require.Equal(t, ic.Notifications[1].Item, elem[2]) - }) -} - -func TestRuntimeGetInvocationCounter(t *testing.T) { - v, ic, chain := createVM(t) - defer chain.Close() - - ic.VM.Invocations[hash.Hash160([]byte{2})] = 42 - - t.Run("No invocations", func(t *testing.T) { - v.LoadScript([]byte{1}) - // do not return an error in this case. - require.NoError(t, runtime.GetInvocationCounter(ic)) - require.EqualValues(t, 1, v.Estack().Pop().BigInt().Int64()) - }) - t.Run("NonZero", func(t *testing.T) { - v.LoadScript([]byte{2}) - require.NoError(t, runtime.GetInvocationCounter(ic)) - require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64()) - }) -} - func TestBlockchainGetContractState(t *testing.T) { v, cs, ic, bc := createVMAndContractState(t) defer bc.Close() diff --git a/pkg/core/interops.go b/pkg/core/interops.go index b60dbaa54..a8dbd95d9 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -83,19 +83,19 @@ var systemInterops = []interop.Function{ {Name: interopnames.SystemRuntimeCheckWitness, Func: runtime.CheckWitness, Price: 30000, RequiredFlags: smartcontract.NoneFlag, ParamCount: 1}, {Name: interopnames.SystemRuntimeGasLeft, Func: runtime.GasLeft, Price: 400}, - {Name: interopnames.SystemRuntimeGetCallingScriptHash, Func: engineGetCallingScriptHash, Price: 400}, - {Name: interopnames.SystemRuntimeGetEntryScriptHash, Func: engineGetEntryScriptHash, Price: 400}, - {Name: interopnames.SystemRuntimeGetExecutingScriptHash, Func: engineGetExecutingScriptHash, Price: 400}, + {Name: interopnames.SystemRuntimeGetCallingScriptHash, Func: runtime.GetCallingScriptHash, Price: 400}, + {Name: interopnames.SystemRuntimeGetEntryScriptHash, Func: runtime.GetEntryScriptHash, Price: 400}, + {Name: interopnames.SystemRuntimeGetExecutingScriptHash, Func: runtime.GetExecutingScriptHash, Price: 400}, {Name: interopnames.SystemRuntimeGetInvocationCounter, Func: runtime.GetInvocationCounter, Price: 400}, {Name: interopnames.SystemRuntimeGetNotifications, Func: runtime.GetNotifications, Price: 10000, ParamCount: 1}, {Name: interopnames.SystemRuntimeGetScriptContainer, Func: engineGetScriptContainer, Price: 250}, - {Name: interopnames.SystemRuntimeGetTime, Func: runtimeGetTime, Price: 250, RequiredFlags: smartcontract.AllowStates}, - {Name: interopnames.SystemRuntimeGetTrigger, Func: runtimeGetTrigger, Price: 250}, - {Name: interopnames.SystemRuntimeLog, Func: runtimeLog, Price: 1000000, RequiredFlags: smartcontract.AllowNotify, + {Name: interopnames.SystemRuntimeGetTime, Func: runtime.GetTime, Price: 250, RequiredFlags: smartcontract.AllowStates}, + {Name: interopnames.SystemRuntimeGetTrigger, Func: runtime.GetTrigger, Price: 250}, + {Name: interopnames.SystemRuntimeLog, Func: runtime.Log, Price: 1000000, RequiredFlags: smartcontract.AllowNotify, ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemRuntimeNotify, Func: runtimeNotify, Price: 1000000, RequiredFlags: smartcontract.AllowNotify, + {Name: interopnames.SystemRuntimeNotify, Func: runtime.Notify, Price: 1000000, RequiredFlags: smartcontract.AllowNotify, ParamCount: 2, DisallowCallback: true}, - {Name: interopnames.SystemRuntimePlatform, Func: runtimePlatform, Price: 250}, + {Name: interopnames.SystemRuntimePlatform, Func: runtime.Platform, Price: 250}, {Name: interopnames.SystemStorageDelete, Func: storageDelete, Price: StoragePrice, RequiredFlags: smartcontract.AllowModifyStates, ParamCount: 2, DisallowCallback: true}, {Name: interopnames.SystemStorageFind, Func: storageFind, Price: 1000000, RequiredFlags: smartcontract.AllowStates,