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).
This commit is contained in:
Roman Khimov 2021-11-19 20:02:32 +03:00
parent 82d2231ea6
commit 29cda5112a
3 changed files with 34 additions and 20 deletions

View file

@ -2185,14 +2185,14 @@ func (bc *Blockchain) InitVerificationVM(v *vm.VM, getContract func(util.Uint160
if md == nil || md.ReturnType != smartcontract.BoolType { if md == nil || md.ReturnType != smartcontract.BoolType {
return ErrInvalidVerificationContract return ErrInvalidVerificationContract
} }
initMD := cs.Manifest.ABI.GetMethod(manifest.MethodInit, 0) verifyOffset := md.Offset
v.LoadScriptWithHash(cs.NEF.Script, hash, callflag.ReadOnly) initOffset := -1
v.Context().NEF = &cs.NEF md = cs.Manifest.ABI.GetMethod(manifest.MethodInit, 0)
v.Context().Jump(md.Offset) if md != nil {
initOffset = md.Offset
if initMD != nil {
v.Call(initMD.Offset)
} }
v.LoadNEFMethod(&cs.NEF, util.Uint160{}, hash, callflag.ReadOnly,
true, verifyOffset, initOffset)
} }
if len(witness.InvocationScript) != 0 { if len(witness.InvocationScript) != 0 {
err := vm.IsScriptCorrect(witness.InvocationScript, nil) err := vm.IsScriptCorrect(witness.InvocationScript, nil)

View file

@ -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)) 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.Invocations[cs.Hash]++
ic.VM.LoadScriptWithCallingHash(caller, cs.NEF.Script, cs.Hash, ic.VM.Context().GetCallFlags()&f, hasReturn) ic.VM.LoadNEFMethod(&cs.NEF, caller, cs.Hash, ic.VM.Context().GetCallFlags()&f,
ic.VM.Context().NEF = &cs.NEF hasReturn, methodOff, initOff)
for e, i := ic.VM.Estack(), len(args)-1; i >= 0; i-- { for e, i := ic.VM.Estack(), len(args)-1; i >= 0; i-- {
e.PushItem(args[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 return nil
} }

View file

@ -297,12 +297,27 @@ func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) {
// each other. // each other.
func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f callflag.CallFlag) { func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f callflag.CallFlag) {
shash := v.GetCurrentScriptHash() 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. // 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) { f callflag.CallFlag, hasReturn bool) {
v.LoadScriptWithFlags(b, f) v.LoadScriptWithFlags(b, f)
ctx := v.Context() ctx := v.Context()