Merge pull request #3472 from nspcc-dev/pick-proper-exe-context

interop: use executing contract state for permissions checks
This commit is contained in:
Anna Shaleva 2024-06-03 16:10:39 +03:00 committed by GitHub
commit 45b8af359d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 26 additions and 16 deletions

View file

@ -1105,7 +1105,7 @@ func handleRun(c *cli.Context) error {
breaks := v.Context().BreakPoints() // We ensure that there's a context loaded.
ic.ReuseVM(v)
v.GasLimit = gasLimit
v.LoadNEFMethod(&cs.NEF, util.Uint160{}, cs.Hash, callflag.All, hasRet, offset, initOff, nil)
v.LoadNEFMethod(&cs.NEF, &cs.Manifest, util.Uint160{}, cs.Hash, callflag.All, hasRet, offset, initOff, nil)
for _, bp := range breaks {
v.AddBreakPoint(bp)
}

View file

@ -2928,7 +2928,7 @@ func (bc *Blockchain) InitVerificationContext(ic *interop.Context, hash util.Uin
initOffset = md.Offset
}
ic.Invocations[cs.Hash]++
ic.VM.LoadNEFMethod(&cs.NEF, util.Uint160{}, hash, callflag.ReadOnly,
ic.VM.LoadNEFMethod(&cs.NEF, &cs.Manifest, util.Uint160{}, hash, callflag.ReadOnly,
true, verifyOffset, initOffset, nil)
}
if len(witness.InvocationScript) != 0 {

View file

@ -144,7 +144,7 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra
}
return nil
}
ic.VM.LoadNEFMethod(&cs.NEF, caller, cs.Hash, f,
ic.VM.LoadNEFMethod(&cs.NEF, &cs.Manifest, caller, cs.Hash, f,
hasReturn, methodOff, initOff, onUnload)
for e, i := ic.VM.Estack(), len(args)-1; i >= 0; i-- {

View file

@ -80,19 +80,19 @@ func Notify(ic *interop.Context) error {
if len(name) > MaxEventNameLen {
return fmt.Errorf("event name must be less than %d", MaxEventNameLen)
}
curHash := ic.VM.GetCurrentScriptHash()
ctr, err := ic.GetContract(curHash)
if err != nil {
curr := ic.VM.Context().GetManifest()
if curr == nil {
return errors.New("notifications are not allowed in dynamic scripts")
}
var (
ev = ctr.Manifest.ABI.GetEvent(name)
ev = curr.ABI.GetEvent(name)
checkErr error
curHash = ic.VM.GetCurrentScriptHash()
)
if ev == nil {
checkErr = fmt.Errorf("notification %s does not exist", name)
} else {
err = ev.CheckCompliance(args)
err := ev.CheckCompliance(args)
if err != nil {
checkErr = fmt.Errorf("notification %s is invalid: %w", name, err)
}

View file

@ -637,7 +637,7 @@ func TestNotify(t *testing.T) {
_, _, bc, cs := getDeployedInternal(t)
ic, err := bc.GetTestVM(trigger.Application, nil, nil)
require.NoError(t, err)
ic.VM.LoadNEFMethod(&cs.NEF, caller, cs.Hash, callflag.NoneFlag, true, 0, -1, nil)
ic.VM.LoadNEFMethod(&cs.NEF, &cs.Manifest, caller, cs.Hash, callflag.NoneFlag, true, 0, -1, nil)
ic.VM.Estack().PushVal(args)
ic.VM.Estack().PushVal(name)
return ic

View file

@ -8,6 +8,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/invocations"
@ -44,6 +45,8 @@ type scriptContext struct {
// NEF represents a NEF file for the current contract.
NEF *nef.File
// Manifest represents a manifest for the current contract.
Manifest *manifest.Manifest
// invTree is an invocation tree (or a branch of it) for this context.
invTree *invocations.Tree
// onUnload is a callback that should be called after current context unloading
@ -249,6 +252,11 @@ func (c *Context) GetNEF() *nef.File {
return c.sc.NEF
}
// GetManifest returns Manifest used by this context if it's present.
func (c *Context) GetManifest() *manifest.Manifest {
return c.sc.Manifest
}
// NumOfReturnVals returns the number of return values expected from this context.
func (c *Context) NumOfReturnVals() int {
return c.retCount

View file

@ -56,6 +56,6 @@ func TestContext_BreakPoints(t *testing.T) {
require.Equal(t, []int{3, 5}, v.Context().BreakPoints())
// New context -> clean breakpoints.
v.loadScriptWithCallingHash(prog, nil, util.Uint160{}, util.Uint160{}, callflag.All, 1, 3, nil)
v.loadScriptWithCallingHash(prog, nil, nil, util.Uint160{}, util.Uint160{}, callflag.All, 1, 3, nil)
require.Equal(t, []int{}, v.Context().BreakPoints())
}

View file

@ -18,6 +18,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
@ -308,14 +309,14 @@ func (v *VM) LoadScript(b []byte) {
// LoadScriptWithFlags loads script and sets call flag to f.
func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) {
v.loadScriptWithCallingHash(b, nil, v.GetCurrentScriptHash(), util.Uint160{}, f, -1, 0, nil)
v.loadScriptWithCallingHash(b, nil, nil, v.GetCurrentScriptHash(), util.Uint160{}, f, -1, 0, nil)
}
// LoadDynamicScript loads the given script with the given flags. This script is
// considered to be dynamic, it can either return no value at all or return
// exactly one value.
func (v *VM) LoadDynamicScript(b []byte, f callflag.CallFlag) {
v.loadScriptWithCallingHash(b, nil, v.GetCurrentScriptHash(), util.Uint160{}, f, -1, 0, DynamicOnUnload)
v.loadScriptWithCallingHash(b, nil, nil, v.GetCurrentScriptHash(), util.Uint160{}, f, -1, 0, DynamicOnUnload)
}
// LoadScriptWithHash is similar to the LoadScriptWithFlags method, but it also loads
@ -325,19 +326,19 @@ func (v *VM) LoadDynamicScript(b []byte, f callflag.CallFlag) {
// accordingly). It's up to the user of this function to make sure the script and hash match
// each other.
func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f callflag.CallFlag) {
v.loadScriptWithCallingHash(b, nil, v.GetCurrentScriptHash(), hash, f, 1, 0, nil)
v.loadScriptWithCallingHash(b, nil, nil, v.GetCurrentScriptHash(), hash, f, 1, 0, nil)
}
// LoadNEFMethod allows to create a context to execute a method from the NEF
// file with the 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,
func (v *VM) LoadNEFMethod(exe *nef.File, manifest *manifest.Manifest, caller util.Uint160, hash util.Uint160, f callflag.CallFlag,
hasReturn bool, methodOff int, initOff int, onContextUnload ContextUnloadCallback) {
var rvcount int
if hasReturn {
rvcount = 1
}
v.loadScriptWithCallingHash(exe.Script, exe, caller, hash, f, rvcount, methodOff, onContextUnload)
v.loadScriptWithCallingHash(exe.Script, exe, manifest, caller, hash, f, rvcount, methodOff, onContextUnload)
if initOff >= 0 {
v.Call(initOff)
}
@ -345,7 +346,7 @@ func (v *VM) LoadNEFMethod(exe *nef.File, caller util.Uint160, hash util.Uint160
// loadScriptWithCallingHash is similar to LoadScriptWithHash but sets calling hash explicitly.
// It should be used for calling from native contracts.
func (v *VM) loadScriptWithCallingHash(b []byte, exe *nef.File, caller util.Uint160,
func (v *VM) loadScriptWithCallingHash(b []byte, exe *nef.File, manifest *manifest.Manifest, caller util.Uint160,
hash util.Uint160, f callflag.CallFlag, rvcount int, offset int, onContextUnload ContextUnloadCallback) {
v.checkInvocationStackSize()
ctx := NewContextWithParams(b, rvcount, offset)
@ -363,6 +364,7 @@ func (v *VM) loadScriptWithCallingHash(b []byte, exe *nef.File, caller util.Uint
ctx.sc.scriptHash = hash
ctx.sc.callingScriptHash = caller
ctx.sc.NEF = exe
ctx.sc.Manifest = manifest
if v.invTree != nil {
curTree := v.invTree
if parent != nil {