package runtime import ( "crypto/elliptic" "errors" "fmt" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "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/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) // CheckHashedWitness checks given hash against current list of script hashes // for verifying in the interop context. func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) { callingSH := ic.VM.GetCallingScriptHash() if !callingSH.Equals(util.Uint160{}) && hash.Equals(callingSH) { return true, nil } if tx, ok := ic.Container.(*transaction.Transaction); ok { return checkScope(ic, tx, ic.VM, hash) } return false, errors.New("script container is not a transaction") } type scopeContext struct { *vm.VM ic *interop.Context } func getContractGroups(v *vm.VM, ic *interop.Context, h util.Uint160) (manifest.Groups, error) { if !v.Context().GetCallFlags().Has(callflag.ReadStates) { return nil, errors.New("missing ReadStates call flag") } cs, err := ic.GetContract(h) if err != nil { return nil, nil // It's OK to not have the contract. } return manifest.Groups(cs.Manifest.Groups), nil } func (sc scopeContext) checkScriptGroups(h util.Uint160, k *keys.PublicKey) (bool, error) { groups, err := getContractGroups(sc.VM, sc.ic, h) if err != nil { return false, err } return groups.Contains(k), nil } func (sc scopeContext) CallingScriptHasGroup(k *keys.PublicKey) (bool, error) { return sc.checkScriptGroups(sc.GetCallingScriptHash(), k) } func (sc scopeContext) CurrentScriptHasGroup(k *keys.PublicKey) (bool, error) { return sc.checkScriptGroups(sc.GetCurrentScriptHash(), k) } func checkScope(ic *interop.Context, tx *transaction.Transaction, v *vm.VM, hash util.Uint160) (bool, error) { for _, c := range tx.Signers { if c.Account == hash { if c.Scopes == transaction.Global { return true, nil } if c.Scopes&transaction.CalledByEntry != 0 { callingScriptHash := v.GetCallingScriptHash() entryScriptHash := v.GetEntryScriptHash() if callingScriptHash.Equals(util.Uint160{}) || callingScriptHash == entryScriptHash { return true, nil } } if c.Scopes&transaction.CustomContracts != 0 { currentScriptHash := v.GetCurrentScriptHash() for _, allowedContract := range c.AllowedContracts { if allowedContract == currentScriptHash { return true, nil } } } if c.Scopes&transaction.CustomGroups != 0 { groups, err := getContractGroups(v, ic, v.GetCurrentScriptHash()) if err != nil { return false, err } // check if the current group is the required one for _, allowedGroup := range c.AllowedGroups { if groups.Contains(allowedGroup) { return true, nil } } } if c.Scopes&transaction.Rules != 0 { ctx := scopeContext{v, ic} for _, r := range c.Rules { res, err := r.Condition.Match(ctx) if err != nil { return false, err } if res { return r.Action == transaction.WitnessAllow, nil } } } return false, nil } } return false, nil } // CheckKeyedWitness checks hash of signature check contract with a given public // key against current list of script hashes for verifying in the interop context. func CheckKeyedWitness(ic *interop.Context, key *keys.PublicKey) (bool, error) { return CheckHashedWitness(ic, key.GetScriptHash()) } // CheckWitness checks witnesses. func CheckWitness(ic *interop.Context) error { var res bool var err error hashOrKey := ic.VM.Estack().Pop().Bytes() hash, err := util.Uint160DecodeBytesBE(hashOrKey) if err != nil { var key *keys.PublicKey key, err = keys.NewPublicKeyFromBytes(hashOrKey, elliptic.P256()) if err != nil { return errors.New("parameter given is neither a key nor a hash") } res, err = CheckKeyedWitness(ic, key) } else { res, err = CheckHashedWitness(ic, hash) } if err != nil { return fmt.Errorf("failed to check witness: %w", err) } ic.VM.Estack().PushItem(stackitem.Bool(res)) return nil }