interop/vm: use more robust CalledByEntry check

Directly check contexts.
This commit is contained in:
Roman Khimov 2022-08-04 16:35:02 +03:00
parent 13f5fdbe8a
commit 99e2681d3a
6 changed files with 24 additions and 8 deletions

View file

@ -41,6 +41,10 @@ func getContractGroups(v *vm.VM, ic *interop.Context, h util.Uint160) (manifest.
return manifest.Groups(cs.Manifest.Groups), nil return manifest.Groups(cs.Manifest.Groups), nil
} }
func (sc scopeContext) IsCalledByEntry() bool {
return sc.VM.Context().IsCalledByEntry()
}
func (sc scopeContext) checkScriptGroups(h util.Uint160, k *keys.PublicKey) (bool, error) { func (sc scopeContext) checkScriptGroups(h util.Uint160, k *keys.PublicKey) (bool, error) {
groups, err := getContractGroups(sc.VM, sc.ic, h) groups, err := getContractGroups(sc.VM, sc.ic, h)
if err != nil { if err != nil {
@ -69,9 +73,7 @@ func checkScope(ic *interop.Context, hash util.Uint160) (bool, error) {
return true, nil return true, nil
} }
if c.Scopes&transaction.CalledByEntry != 0 { if c.Scopes&transaction.CalledByEntry != 0 {
callingScriptHash := ic.VM.GetCallingScriptHash() if ic.VM.Context().IsCalledByEntry() {
entryScriptHash := ic.VM.GetEntryScriptHash()
if callingScriptHash.Equals(util.Uint160{}) || callingScriptHash == entryScriptHash {
return true, nil return true, nil
} }
} }

View file

@ -63,9 +63,9 @@ type WitnessCondition interface {
type MatchContext interface { type MatchContext interface {
GetCallingScriptHash() util.Uint160 GetCallingScriptHash() util.Uint160
GetCurrentScriptHash() util.Uint160 GetCurrentScriptHash() util.Uint160
GetEntryScriptHash() util.Uint160
CallingScriptHasGroup(*keys.PublicKey) (bool, error) CallingScriptHasGroup(*keys.PublicKey) (bool, error)
CurrentScriptHasGroup(*keys.PublicKey) (bool, error) CurrentScriptHasGroup(*keys.PublicKey) (bool, error)
IsCalledByEntry() bool
} }
type ( type (
@ -394,8 +394,7 @@ func (c ConditionCalledByEntry) Type() WitnessConditionType {
// Match implements the WitnessCondition interface checking whether this condition // Match implements the WitnessCondition interface checking whether this condition
// matches given context. // matches given context.
func (c ConditionCalledByEntry) Match(ctx MatchContext) (bool, error) { func (c ConditionCalledByEntry) Match(ctx MatchContext) (bool, error) {
entry := ctx.GetEntryScriptHash() return ctx.IsCalledByEntry(), nil
return entry.Equals(ctx.GetCallingScriptHash()) || entry.Equals(ctx.GetCurrentScriptHash()), nil
} }
// EncodeBinary implements the WitnessCondition interface allowing to serialize condition. // EncodeBinary implements the WitnessCondition interface allowing to serialize condition.

View file

@ -170,6 +170,9 @@ func (t *TestMC) GetCurrentScriptHash() util.Uint160 {
func (t *TestMC) GetEntryScriptHash() util.Uint160 { func (t *TestMC) GetEntryScriptHash() util.Uint160 {
return t.entry return t.entry
} }
func (t *TestMC) IsCalledByEntry() bool {
return t.entry.Equals(t.calling) || t.calling.Equals(util.Uint160{})
}
func (t *TestMC) CallingScriptHasGroup(k *keys.PublicKey) (bool, error) { func (t *TestMC) CallingScriptHasGroup(k *keys.PublicKey) (bool, error) {
res, err := t.CurrentScriptHasGroup(k) res, err := t.CurrentScriptHasGroup(k)
return !res, err // To differentiate from current we invert the logic value. return !res, err // To differentiate from current we invert the logic value.

View file

@ -13,7 +13,7 @@ type WitnessScope byte
const ( const (
// None specifies that no contract was witnessed. Only sign the transaction. // None specifies that no contract was witnessed. Only sign the transaction.
None WitnessScope = 0 None WitnessScope = 0
// CalledByEntry means that this condition must hold: EntryScriptHash == CallingScriptHash. // CalledByEntry witness is only valid in entry script and ones directly called by it.
// No params is needed, as the witness/permission/signature given on first invocation will // No params is needed, as the witness/permission/signature given on first invocation will
// automatically expire if entering deeper internal invokes. This can be default safe // automatically expire if entering deeper internal invokes. This can be default safe
// choice for native NEO/GAS (previously used on Neo 2 as "attach" mode). // choice for native NEO/GAS (previously used on Neo 2 as "attach" mode).

View file

@ -37,6 +37,9 @@ type scriptContext struct {
// Caller's contract script hash. // Caller's contract script hash.
callingScriptHash util.Uint160 callingScriptHash util.Uint160
// Caller's scriptContext, if not entry.
callingContext *scriptContext
// Call flags this context was created with. // Call flags this context was created with.
callFlag callflag.CallFlag callFlag callflag.CallFlag
@ -326,6 +329,12 @@ func (v *VM) getContextScriptHash(n int) util.Uint160 {
return ctx.ScriptHash() return ctx.ScriptHash()
} }
// IsCalledByEntry checks parent script contexts and return true if the current one
// is an entry script (the first loaded into the VM) or one called by it.
func (c *Context) IsCalledByEntry() bool {
return c.sc.callingContext == nil || c.sc.callingContext.callingContext == nil
}
// PushContextScriptHash pushes the script hash of the // PushContextScriptHash pushes the script hash of the
// invocation stack element number n to the evaluation stack. // invocation stack element number n to the evaluation stack.
func (v *VM) PushContextScriptHash(n int) error { func (v *VM) PushContextScriptHash(n int) error {

View file

@ -340,15 +340,18 @@ func (v *VM) loadScriptWithCallingHash(b []byte, exe *nef.File, caller util.Uint
if rvcount != -1 || v.estack.Len() != 0 { if rvcount != -1 || v.estack.Len() != 0 {
v.estack = subStack(v.estack) v.estack = subStack(v.estack)
} }
parent := v.Context()
ctx.sc.estack = v.estack ctx.sc.estack = v.estack
initStack(&ctx.tryStack, "exception", nil) initStack(&ctx.tryStack, "exception", nil)
ctx.sc.callFlag = f ctx.sc.callFlag = f
ctx.sc.scriptHash = hash ctx.sc.scriptHash = hash
ctx.sc.callingScriptHash = caller ctx.sc.callingScriptHash = caller
if parent != nil {
ctx.sc.callingContext = parent.sc
}
ctx.sc.NEF = exe ctx.sc.NEF = exe
if v.invTree != nil { if v.invTree != nil {
curTree := v.invTree curTree := v.invTree
parent := v.Context()
if parent != nil { if parent != nil {
curTree = parent.sc.invTree curTree = parent.sc.invTree
} }