forked from TrueCloudLab/neoneo-go
Merge pull request #1056 from nspcc-dev/fix/runtime
Adjust runtime syscalls to NEO3
This commit is contained in:
commit
06ef1e6c74
21 changed files with 244 additions and 82 deletions
|
@ -1,22 +1,21 @@
|
||||||
package enginecontract
|
package enginecontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Main is that famous Main() function, you know.
|
// Main is that famous Main() function, you know.
|
||||||
func Main() bool {
|
func Main() bool {
|
||||||
tx := engine.GetScriptContainer()
|
tx := runtime.GetScriptContainer()
|
||||||
runtime.Notify(tx)
|
runtime.Notify(tx)
|
||||||
|
|
||||||
callingScriptHash := engine.GetCallingScriptHash()
|
callingScriptHash := runtime.GetCallingScriptHash()
|
||||||
runtime.Notify(callingScriptHash)
|
runtime.Notify(callingScriptHash)
|
||||||
|
|
||||||
execScriptHash := engine.GetExecutingScriptHash()
|
execScriptHash := runtime.GetExecutingScriptHash()
|
||||||
runtime.Notify(execScriptHash)
|
runtime.Notify(execScriptHash)
|
||||||
|
|
||||||
entryScriptHash := engine.GetEntryScriptHash()
|
entryScriptHash := runtime.GetEntryScriptHash()
|
||||||
runtime.Notify(entryScriptHash)
|
runtime.Notify(entryScriptHash)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package nep5
|
package nep5
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/util"
|
"github.com/nspcc-dev/neo-go/pkg/interop/util"
|
||||||
|
@ -85,7 +84,7 @@ func IsUsableAddress(addr []byte) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a smart contract is calling scripthash
|
// Check if a smart contract is calling scripthash
|
||||||
callingScriptHash := engine.GetCallingScriptHash()
|
callingScriptHash := runtime.GetCallingScriptHash()
|
||||||
if util.Equals(callingScriptHash, addr) {
|
if util.Equals(callingScriptHash, addr) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -586,6 +586,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
if tv := c.typeAndValueOf(n); tv.Value != nil {
|
if tv := c.typeAndValueOf(n); tv.Value != nil {
|
||||||
c.emitLoadConst(tv)
|
c.emitLoadConst(tv)
|
||||||
|
} else if n.Name == "nil" {
|
||||||
|
emit.Opcode(c.prog.BinWriter, opcode.PUSHNULL)
|
||||||
} else {
|
} else {
|
||||||
c.emitLoadVar(n.Name)
|
c.emitLoadVar(n.Name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,14 @@ var syscalls = map[string]map[string]string{
|
||||||
"Put": "System.Storage.Put",
|
"Put": "System.Storage.Put",
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
|
"GetScriptContainer": "System.Runtime.GetScriptContainer",
|
||||||
|
"GetCallingScriptHash": "System.Runtime.GetCallingScriptHash",
|
||||||
|
"GetEntryScriptHash": "System.Runtime.GetEntryScriptHash",
|
||||||
|
"GetExecutingScriptHash": "System.Runtime.GetExecutingScriptHash",
|
||||||
|
"GetNotifications": "System.Runtime.GetNotifications",
|
||||||
|
"GetInvocationCounter": "System.Runtime.GetInvocationCounter",
|
||||||
|
|
||||||
|
"GasLeft": "System.Runtime.GasLeft",
|
||||||
"GetTrigger": "System.Runtime.GetTrigger",
|
"GetTrigger": "System.Runtime.GetTrigger",
|
||||||
"CheckWitness": "System.Runtime.CheckWitness",
|
"CheckWitness": "System.Runtime.CheckWitness",
|
||||||
"Notify": "System.Runtime.Notify",
|
"Notify": "System.Runtime.Notify",
|
||||||
|
@ -44,12 +52,9 @@ var syscalls = map[string]map[string]string{
|
||||||
"Create": "System.Contract.Create",
|
"Create": "System.Contract.Create",
|
||||||
"Destroy": "System.Contract.Destroy",
|
"Destroy": "System.Contract.Destroy",
|
||||||
"Update": "System.Contract.Update",
|
"Update": "System.Contract.Update",
|
||||||
},
|
|
||||||
"engine": {
|
"IsStandard": "System.Contract.IsStandard",
|
||||||
"GetScriptContainer": "System.ExecutionEngine.GetScriptContainer",
|
"CreateStandardAccount": "System.Contract.CreateStandardAccount",
|
||||||
"GetCallingScriptHash": "System.ExecutionEngine.GetCallingScriptHash",
|
|
||||||
"GetEntryScriptHash": "System.ExecutionEngine.GetEntryScriptHash",
|
|
||||||
"GetExecutingScriptHash": "System.ExecutionEngine.GetExecutingScriptHash",
|
|
||||||
},
|
},
|
||||||
"iterator": {
|
"iterator": {
|
||||||
"Concat": "System.Iterator.Concat",
|
"Concat": "System.Iterator.Concat",
|
||||||
|
|
|
@ -566,7 +566,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
v.LoadScriptWithFlags(tx.Script, smartcontract.All)
|
v.LoadScriptWithFlags(tx.Script, smartcontract.All)
|
||||||
v.SetPriceGetter(getPrice)
|
v.SetPriceGetter(getPrice)
|
||||||
if bc.config.FreeGasLimit > 0 {
|
if bc.config.FreeGasLimit > 0 {
|
||||||
v.SetGasLimit(bc.config.FreeGasLimit + tx.SystemFee)
|
v.GasLimit = bc.config.FreeGasLimit + tx.SystemFee
|
||||||
}
|
}
|
||||||
|
|
||||||
err := v.Run()
|
err := v.Run()
|
||||||
|
|
|
@ -30,6 +30,7 @@ type Context struct {
|
||||||
DAO *dao.Cached
|
DAO *dao.Cached
|
||||||
Notifications []state.NotificationEvent
|
Notifications []state.NotificationEvent
|
||||||
Log *zap.Logger
|
Log *zap.Logger
|
||||||
|
Invocations map[util.Uint160]int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext returns new interop context.
|
// NewContext returns new interop context.
|
||||||
|
@ -45,6 +46,7 @@ func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, n
|
||||||
DAO: dao,
|
DAO: dao,
|
||||||
Notifications: nes,
|
Notifications: nes,
|
||||||
Log: log,
|
Log: log,
|
||||||
|
Invocations: make(map[util.Uint160]int),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
61
pkg/core/interop/runtime/util.go
Normal file
61
pkg/core/interop/runtime/util.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInvocationCounter returns how many times current contract was invoked during current tx execution.
|
||||||
|
func GetInvocationCounter(ic *interop.Context, v *vm.VM) error {
|
||||||
|
count, ok := ic.Invocations[v.GetCurrentScriptHash()]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("current contract wasn't invoked from others")
|
||||||
|
}
|
||||||
|
v.Estack().PushVal(count)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -443,6 +443,7 @@ func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, method stac
|
||||||
return errors.New("disallowed method call")
|
return errors.New("disallowed method call")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ic.Invocations[u]++
|
||||||
v.LoadScriptWithHash(cs.Script, u, v.Context().GetCallFlags()&f)
|
v.LoadScriptWithHash(cs.Script, u, v.Context().GetCallFlags()&f)
|
||||||
v.Estack().PushVal(args)
|
v.Estack().PushVal(args)
|
||||||
v.Estack().PushVal(method)
|
v.Estack().PushVal(method)
|
||||||
|
|
|
@ -5,7 +5,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/dbft/crypto"
|
"github.com/nspcc-dev/dbft/crypto"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
@ -197,3 +199,66 @@ func TestContractCreateAccount(t *testing.T) {
|
||||||
require.Error(t, contractCreateStandardAccount(ic, v))
|
require.Error(t, contractCreateStandardAccount(ic, v))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, 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])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRuntimeGetInvocationCounter(t *testing.T) {
|
||||||
|
v, ic, chain := createVM(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
|
ic.Invocations[hash.Hash160([]byte{2})] = 42
|
||||||
|
|
||||||
|
t.Run("Zero", func(t *testing.T) {
|
||||||
|
v.LoadScript([]byte{1})
|
||||||
|
require.Error(t, runtime.GetInvocationCounter(ic, v))
|
||||||
|
})
|
||||||
|
t.Run("NonZero", func(t *testing.T) {
|
||||||
|
v.LoadScript([]byte{2})
|
||||||
|
require.NoError(t, runtime.GetInvocationCounter(ic, v))
|
||||||
|
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -99,10 +99,6 @@ var systemInterops = []interop.Function{
|
||||||
{Name: "System.Enumerator.Create", Func: enumerator.Create, Price: 400},
|
{Name: "System.Enumerator.Create", Func: enumerator.Create, Price: 400},
|
||||||
{Name: "System.Enumerator.Next", Func: enumerator.Next, Price: 1000000},
|
{Name: "System.Enumerator.Next", Func: enumerator.Next, Price: 1000000},
|
||||||
{Name: "System.Enumerator.Value", Func: enumerator.Value, Price: 400},
|
{Name: "System.Enumerator.Value", Func: enumerator.Value, Price: 400},
|
||||||
{Name: "System.ExecutionEngine.GetCallingScriptHash", Func: engineGetCallingScriptHash, Price: 1},
|
|
||||||
{Name: "System.ExecutionEngine.GetEntryScriptHash", Func: engineGetEntryScriptHash, Price: 1},
|
|
||||||
{Name: "System.ExecutionEngine.GetExecutingScriptHash", Func: engineGetExecutingScriptHash, Price: 1},
|
|
||||||
{Name: "System.ExecutionEngine.GetScriptContainer", Func: engineGetScriptContainer, Price: 1},
|
|
||||||
{Name: "System.Iterator.Concat", Func: iterator.Concat, Price: 400},
|
{Name: "System.Iterator.Concat", Func: iterator.Concat, Price: 400},
|
||||||
{Name: "System.Iterator.Create", Func: iterator.Create, Price: 400},
|
{Name: "System.Iterator.Create", Func: iterator.Create, Price: 400},
|
||||||
{Name: "System.Iterator.Key", Func: iterator.Key, Price: 400},
|
{Name: "System.Iterator.Key", Func: iterator.Key, Price: 400},
|
||||||
|
@ -110,13 +106,19 @@ 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: 200, RequiredFlags: smartcontract.AllowStates},
|
{Name: "System.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 30000},
|
||||||
{Name: "System.Runtime.GetTime", Func: runtimeGetTime, Price: 1,
|
{Name: "System.Runtime.GasLeft", Func: runtime.GasLeft, Price: 400},
|
||||||
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates},
|
{Name: "System.Runtime.GetCallingScriptHash", Func: engineGetCallingScriptHash, Price: 400},
|
||||||
{Name: "System.Runtime.GetTrigger", Func: runtimeGetTrigger, Price: 1},
|
{Name: "System.Runtime.GetEntryScriptHash", Func: engineGetEntryScriptHash, Price: 400},
|
||||||
{Name: "System.Runtime.Log", Func: runtimeLog, Price: 1, RequiredFlags: smartcontract.AllowNotify},
|
{Name: "System.Runtime.GetExecutingScriptHash", Func: engineGetExecutingScriptHash, Price: 400},
|
||||||
{Name: "System.Runtime.Notify", Func: runtimeNotify, Price: 1, RequiredFlags: smartcontract.AllowNotify},
|
{Name: "System.Runtime.GetInvocationCounter", Func: runtime.GetInvocationCounter, Price: 400},
|
||||||
{Name: "System.Runtime.Platform", Func: runtimePlatform, Price: 1},
|
{Name: "System.Runtime.GetScriptContainer", Func: engineGetScriptContainer, Price: 250},
|
||||||
|
{Name: "System.Runtime.GetTime", Func: runtimeGetTime, Price: 250,
|
||||||
|
AllowedTriggers: trigger.Application},
|
||||||
|
{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},
|
||||||
|
{Name: "System.Runtime.Platform", Func: runtimePlatform, Price: 250},
|
||||||
{Name: "System.Storage.Delete", Func: storageDelete, Price: StoragePrice,
|
{Name: "System.Storage.Delete", Func: storageDelete, Price: StoragePrice,
|
||||||
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates},
|
AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates},
|
||||||
{Name: "System.Storage.Find", Func: storageFind, Price: 1000000,
|
{Name: "System.Storage.Find", Func: storageFind, Price: 1000000,
|
||||||
|
|
|
@ -32,3 +32,15 @@ func Update(script []byte, manifest []byte) Contract {
|
||||||
// not by any outside code. When contract is deleted all associated storage
|
// not by any outside code. When contract is deleted all associated storage
|
||||||
// items are deleted too. This function uses `System.Contract.Destroy` syscall.
|
// items are deleted too. This function uses `System.Contract.Destroy` syscall.
|
||||||
func Destroy() {}
|
func Destroy() {}
|
||||||
|
|
||||||
|
// IsStandard checks if contract with provided hash is a standard signature/multisig contract.
|
||||||
|
// This function uses `System.Contract.IsStandard` syscall.
|
||||||
|
func IsStandard(h []byte) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateStandardAccount calculates script hash of a given public key.
|
||||||
|
// This function uses `System.Contract.CreateStandardAccount` syscall.
|
||||||
|
func CreateStandardAccount(pub []byte) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,47 +1,10 @@
|
||||||
/*
|
/*
|
||||||
Package engine provides access to VM execution metadata and allows to make contract calls.
|
Package engine allows to make contract calls.
|
||||||
It's roughly similar in function to ExecutionEngine class in the Neo .net
|
It's roughly similar in function to ExecutionEngine class in the Neo .net
|
||||||
framework.
|
framework.
|
||||||
*/
|
*/
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
|
||||||
|
|
||||||
// GetScriptContainer returns the transaction that initially triggered current
|
|
||||||
// execution context. It never changes in a single execution, no matter how deep
|
|
||||||
// this execution goes. This function uses
|
|
||||||
// `System.ExecutionEngine.GetScriptContainer` syscall.
|
|
||||||
func GetScriptContainer() blockchain.Transaction {
|
|
||||||
return blockchain.Transaction{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExecutingScriptHash returns script hash (160 bit in BE form represented
|
|
||||||
// as 20-byte slice) of the contract that is currently being executed. Any
|
|
||||||
// AppCall can change the value returned by this function if it calls a
|
|
||||||
// different contract. This function uses
|
|
||||||
// `System.ExecutionEngine.GetExecutingScriptHash` syscall.
|
|
||||||
func GetExecutingScriptHash() []byte {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCallingScriptHash returns script hash (160 bit in BE form represented
|
|
||||||
// as 20-byte slice) of the contract that started the execution of the currently
|
|
||||||
// running context (caller of current contract or function), so it's one level
|
|
||||||
// above the GetExecutingScriptHash in the call stack. It uses
|
|
||||||
// `System.ExecutionEngine.GetCallingScriptHash` syscall.
|
|
||||||
func GetCallingScriptHash() []byte {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEntryScriptHash returns script hash (160 bit in BE form represented
|
|
||||||
// as 20-byte slice) of the contract that initially started current execution
|
|
||||||
// (this is a script that is contained in a transaction returned by
|
|
||||||
// GetScriptContainer) execution from the start. This function uses
|
|
||||||
// `System.ExecutionEngine.GetEntryScriptHash` syscall.
|
|
||||||
func GetEntryScriptHash() []byte {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppCall executes previously deployed blockchain contract with specified hash
|
// AppCall executes previously deployed blockchain contract with specified hash
|
||||||
// (160 bit in BE form represented as 20-byte slice) using provided arguments.
|
// (160 bit in BE form represented as 20-byte slice) using provided arguments.
|
||||||
// It returns whatever this contract returns. Even though this function accepts
|
// It returns whatever this contract returns. Even though this function accepts
|
||||||
|
|
38
pkg/interop/runtime/engine.go
Normal file
38
pkg/interop/runtime/engine.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
|
|
||||||
|
// GetScriptContainer returns the transaction that initially triggered current
|
||||||
|
// execution context. It never changes in a single execution, no matter how deep
|
||||||
|
// this execution goes. This function uses
|
||||||
|
// `System.Runtime.GetScriptContainer` syscall.
|
||||||
|
func GetScriptContainer() blockchain.Transaction {
|
||||||
|
return blockchain.Transaction{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExecutingScriptHash returns script hash (160 bit in BE form represented
|
||||||
|
// as 20-byte slice) of the contract that is currently being executed. Any
|
||||||
|
// AppCall can change the value returned by this function if it calls a
|
||||||
|
// different contract. This function uses
|
||||||
|
// `System.Runtime.GetExecutingScriptHash` syscall.
|
||||||
|
func GetExecutingScriptHash() []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCallingScriptHash returns script hash (160 bit in BE form represented
|
||||||
|
// as 20-byte slice) of the contract that started the execution of the currently
|
||||||
|
// running context (caller of current contract or function), so it's one level
|
||||||
|
// above the GetExecutingScriptHash in the call stack. It uses
|
||||||
|
// `System.Runtime.GetCallingScriptHash` syscall.
|
||||||
|
func GetCallingScriptHash() []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEntryScriptHash returns script hash (160 bit in BE form represented
|
||||||
|
// as 20-byte slice) of the contract that initially started current execution
|
||||||
|
// (this is a script that is contained in a transaction returned by
|
||||||
|
// GetScriptContainer) execution from the start. This function uses
|
||||||
|
// `System.Runtime.GetEntryScriptHash` syscall.
|
||||||
|
func GetEntryScriptHash() []byte {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -54,3 +54,23 @@ func Application() byte {
|
||||||
func Verification() byte {
|
func Verification() byte {
|
||||||
return 0x00
|
return 0x00
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GasLeft returns the amount of gas available for the current execution.
|
||||||
|
// This function uses `System.Runtime.GasLeft` syscall.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInvocationCounter returns how many times current contract was invoked during current tx execution.
|
||||||
|
// This function uses `System.Runtime.GetInvocationCounter` syscall.
|
||||||
|
func GetInvocationCounter() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
|
@ -906,7 +906,7 @@ func (s *Server) invokescript(reqParams request.Params) (interface{}, *response.
|
||||||
// result.
|
// result.
|
||||||
func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) *result.Invoke {
|
func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) *result.Invoke {
|
||||||
vm := s.chain.GetTestVM(tx)
|
vm := s.chain.GetTestVM(tx)
|
||||||
vm.SetGasLimit(s.config.MaxGasInvoke)
|
vm.GasLimit = s.config.MaxGasInvoke
|
||||||
vm.LoadScriptWithFlags(script, smartcontract.All)
|
vm.LoadScriptWithFlags(script, smartcontract.All)
|
||||||
_ = vm.Run()
|
_ = vm.Run()
|
||||||
result := &result.Invoke{
|
result := &result.Invoke{
|
||||||
|
|
|
@ -50,18 +50,18 @@ type rpcTestCase struct {
|
||||||
check func(t *testing.T, e *executor, result interface{})
|
check func(t *testing.T, e *executor, result interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
const testContractHash = "279c86355b30d452386c1717e2ffa01c9dac31f3"
|
const testContractHash = "e65ff7b3a02d207b584a5c27057d4e9862ef01da"
|
||||||
|
|
||||||
var rpcTestCases = map[string][]rpcTestCase{
|
var rpcTestCases = map[string][]rpcTestCase{
|
||||||
"getapplicationlog": {
|
"getapplicationlog": {
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["136ef2ba8259d121a173da2588ae0fbb735f0f740820fded9cd8da8fee109b20"]`,
|
params: `["9352fa8d351635bb151e7e5a3a923bfe4d8fb90f05b605ec00af95c2410b594d"]`,
|
||||||
result: func(e *executor) interface{} { return &result.ApplicationLog{} },
|
result: func(e *executor) interface{} { return &result.ApplicationLog{} },
|
||||||
check: func(t *testing.T, e *executor, acc interface{}) {
|
check: func(t *testing.T, e *executor, acc interface{}) {
|
||||||
res, ok := acc.(*result.ApplicationLog)
|
res, ok := acc.(*result.ApplicationLog)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
expectedTxHash, err := util.Uint256DecodeStringLE("136ef2ba8259d121a173da2588ae0fbb735f0f740820fded9cd8da8fee109b20")
|
expectedTxHash, err := util.Uint256DecodeStringLE("9352fa8d351635bb151e7e5a3a923bfe4d8fb90f05b605ec00af95c2410b594d")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, expectedTxHash, res.TxHash)
|
assert.Equal(t, expectedTxHash, res.TxHash)
|
||||||
assert.Equal(t, 1, len(res.Executions))
|
assert.Equal(t, 1, len(res.Executions))
|
||||||
|
@ -483,7 +483,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
"gettransactionheight": {
|
"gettransactionheight": {
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["136ef2ba8259d121a173da2588ae0fbb735f0f740820fded9cd8da8fee109b20"]`,
|
params: `["9352fa8d351635bb151e7e5a3a923bfe4d8fb90f05b605ec00af95c2410b594d"]`,
|
||||||
result: func(e *executor) interface{} {
|
result: func(e *executor) interface{} {
|
||||||
h := 0
|
h := 0
|
||||||
return &h
|
return &h
|
||||||
|
|
BIN
pkg/rpc/server/testdata/test_contract.avm
vendored
BIN
pkg/rpc/server/testdata/test_contract.avm
vendored
Binary file not shown.
3
pkg/rpc/server/testdata/test_contract.go
vendored
3
pkg/rpc/server/testdata/test_contract.go
vendored
|
@ -1,7 +1,6 @@
|
||||||
package testdata
|
package testdata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||||
)
|
)
|
||||||
|
@ -71,7 +70,7 @@ func Main(operation string, args []interface{}) interface{} {
|
||||||
return true
|
return true
|
||||||
case "init":
|
case "init":
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
h := engine.GetExecutingScriptHash()
|
h := runtime.GetExecutingScriptHash()
|
||||||
amount := totalSupply
|
amount := totalSupply
|
||||||
storage.Put(ctx, h, amount)
|
storage.Put(ctx, h, amount)
|
||||||
runtime.Notify("transfer", []byte{}, h, amount)
|
runtime.Notify("transfer", []byte{}, h, amount)
|
||||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
12
pkg/vm/vm.go
12
pkg/vm/vm.go
|
@ -78,7 +78,7 @@ type VM struct {
|
||||||
refs *refCounter
|
refs *refCounter
|
||||||
|
|
||||||
gasConsumed util.Fixed8
|
gasConsumed util.Fixed8
|
||||||
gasLimit util.Fixed8
|
GasLimit util.Fixed8
|
||||||
|
|
||||||
trigger trigger.Type
|
trigger trigger.Type
|
||||||
|
|
||||||
|
@ -134,16 +134,10 @@ func (v *VM) GasConsumed() util.Fixed8 {
|
||||||
return v.gasConsumed
|
return v.gasConsumed
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetGasLimit sets maximum amount of gas which v can spent.
|
|
||||||
// If max <= 0, no limit is imposed.
|
|
||||||
func (v *VM) SetGasLimit(max util.Fixed8) {
|
|
||||||
v.gasLimit = max
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddGas consumes specified amount of gas. It returns true iff gas limit wasn't exceeded.
|
// AddGas consumes specified amount of gas. It returns true iff gas limit wasn't exceeded.
|
||||||
func (v *VM) AddGas(gas util.Fixed8) bool {
|
func (v *VM) AddGas(gas util.Fixed8) bool {
|
||||||
v.gasConsumed += gas
|
v.gasConsumed += gas
|
||||||
return v.gasLimit == 0 || v.gasConsumed <= v.gasLimit
|
return v.GasLimit == 0 || v.gasConsumed <= v.GasLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
// Estack returns the evaluation stack so interop hooks can utilize this.
|
// Estack returns the evaluation stack so interop hooks can utilize this.
|
||||||
|
@ -520,7 +514,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
|
|
||||||
if v.getPrice != nil && ctx.ip < len(ctx.prog) {
|
if v.getPrice != nil && ctx.ip < len(ctx.prog) {
|
||||||
v.gasConsumed += v.getPrice(v, op, parameter)
|
v.gasConsumed += v.getPrice(v, op, parameter)
|
||||||
if v.gasLimit > 0 && v.gasConsumed > v.gasLimit {
|
if v.GasLimit > 0 && v.gasConsumed > v.GasLimit {
|
||||||
panic("gas limit is exceeded")
|
panic("gas limit is exceeded")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ func TestVM_SetPriceGetter(t *testing.T) {
|
||||||
|
|
||||||
t.Run("with sufficient gas limit", func(t *testing.T) {
|
t.Run("with sufficient gas limit", func(t *testing.T) {
|
||||||
v.Load(prog)
|
v.Load(prog)
|
||||||
v.SetGasLimit(9)
|
v.GasLimit = 9
|
||||||
runVM(t, v)
|
runVM(t, v)
|
||||||
|
|
||||||
require.EqualValues(t, 9, v.GasConsumed())
|
require.EqualValues(t, 9, v.GasConsumed())
|
||||||
|
@ -98,14 +98,14 @@ func TestVM_SetPriceGetter(t *testing.T) {
|
||||||
|
|
||||||
t.Run("with small gas limit", func(t *testing.T) {
|
t.Run("with small gas limit", func(t *testing.T) {
|
||||||
v.Load(prog)
|
v.Load(prog)
|
||||||
v.SetGasLimit(8)
|
v.GasLimit = 8
|
||||||
checkVMFailed(t, v)
|
checkVMFailed(t, v)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddGas(t *testing.T) {
|
func TestAddGas(t *testing.T) {
|
||||||
v := New()
|
v := New()
|
||||||
v.SetGasLimit(10)
|
v.GasLimit = 10
|
||||||
require.True(t, v.AddGas(5))
|
require.True(t, v.AddGas(5))
|
||||||
require.True(t, v.AddGas(5))
|
require.True(t, v.AddGas(5))
|
||||||
require.False(t, v.AddGas(5))
|
require.False(t, v.AddGas(5))
|
||||||
|
|
Loading…
Reference in a new issue