forked from TrueCloudLab/neoneo-go
Merge pull request #1218 from nspcc-dev/neo3/interop/post-preview2_adjustment2
interop: post-preview2 adjustment, part 2
This commit is contained in:
commit
06f70e6a0b
11 changed files with 91 additions and 30 deletions
|
@ -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},
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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},
|
||||||
|
|
|
@ -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{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue