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. breaks := v.Context().BreakPoints() // We ensure that there's a context loaded.
ic.ReuseVM(v) ic.ReuseVM(v)
v.GasLimit = gasLimit 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 { for _, bp := range breaks {
v.AddBreakPoint(bp) v.AddBreakPoint(bp)
} }

View file

@ -2928,7 +2928,7 @@ func (bc *Blockchain) InitVerificationContext(ic *interop.Context, hash util.Uin
initOffset = md.Offset initOffset = md.Offset
} }
ic.Invocations[cs.Hash]++ 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) true, verifyOffset, initOffset, nil)
} }
if len(witness.InvocationScript) != 0 { if len(witness.InvocationScript) != 0 {

View file

@ -144,7 +144,7 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra
} }
return nil 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) hasReturn, methodOff, initOff, onUnload)
for e, i := ic.VM.Estack(), len(args)-1; i >= 0; i-- { 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 { if len(name) > MaxEventNameLen {
return fmt.Errorf("event name must be less than %d", MaxEventNameLen) return fmt.Errorf("event name must be less than %d", MaxEventNameLen)
} }
curHash := ic.VM.GetCurrentScriptHash() curr := ic.VM.Context().GetManifest()
ctr, err := ic.GetContract(curHash) if curr == nil {
if err != nil {
return errors.New("notifications are not allowed in dynamic scripts") return errors.New("notifications are not allowed in dynamic scripts")
} }
var ( var (
ev = ctr.Manifest.ABI.GetEvent(name) ev = curr.ABI.GetEvent(name)
checkErr error checkErr error
curHash = ic.VM.GetCurrentScriptHash()
) )
if ev == nil { if ev == nil {
checkErr = fmt.Errorf("notification %s does not exist", name) checkErr = fmt.Errorf("notification %s does not exist", name)
} else { } else {
err = ev.CheckCompliance(args) err := ev.CheckCompliance(args)
if err != nil { if err != nil {
checkErr = fmt.Errorf("notification %s is invalid: %w", name, err) 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) _, _, bc, cs := getDeployedInternal(t)
ic, err := bc.GetTestVM(trigger.Application, nil, nil) ic, err := bc.GetTestVM(trigger.Application, nil, nil)
require.NoError(t, err) 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(args)
ic.VM.Estack().PushVal(name) ic.VM.Estack().PushVal(name)
return ic 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/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "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/nef"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/invocations" "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 represents a NEF file for the current contract.
NEF *nef.File 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 is an invocation tree (or a branch of it) for this context.
invTree *invocations.Tree invTree *invocations.Tree
// onUnload is a callback that should be called after current context unloading // 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 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. // NumOfReturnVals returns the number of return values expected from this context.
func (c *Context) NumOfReturnVals() int { func (c *Context) NumOfReturnVals() int {
return c.retCount return c.retCount

View file

@ -56,6 +56,6 @@ func TestContext_BreakPoints(t *testing.T) {
require.Equal(t, []int{3, 5}, v.Context().BreakPoints()) require.Equal(t, []int{3, 5}, v.Context().BreakPoints())
// New context -> clean 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()) 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/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "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/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/nef"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "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. // LoadScriptWithFlags loads script and sets call flag to f.
func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) { 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 // 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 // considered to be dynamic, it can either return no value at all or return
// exactly one value. // exactly one value.
func (v *VM) LoadDynamicScript(b []byte, f callflag.CallFlag) { 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 // 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 // accordingly). It's up to the user of this function to make sure the script and hash match
// 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) {
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 // 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, // file with the specified caller and executing hash, call flags, return value,
// method and _initialize offsets. // 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) { hasReturn bool, methodOff int, initOff int, onContextUnload ContextUnloadCallback) {
var rvcount int var rvcount int
if hasReturn { if hasReturn {
rvcount = 1 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 { if initOff >= 0 {
v.Call(initOff) 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. // 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(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) { hash util.Uint160, f callflag.CallFlag, rvcount int, offset int, onContextUnload ContextUnloadCallback) {
v.checkInvocationStackSize() v.checkInvocationStackSize()
ctx := NewContextWithParams(b, rvcount, offset) 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.scriptHash = hash
ctx.sc.callingScriptHash = caller ctx.sc.callingScriptHash = caller
ctx.sc.NEF = exe ctx.sc.NEF = exe
ctx.sc.Manifest = manifest
if v.invTree != nil { if v.invTree != nil {
curTree := v.invTree curTree := v.invTree
if parent != nil { if parent != nil {