f96d9d168e
Oracle responses must use the same set of signers as oracle requests even though the transaction itself is signed by oracle nodes/contract. We can probably improve interop.Context by removing Tx field completely and adding more functionality to Container, but it's not very convenient for VerifyWitness and will require adding more stub-like methods for Block, so Tx is used for now (and we do have it in every relevant case).
144 lines
4.1 KiB
Go
144 lines
4.1 KiB
Go
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
|
|
}
|
|
return checkScope(ic, hash)
|
|
}
|
|
|
|
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, hash util.Uint160) (bool, error) {
|
|
signers := ic.Signers()
|
|
if len(signers) == 0 {
|
|
return false, errors.New("no valid signers")
|
|
}
|
|
for i := range signers {
|
|
c := &signers[i]
|
|
if c.Account == hash {
|
|
if c.Scopes == transaction.Global {
|
|
return true, nil
|
|
}
|
|
if c.Scopes&transaction.CalledByEntry != 0 {
|
|
callingScriptHash := ic.VM.GetCallingScriptHash()
|
|
entryScriptHash := ic.VM.GetEntryScriptHash()
|
|
if callingScriptHash.Equals(util.Uint160{}) || callingScriptHash == entryScriptHash {
|
|
return true, nil
|
|
}
|
|
}
|
|
if c.Scopes&transaction.CustomContracts != 0 {
|
|
currentScriptHash := ic.VM.GetCurrentScriptHash()
|
|
for _, allowedContract := range c.AllowedContracts {
|
|
if allowedContract == currentScriptHash {
|
|
return true, nil
|
|
}
|
|
}
|
|
}
|
|
if c.Scopes&transaction.CustomGroups != 0 {
|
|
groups, err := getContractGroups(ic.VM, ic, ic.VM.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{ic.VM, 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
|
|
}
|