core: implement System.Runtime.GetNotifications syscall

This commit is contained in:
Evgenii Stratonikov 2020-06-16 12:30:25 +03:00
parent afd8f3b87a
commit 75e597f880
4 changed files with 84 additions and 0 deletions

View file

@ -28,6 +28,7 @@ var syscalls = map[string]map[string]string{
"GetCallingScriptHash": "System.Runtime.GetCallingScriptHash",
"GetEntryScriptHash": "System.Runtime.GetEntryScriptHash",
"GetExecutingScriptHash": "System.Runtime.GetExecutingScriptHash",
"GetNotifications": "System.Runtime.GetNotifications",
"GasLeft": "System.Runtime.GasLeft",
"GetTrigger": "System.Runtime.GetTrigger",

View file

@ -2,7 +2,11 @@ package runtime
import (
"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/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/pkg/errors"
)
// GasLeft returns remaining amount of GAS.
@ -10,3 +14,38 @@ func GasLeft(_ *interop.Context, v *vm.VM) error {
v.Estack().PushVal(int64(v.GasLimit - v.GasConsumed()))
return nil
}
// GetNotifications returns notifications emitted by current contract execution.
func GetNotifications(ic *interop.Context, v *vm.VM) error {
item := v.Estack().Pop().Item()
notifications := ic.Notifications
if _, ok := item.(stackitem.Null); !ok {
b, err := item.TryBytes()
if err != nil {
return err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return err
}
notifications = []state.NotificationEvent{}
for i := range ic.Notifications {
if ic.Notifications[i].ScriptHash.Equals(u) {
notifications = append(notifications, ic.Notifications[i])
}
}
}
if len(notifications) > vm.MaxStackSize {
return errors.New("too many notifications")
}
arr := stackitem.NewArray(make([]stackitem.Item, 0, len(notifications)))
for i := range notifications {
ev := stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(notifications[i].ScriptHash.BytesBE()),
notifications[i].Item,
})
arr.Append(ev)
}
v.Estack().PushVal(arr)
return nil
}

View file

@ -208,3 +208,39 @@ func TestRuntimeGasLeft(t *testing.T) {
require.NoError(t, runtime.GasLeft(ic, v))
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}, Item: stackitem.NewByteArray([]byte{11})},
{ScriptHash: util.Uint160{2}, Item: stackitem.NewByteArray([]byte{22})},
{ScriptHash: util.Uint160{1}, Item: stackitem.NewByteArray([]byte{33})},
}
t.Run("NoFilter", func(t *testing.T) {
v.Estack().PushVal(stackitem.Null{})
require.NoError(t, runtime.GetNotifications(ic, v))
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())
require.Equal(t, ic.Notifications[i].Item, elem[1])
}
})
t.Run("WithFilter", func(t *testing.T) {
h := util.Uint160{2}.BytesBE()
v.Estack().PushVal(h)
require.NoError(t, runtime.GetNotifications(ic, v))
arr := v.Estack().Pop().Array()
require.Equal(t, 1, len(arr))
elem := arr[0].Value().([]stackitem.Item)
require.Equal(t, h, elem[0].Value())
require.Equal(t, ic.Notifications[1].Item, elem[1])
})
}

View file

@ -60,3 +60,11 @@ func Verification() byte {
func GasLeft() int64 {
return 0
}
// GetNotifications returns notifications emitted by contract h.
// 'nil' literal means no filtering. It returns slice consisting of following elements:
// [ scripthash of notification's contract , emitted item ].
// This function uses `System.Runtime.GetNotifications` syscall.
func GetNotifications(h []byte) [][]interface{} {
return nil
}