Merge pull request #1218 from nspcc-dev/neo3/interop/post-preview2_adjustment2

interop: post-preview2 adjustment, part 2
This commit is contained in:
Roman Khimov 2020-07-23 09:35:34 +03:00 committed by GitHub
commit 06f70e6a0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 91 additions and 30 deletions

View file

@ -68,6 +68,7 @@ var syscalls = map[string]map[string]Syscall{
"CheckWitness": {"System.Runtime.CheckWitness", false},
"Log": {"System.Runtime.Log", false},
"Notify": {"System.Runtime.Notify", false},
"Platform": {"System.Runtime.Platform", false},
},
"storage": {
"ConvertContextToReadOnly": {"System.Storage.AsReadOnly", false},

View file

@ -10,7 +10,7 @@ func Concat(_ *interop.Context, v *vm.VM) error {
return vm.EnumeratorConcat(v)
}
// Create creates an enumerator from an array-like stack item.
// Create creates an enumerator from an array-like or bytearray-like stack item.
func Create(_ *interop.Context, v *vm.VM) error {
return vm.EnumeratorCreate(v)
}

View file

@ -11,7 +11,11 @@ import (
// GasLeft returns remaining amount of GAS.
func GasLeft(_ *interop.Context, v *vm.VM) error {
v.Estack().PushVal(v.GasLimit - v.GasConsumed())
if v.GasLimit == -1 {
v.Estack().PushVal(v.GasLimit)
} else {
v.Estack().PushVal(v.GasLimit - v.GasConsumed())
}
return nil
}

View file

@ -19,17 +19,7 @@ func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) {
return checkScope(ic.DAO, tx, ic.ScriptGetter, hash)
}
// only for non-Transaction types (Block, etc.)
hashes, err := ic.Chain.GetScriptHashesForVerifying(ic.Tx)
if err != nil {
return false, errors.Wrap(err, "failed to get script hashes")
}
for _, v := range hashes {
if hash.Equals(v) {
return true, nil
}
}
return false, nil
return false, errors.New("script container is not a transaction")
}
func checkScope(d dao.DAO, tx *transaction.Transaction, v vm.ScriptHashGetter, hash util.Uint160) (bool, error) {

View file

@ -6,6 +6,7 @@ import (
"fmt"
"math"
"math/big"
"unicode/utf8"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
@ -29,6 +30,8 @@ const (
MaxTraceableBlocks = transaction.MaxValidUntilBlockIncrement
// 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
@ -250,6 +253,9 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
if len(name) > MaxEventNameLen {
return fmt.Errorf("event name must be less than %d", MaxEventNameLen)
}
if !utf8.Valid(name) {
return errors.New("event name should be UTF8-encoded")
}
elem := v.Estack().Pop()
args := elem.Array()
// But it has to be serializable, otherwise we either have some broken
@ -272,7 +278,14 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
// runtimeLog logs the message passed.
func runtimeLog(ic *interop.Context, v *vm.VM) error {
msg := fmt.Sprintf("%q", v.Estack().Pop().Bytes())
state := v.Estack().Pop().Bytes()
if len(state) > MaxNotificationSize {
return fmt.Errorf("message length shouldn't exceed %v", MaxNotificationSize)
}
if !utf8.Valid(state) {
return errors.New("log message should be UTF8-encoded")
}
msg := fmt.Sprintf("%q", state)
ic.Log.Info("runtime log",
zap.Stringer("script", v.GetCurrentScriptHash()),
zap.String("logs", msg))

View file

@ -110,15 +110,17 @@ var systemInterops = []interop.Function{
{Name: "System.Iterator.Values", Func: iterator.Values, Price: 400},
{Name: "System.Json.Deserialize", Func: json.Deserialize, Price: 500000},
{Name: "System.Json.Serialize", Func: json.Serialize, Price: 100000},
{Name: "System.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 30000},
{Name: "System.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 30000,
RequiredFlags: smartcontract.AllowStates},
{Name: "System.Runtime.GasLeft", Func: runtime.GasLeft, Price: 400},
{Name: "System.Runtime.GetCallingScriptHash", Func: engineGetCallingScriptHash, Price: 400},
{Name: "System.Runtime.GetEntryScriptHash", Func: engineGetEntryScriptHash, Price: 400},
{Name: "System.Runtime.GetExecutingScriptHash", Func: engineGetExecutingScriptHash, Price: 400},
{Name: "System.Runtime.GetInvocationCounter", Func: runtime.GetInvocationCounter, Price: 400},
{Name: "System.Runtime.GetNotifications", Func: runtime.GetNotifications, Price: 10000},
{Name: "System.Runtime.GetScriptContainer", Func: engineGetScriptContainer, Price: 250},
{Name: "System.Runtime.GetTime", Func: runtimeGetTime, Price: 250,
AllowedTriggers: trigger.Application},
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates},
{Name: "System.Runtime.GetTrigger", Func: runtimeGetTrigger, Price: 250},
{Name: "System.Runtime.Log", Func: runtimeLog, Price: 1000000, RequiredFlags: smartcontract.AllowNotify},
{Name: "System.Runtime.Notify", Func: runtimeNotify, Price: 1000000, RequiredFlags: smartcontract.AllowNotify},

View file

@ -9,11 +9,11 @@ package enumerator
// or structures that have values with no explicit keys.
type Enumerator struct{}
// Create creates a new enumerator from the given items (slice or structure).
// New enumerator points at index -1 of its items, so the user of it has to
// advance it first with Next. This function uses `System.Enumerator.Create`
// syscall.
func Create(items []interface{}) Enumerator {
// Create creates a new enumerator from the given items (slice, structure, byte
// array and integer or boolean converted to byte array). New enumerator points
// at index -1 of its items, so the user of it has to advance it first with Next.
// This function uses `System.Enumerator.Create` syscall.
func Create(items interface{}) Enumerator {
return Enumerator{}
}

View file

@ -11,10 +11,11 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/enumerator"
// structure is similar in function to Neo .net framework's Iterator.
type Iterator struct{}
// Create creates an iterator from the given items (array, struct or map). A new
// iterator is set to point at element -1, so to access its first element you
// need to call Next first. This function uses `System.Iterator.Create` syscall.
func Create(items []interface{}) Iterator {
// Create creates an iterator from the given items (array, struct, map, byte
// array or integer and boolean converted to byte array). A new iterator is set
// to point at element -1, so to access its first element you need to call Next
// first. This function uses `System.Iterator.Create` syscall.
func Create(items interface{}) Iterator {
return Iterator{}
}

View file

@ -80,3 +80,9 @@ func GetNotifications(h []byte) [][]interface{} {
func GetInvocationCounter() int {
return 0
}
// Platform returns the platform name, which is set to be `NEO`. This function uses
// `System.Runtime.Platform` syscall.
func Platform() []byte {
return nil
}

View file

@ -126,12 +126,25 @@ func init() {
// EnumeratorCreate handles syscall System.Enumerator.Create.
func EnumeratorCreate(v *VM) error {
data := v.Estack().Pop().Array()
v.Estack().Push(&Element{
value: stackitem.NewInterop(&arrayWrapper{
var interop interface{}
switch t := v.Estack().Pop().value.(type) {
case *stackitem.Array, *stackitem.Struct:
interop = &arrayWrapper{
index: -1,
value: t.Value().([]stackitem.Item),
}
default:
data, err := t.TryBytes()
if err != nil {
return fmt.Errorf("can not create enumerator from type %s: %v", t.Type(), err)
}
interop = &byteArrayWrapper{
index: -1,
value: data,
}),
}
}
v.Estack().Push(&Element{
value: stackitem.NewInterop(interop),
})
return nil
@ -185,7 +198,14 @@ func IteratorCreate(v *VM) error {
case *stackitem.Map:
item = NewMapIterator(t)
default:
return errors.New("non-iterable type")
data, err := t.TryBytes()
if err != nil {
return fmt.Errorf("non-iterable type %s", t.Type())
}
item = stackitem.NewInterop(&byteArrayWrapper{
index: -1,
value: data,
})
}
v.Estack().Push(&Element{value: item})

View file

@ -1,6 +1,8 @@
package vm
import (
"math/big"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
@ -15,6 +17,11 @@ type (
value []stackitem.Item
}
byteArrayWrapper struct {
index int
value []byte
}
concatEnum struct {
current enumerator
second enumerator
@ -63,6 +70,23 @@ func (a *arrayWrapper) Key() stackitem.Item {
return stackitem.Make(a.index)
}
func (a *byteArrayWrapper) Next() bool {
if next := a.index + 1; next < len(a.value) {
a.index = next
return true
}
return false
}
func (a *byteArrayWrapper) Value() stackitem.Item {
return stackitem.NewBigInteger(big.NewInt(int64(a.value[a.index])))
}
func (a *byteArrayWrapper) Key() stackitem.Item {
return stackitem.Make(a.index)
}
func (c *concatEnum) Next() bool {
if c.current.Next() {
return true