mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-27 03:58:06 +00:00
core: move System.Runtime.*
interops to runtime/
package
Also extend test suite.
This commit is contained in:
parent
972b0f176d
commit
2eb256014e
7 changed files with 392 additions and 166 deletions
93
pkg/core/interop/runtime/engine.go
Normal file
93
pkg/core/interop/runtime/engine.go
Normal file
|
@ -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
|
||||
}
|
175
pkg/core/interop/runtime/engine_test.go
Normal file
175
pkg/core/interop/runtime/engine_test.go
Normal file
|
@ -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)
|
||||
})
|
||||
}
|
116
pkg/core/interop/runtime/util_test.go
Normal file
116
pkg/core/interop/runtime/util_test.go
Normal file
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue