core: implement System.Runtime.GetInvocationCounter syscall

This commit is contained in:
Evgenii Stratonikov 2020-06-16 12:47:42 +03:00
parent 75e597f880
commit 3762ebdd08
7 changed files with 39 additions and 0 deletions

View file

@ -29,6 +29,7 @@ var syscalls = map[string]map[string]string{
"GetEntryScriptHash": "System.Runtime.GetEntryScriptHash", "GetEntryScriptHash": "System.Runtime.GetEntryScriptHash",
"GetExecutingScriptHash": "System.Runtime.GetExecutingScriptHash", "GetExecutingScriptHash": "System.Runtime.GetExecutingScriptHash",
"GetNotifications": "System.Runtime.GetNotifications", "GetNotifications": "System.Runtime.GetNotifications",
"GetInvocationCounter": "System.Runtime.GetInvocationCounter",
"GasLeft": "System.Runtime.GasLeft", "GasLeft": "System.Runtime.GasLeft",
"GetTrigger": "System.Runtime.GetTrigger", "GetTrigger": "System.Runtime.GetTrigger",

View file

@ -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),
} }
} }

View file

@ -49,3 +49,13 @@ func GetNotifications(ic *interop.Context, v *vm.VM) error {
v.Estack().PushVal(arr) v.Estack().PushVal(arr)
return nil 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
}

View file

@ -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)

View file

@ -7,6 +7,7 @@ import (
"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/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"
@ -244,3 +245,20 @@ func TestRuntimeGetNotifications(t *testing.T) {
require.Equal(t, ic.Notifications[1].Item, elem[1]) 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())
})
}

View file

@ -111,6 +111,7 @@ var systemInterops = []interop.Function{
{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.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},

View file

@ -68,3 +68,9 @@ func GasLeft() int64 {
func GetNotifications(h []byte) [][]interface{} { func GetNotifications(h []byte) [][]interface{} {
return nil 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
}