github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/interop/runtime/witness.go (about)

     1  package runtime
     2  
     3  import (
     4  	"crypto/elliptic"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/nspcc-dev/neo-go/pkg/core/interop"
     9  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    10  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    11  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
    12  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
    13  	"github.com/nspcc-dev/neo-go/pkg/util"
    14  	"github.com/nspcc-dev/neo-go/pkg/vm"
    15  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    16  )
    17  
    18  // CheckHashedWitness checks the given hash against the current list of script hashes
    19  // for verifying in the interop context.
    20  func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) {
    21  	callingSH := ic.VM.GetCallingScriptHash()
    22  	if !callingSH.Equals(util.Uint160{}) && hash.Equals(callingSH) {
    23  		return true, nil
    24  	}
    25  	return checkScope(ic, hash)
    26  }
    27  
    28  type scopeContext struct {
    29  	*vm.VM
    30  	ic *interop.Context
    31  }
    32  
    33  func getContractGroups(v *vm.VM, ic *interop.Context, h util.Uint160) (manifest.Groups, error) {
    34  	if !v.Context().GetCallFlags().Has(callflag.ReadStates) {
    35  		return nil, errors.New("missing ReadStates call flag")
    36  	}
    37  	cs, err := ic.GetContract(h)
    38  	if err != nil {
    39  		return nil, nil // It's OK to not have the contract.
    40  	}
    41  	return manifest.Groups(cs.Manifest.Groups), nil
    42  }
    43  
    44  func (sc scopeContext) IsCalledByEntry() bool {
    45  	return sc.VM.Context().IsCalledByEntry()
    46  }
    47  
    48  func (sc scopeContext) checkScriptGroups(h util.Uint160, k *keys.PublicKey) (bool, error) {
    49  	groups, err := getContractGroups(sc.VM, sc.ic, h)
    50  	if err != nil {
    51  		return false, err
    52  	}
    53  	return groups.Contains(k), nil
    54  }
    55  
    56  func (sc scopeContext) CallingScriptHasGroup(k *keys.PublicKey) (bool, error) {
    57  	return sc.checkScriptGroups(sc.GetCallingScriptHash(), k)
    58  }
    59  
    60  func (sc scopeContext) CurrentScriptHasGroup(k *keys.PublicKey) (bool, error) {
    61  	return sc.checkScriptGroups(sc.GetCurrentScriptHash(), k)
    62  }
    63  
    64  func checkScope(ic *interop.Context, hash util.Uint160) (bool, error) {
    65  	signers := ic.Signers()
    66  	if len(signers) == 0 {
    67  		return false, errors.New("no valid signers")
    68  	}
    69  	for i := range signers {
    70  		c := &signers[i]
    71  		if c.Account == hash {
    72  			if c.Scopes == transaction.Global {
    73  				return true, nil
    74  			}
    75  			if c.Scopes&transaction.CalledByEntry != 0 {
    76  				if ic.VM.Context().IsCalledByEntry() {
    77  					return true, nil
    78  				}
    79  			}
    80  			if c.Scopes&transaction.CustomContracts != 0 {
    81  				currentScriptHash := ic.VM.GetCurrentScriptHash()
    82  				for _, allowedContract := range c.AllowedContracts {
    83  					if allowedContract == currentScriptHash {
    84  						return true, nil
    85  					}
    86  				}
    87  			}
    88  			if c.Scopes&transaction.CustomGroups != 0 {
    89  				groups, err := getContractGroups(ic.VM, ic, ic.VM.GetCurrentScriptHash())
    90  				if err != nil {
    91  					return false, err
    92  				}
    93  				// check if the current group is the required one
    94  				for _, allowedGroup := range c.AllowedGroups {
    95  					if groups.Contains(allowedGroup) {
    96  						return true, nil
    97  					}
    98  				}
    99  			}
   100  			if c.Scopes&transaction.Rules != 0 {
   101  				ctx := scopeContext{ic.VM, ic}
   102  				for _, r := range c.Rules {
   103  					res, err := r.Condition.Match(ctx)
   104  					if err != nil {
   105  						return false, err
   106  					}
   107  					if res {
   108  						return r.Action == transaction.WitnessAllow, nil
   109  					}
   110  				}
   111  			}
   112  			return false, nil
   113  		}
   114  	}
   115  	return false, nil
   116  }
   117  
   118  // CheckKeyedWitness checks the hash of the signature check contract with the given public
   119  // key against the current list of script hashes for verifying in the interop context.
   120  func CheckKeyedWitness(ic *interop.Context, key *keys.PublicKey) (bool, error) {
   121  	return CheckHashedWitness(ic, key.GetScriptHash())
   122  }
   123  
   124  // CheckWitness checks witnesses.
   125  func CheckWitness(ic *interop.Context) error {
   126  	var res bool
   127  	var err error
   128  
   129  	hashOrKey := ic.VM.Estack().Pop().Bytes()
   130  	hash, err := util.Uint160DecodeBytesBE(hashOrKey)
   131  	if err != nil {
   132  		var key *keys.PublicKey
   133  		key, err = keys.NewPublicKeyFromBytes(hashOrKey, elliptic.P256())
   134  		if err != nil {
   135  			return errors.New("parameter given is neither a key nor a hash")
   136  		}
   137  		res, err = CheckKeyedWitness(ic, key)
   138  	} else {
   139  		res, err = CheckHashedWitness(ic, hash)
   140  	}
   141  	if err != nil {
   142  		return fmt.Errorf("failed to check witness: %w", err)
   143  	}
   144  	ic.VM.Estack().PushItem(stackitem.Bool(res))
   145  	return nil
   146  }