From 45bfce60a51f329be993049136824eb9f90ffc04 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 2 Oct 2020 18:42:23 +0300 Subject: [PATCH 1/3] core: remove error from runtime.GetInvocationCounter close #1444 --- pkg/core/interop/runtime/util.go | 6 ++++-- pkg/core/interop_system_test.go | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/core/interop/runtime/util.go b/pkg/core/interop/runtime/util.go index f9037887f..861be68bf 100644 --- a/pkg/core/interop/runtime/util.go +++ b/pkg/core/interop/runtime/util.go @@ -58,9 +58,11 @@ func GetNotifications(ic *interop.Context) error { // GetInvocationCounter returns how many times current contract was invoked during current tx execution. func GetInvocationCounter(ic *interop.Context) error { - count, ok := ic.Invocations[ic.VM.GetCurrentScriptHash()] + currentScriptHash := ic.VM.GetCurrentScriptHash() + count, ok := ic.Invocations[currentScriptHash] if !ok { - return errors.New("current contract wasn't invoked from others") + count = 1 + ic.Invocations[currentScriptHash] = count } ic.VM.Estack().PushVal(count) return nil diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index d947fb08e..49e88acfa 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -298,9 +298,11 @@ func TestRuntimeGetInvocationCounter(t *testing.T) { ic.Invocations[hash.Hash160([]byte{2})] = 42 - t.Run("Zero", func(t *testing.T) { + t.Run("No invocations", func(t *testing.T) { v.LoadScript([]byte{1}) - require.Error(t, runtime.GetInvocationCounter(ic)) + // do not return an error in this case. + require.NoError(t, runtime.GetInvocationCounter(ic)) + require.EqualValues(t, 1, v.Estack().Pop().BigInt().Int64()) }) t.Run("NonZero", func(t *testing.T) { v.LoadScript([]byte{2}) From 6ce00fde82d33bd7af15b97290730df8b695cf1b Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 5 Oct 2020 13:08:55 +0300 Subject: [PATCH 2/3] vm, core: move invocation counter from InteropContext to VM --- pkg/core/interop/context.go | 2 -- pkg/core/interop/contract/call.go | 2 +- pkg/core/interop/runtime/util.go | 4 ++-- pkg/core/interop_system_test.go | 2 +- pkg/vm/vm.go | 4 ++++ 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index 61ab10961..7528f61f6 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -35,7 +35,6 @@ type Context struct { DAO *dao.Cached Notifications []state.NotificationEvent Log *zap.Logger - Invocations map[util.Uint160]int VM *vm.VM Functions [][]Function } @@ -53,7 +52,6 @@ func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, n DAO: dao, Notifications: nes, Log: log, - Invocations: make(map[util.Uint160]int), // Functions is a slice of slices of interops sorted by ID. Functions: [][]Function{}, } diff --git a/pkg/core/interop/contract/call.go b/pkg/core/interop/contract/call.go index cb0b4a333..e27a54894 100644 --- a/pkg/core/interop/contract/call.go +++ b/pkg/core/interop/contract/call.go @@ -67,7 +67,7 @@ func CallExInternal(ic *interop.Context, cs *state.Contract, } u := cs.ScriptHash() - ic.Invocations[u]++ + ic.VM.Invocations[u]++ ic.VM.LoadScriptWithHash(cs.Script, u, ic.VM.Context().GetCallFlags()&f) var isNative bool for i := range ic.Natives { diff --git a/pkg/core/interop/runtime/util.go b/pkg/core/interop/runtime/util.go index 861be68bf..de17a0b59 100644 --- a/pkg/core/interop/runtime/util.go +++ b/pkg/core/interop/runtime/util.go @@ -59,10 +59,10 @@ func GetNotifications(ic *interop.Context) error { // GetInvocationCounter returns how many times current contract was invoked during current tx execution. func GetInvocationCounter(ic *interop.Context) error { currentScriptHash := ic.VM.GetCurrentScriptHash() - count, ok := ic.Invocations[currentScriptHash] + count, ok := ic.VM.Invocations[currentScriptHash] if !ok { count = 1 - ic.Invocations[currentScriptHash] = count + ic.VM.Invocations[currentScriptHash] = count } ic.VM.Estack().PushVal(count) return nil diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index 49e88acfa..7abdfcbd7 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -296,7 +296,7 @@ func TestRuntimeGetInvocationCounter(t *testing.T) { v, ic, chain := createVM(t) defer chain.Close() - ic.Invocations[hash.Hash160([]byte{2})] = 42 + ic.VM.Invocations[hash.Hash160([]byte{2})] = 42 t.Run("No invocations", func(t *testing.T) { v.LoadScript([]byte{1}) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index ffc9332e6..706c6fb96 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -80,6 +80,9 @@ type VM struct { SyscallHandler func(v *VM, id uint32) error trigger trigger.Type + + // Invocations is a script invocation counter. + Invocations map[util.Uint160]int } // New returns a new VM object ready to load AVM bytecode scripts. @@ -96,6 +99,7 @@ func NewWithTrigger(t trigger.Type) *VM { trigger: t, SyscallHandler: defaultSyscallHandler, + Invocations: make(map[util.Uint160]int), } vm.estack = vm.newItemStack("evaluation") From cbf89fbb192dca7a50ed1a39e5d1ef9e99c18924 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 5 Oct 2020 13:35:53 +0300 Subject: [PATCH 3/3] vm: add Call method which increments invocation counter --- pkg/vm/vm.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 706c6fb96..0a18f3b59 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -1229,7 +1229,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro v.checkInvocationStackSize() // Note: jump offset must be calculated regarding to new context, // but it is cloned and thus has the same script and instruction pointer. - v.Call(ctx, v.getJumpOffset(ctx, parameter)) + v.call(ctx, v.getJumpOffset(ctx, parameter)) case opcode.CALLA: ptr := v.estack.Pop().Item().(*stackitem.Pointer) @@ -1237,7 +1237,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro panic("invalid script in pointer") } - v.Call(ctx, ptr.Position()) + v.call(ctx, ptr.Position()) case opcode.SYSCALL: interopID := GetInteropID(parameter) @@ -1459,8 +1459,17 @@ func (v *VM) Jump(ctx *Context, offset int) { } // Call calls method by offset. It is similar to Jump but also -// pushes new context to the invocation state +// pushes new context to the invocation stack and increments +// invocation counter for the corresponding context script hash. func (v *VM) Call(ctx *Context, offset int) { + v.call(ctx, offset) + v.Invocations[ctx.ScriptHash()]++ +} + +// call is an internal representation of Call, which does not +// affect the invocation counter and is only being used by vm +// package. +func (v *VM) call(ctx *Context, offset int) { newCtx := ctx.Copy() newCtx.CheckReturn = false newCtx.local = nil