From 29cda5112a41f4aeb4ace1bd8722e9535e605049 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 19 Nov 2021 20:02:32 +0300 Subject: [PATCH] vm: simplify NEF loading by providing a special method Offsets are still handled outside of VM to avoid introducing manifest dependency (that's likely to be circular). --- pkg/core/blockchain.go | 14 +++++++------- pkg/core/interop/contract/call.go | 19 +++++++++---------- pkg/vm/vm.go | 21 ++++++++++++++++++--- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 6e8541f73..df7c1adfd 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -2185,14 +2185,14 @@ func (bc *Blockchain) InitVerificationVM(v *vm.VM, getContract func(util.Uint160 if md == nil || md.ReturnType != smartcontract.BoolType { return ErrInvalidVerificationContract } - initMD := cs.Manifest.ABI.GetMethod(manifest.MethodInit, 0) - v.LoadScriptWithHash(cs.NEF.Script, hash, callflag.ReadOnly) - v.Context().NEF = &cs.NEF - v.Context().Jump(md.Offset) - - if initMD != nil { - v.Call(initMD.Offset) + verifyOffset := md.Offset + initOffset := -1 + md = cs.Manifest.ABI.GetMethod(manifest.MethodInit, 0) + if md != nil { + initOffset = md.Offset } + v.LoadNEFMethod(&cs.NEF, util.Uint160{}, hash, callflag.ReadOnly, + true, verifyOffset, initOffset) } if len(witness.InvocationScript) != 0 { err := vm.IsScriptCorrect(witness.InvocationScript, nil) diff --git a/pkg/core/interop/contract/call.go b/pkg/core/interop/contract/call.go index c9c7bbc5a..484a857cb 100644 --- a/pkg/core/interop/contract/call.go +++ b/pkg/core/interop/contract/call.go @@ -112,20 +112,19 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra return fmt.Errorf("invalid argument count: %d (expected %d)", len(args), len(md.Parameters)) } + methodOff := md.Offset + initOff := -1 + md = cs.Manifest.ABI.GetMethod(manifest.MethodInit, 0) + if md != nil { + initOff = md.Offset + } ic.VM.Invocations[cs.Hash]++ - ic.VM.LoadScriptWithCallingHash(caller, cs.NEF.Script, cs.Hash, ic.VM.Context().GetCallFlags()&f, hasReturn) - ic.VM.Context().NEF = &cs.NEF + ic.VM.LoadNEFMethod(&cs.NEF, caller, cs.Hash, ic.VM.Context().GetCallFlags()&f, + hasReturn, methodOff, initOff) + for e, i := ic.VM.Estack(), len(args)-1; i >= 0; i-- { e.PushItem(args[i]) } - // Move IP to the target method. - ic.VM.Context().Jump(md.Offset) - - md = cs.Manifest.ABI.GetMethod(manifest.MethodInit, 0) - if md != nil { - ic.VM.Call(md.Offset) - } - return nil } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 855aa7cb4..42209fb5f 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -297,12 +297,27 @@ func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) { // each other. func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f callflag.CallFlag) { shash := v.GetCurrentScriptHash() - v.LoadScriptWithCallingHash(shash, b, hash, f, true) + v.loadScriptWithCallingHash(shash, b, hash, f, true) } -// LoadScriptWithCallingHash is similar to LoadScriptWithHash but sets calling hash explicitly. +// LoadNEFMethod allows to create a context to execute a method from the NEF +// file with specified caller and executing hash, call flags, return value, +// method and _initialize offsets. +func (v *VM) LoadNEFMethod(exe *nef.File, caller util.Uint160, hash util.Uint160, f callflag.CallFlag, + hasReturn bool, methodOff int, initOff int) { + v.loadScriptWithCallingHash(caller, exe.Script, hash, f, hasReturn) + ctx := v.Context() + ctx.NEF = exe + // Move IP to the target method. + ctx.Jump(methodOff) + if initOff >= 0 { + v.Call(initOff) + } +} + +// loadScriptWithCallingHash is similar to LoadScriptWithHash but sets calling hash explicitly. // It should be used for calling from native contracts. -func (v *VM) LoadScriptWithCallingHash(caller util.Uint160, b []byte, hash util.Uint160, +func (v *VM) loadScriptWithCallingHash(caller util.Uint160, b []byte, hash util.Uint160, f callflag.CallFlag, hasReturn bool) { v.LoadScriptWithFlags(b, f) ctx := v.Context()