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}, "CheckWitness": {"System.Runtime.CheckWitness", false},
"Log": {"System.Runtime.Log", false}, "Log": {"System.Runtime.Log", false},
"Notify": {"System.Runtime.Notify", false}, "Notify": {"System.Runtime.Notify", false},
"Platform": {"System.Runtime.Platform", false},
}, },
"storage": { "storage": {
"ConvertContextToReadOnly": {"System.Storage.AsReadOnly", false}, "ConvertContextToReadOnly": {"System.Storage.AsReadOnly", false},

View file

@ -10,7 +10,7 @@ func Concat(_ *interop.Context, v *vm.VM) error {
return vm.EnumeratorConcat(v) 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 { func Create(_ *interop.Context, v *vm.VM) error {
return vm.EnumeratorCreate(v) return vm.EnumeratorCreate(v)
} }

View file

@ -11,7 +11,11 @@ import (
// GasLeft returns remaining amount of GAS. // GasLeft returns remaining amount of GAS.
func GasLeft(_ *interop.Context, v *vm.VM) error { 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 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) return checkScope(ic.DAO, tx, ic.ScriptGetter, hash)
} }
// only for non-Transaction types (Block, etc.) return false, errors.New("script container is not a transaction")
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
} }
func checkScope(d dao.DAO, tx *transaction.Transaction, v vm.ScriptHashGetter, hash util.Uint160) (bool, error) { func checkScope(d dao.DAO, tx *transaction.Transaction, v vm.ScriptHashGetter, hash util.Uint160) (bool, error) {

View file

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
"unicode/utf8"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer" "github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
@ -29,6 +30,8 @@ const (
MaxTraceableBlocks = transaction.MaxValidUntilBlockIncrement MaxTraceableBlocks = transaction.MaxValidUntilBlockIncrement
// MaxEventNameLen is the maximum length of a name for event. // MaxEventNameLen is the maximum length of a name for event.
MaxEventNameLen = 32 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 // 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 { if len(name) > MaxEventNameLen {
return fmt.Errorf("event name must be less than %d", 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() elem := v.Estack().Pop()
args := elem.Array() args := elem.Array()
// But it has to be serializable, otherwise we either have some broken // 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. // runtimeLog logs the message passed.
func runtimeLog(ic *interop.Context, v *vm.VM) error { 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", ic.Log.Info("runtime log",
zap.Stringer("script", v.GetCurrentScriptHash()), zap.Stringer("script", v.GetCurrentScriptHash()),
zap.String("logs", msg)) 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.Iterator.Values", Func: iterator.Values, Price: 400},
{Name: "System.Json.Deserialize", Func: json.Deserialize, Price: 500000}, {Name: "System.Json.Deserialize", Func: json.Deserialize, Price: 500000},
{Name: "System.Json.Serialize", Func: json.Serialize, Price: 100000}, {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.GasLeft", Func: runtime.GasLeft, Price: 400},
{Name: "System.Runtime.GetCallingScriptHash", Func: engineGetCallingScriptHash, Price: 400}, {Name: "System.Runtime.GetCallingScriptHash", Func: engineGetCallingScriptHash, Price: 400},
{Name: "System.Runtime.GetEntryScriptHash", Func: engineGetEntryScriptHash, Price: 400}, {Name: "System.Runtime.GetEntryScriptHash", Func: engineGetEntryScriptHash, Price: 400},
{Name: "System.Runtime.GetExecutingScriptHash", Func: engineGetExecutingScriptHash, Price: 400}, {Name: "System.Runtime.GetExecutingScriptHash", Func: engineGetExecutingScriptHash, Price: 400},
{Name: "System.Runtime.GetInvocationCounter", Func: runtime.GetInvocationCounter, 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.GetScriptContainer", Func: engineGetScriptContainer, Price: 250},
{Name: "System.Runtime.GetTime", Func: runtimeGetTime, 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.GetTrigger", Func: runtimeGetTrigger, Price: 250},
{Name: "System.Runtime.Log", Func: runtimeLog, Price: 1000000, RequiredFlags: smartcontract.AllowNotify}, {Name: "System.Runtime.Log", Func: runtimeLog, Price: 1000000, RequiredFlags: smartcontract.AllowNotify},
{Name: "System.Runtime.Notify", Func: runtimeNotify, 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. // or structures that have values with no explicit keys.
type Enumerator struct{} type Enumerator struct{}
// Create creates a new enumerator from the given items (slice or structure). // Create creates a new enumerator from the given items (slice, structure, byte
// New enumerator points at index -1 of its items, so the user of it has to // array and integer or boolean converted to byte array). New enumerator points
// advance it first with Next. This function uses `System.Enumerator.Create` // at index -1 of its items, so the user of it has to advance it first with Next.
// syscall. // This function uses `System.Enumerator.Create` syscall.
func Create(items []interface{}) Enumerator { func Create(items interface{}) Enumerator {
return 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. // structure is similar in function to Neo .net framework's Iterator.
type Iterator struct{} type Iterator struct{}
// Create creates an iterator from the given items (array, struct or map). A new // Create creates an iterator from the given items (array, struct, map, byte
// iterator is set to point at element -1, so to access its first element you // array or integer and boolean converted to byte array). A new iterator is set
// need to call Next first. This function uses `System.Iterator.Create` syscall. // to point at element -1, so to access its first element you need to call Next
func Create(items []interface{}) Iterator { // first. This function uses `System.Iterator.Create` syscall.
func Create(items interface{}) Iterator {
return Iterator{} return Iterator{}
} }

View file

@ -80,3 +80,9 @@ func GetNotifications(h []byte) [][]interface{} {
func GetInvocationCounter() int { func GetInvocationCounter() int {
return 0 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. // EnumeratorCreate handles syscall System.Enumerator.Create.
func EnumeratorCreate(v *VM) error { func EnumeratorCreate(v *VM) error {
data := v.Estack().Pop().Array() var interop interface{}
v.Estack().Push(&Element{ switch t := v.Estack().Pop().value.(type) {
value: stackitem.NewInterop(&arrayWrapper{ 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, index: -1,
value: data, value: data,
}), }
}
v.Estack().Push(&Element{
value: stackitem.NewInterop(interop),
}) })
return nil return nil
@ -185,7 +198,14 @@ func IteratorCreate(v *VM) error {
case *stackitem.Map: case *stackitem.Map:
item = NewMapIterator(t) item = NewMapIterator(t)
default: 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}) v.Estack().Push(&Element{value: item})

View file

@ -1,6 +1,8 @@
package vm package vm
import ( import (
"math/big"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
@ -15,6 +17,11 @@ type (
value []stackitem.Item value []stackitem.Item
} }
byteArrayWrapper struct {
index int
value []byte
}
concatEnum struct { concatEnum struct {
current enumerator current enumerator
second enumerator second enumerator
@ -63,6 +70,23 @@ func (a *arrayWrapper) Key() stackitem.Item {
return stackitem.Make(a.index) 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 { func (c *concatEnum) Next() bool {
if c.current.Next() { if c.current.Next() {
return true return true