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

     1  package transaction
     2  
     3  //go:generate stringer -type=WitnessScope -linecomment -output=witness_scope_string.go
     4  import (
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"strings"
     9  )
    10  
    11  // WitnessScope represents set of witness flags for Transaction signer.
    12  type WitnessScope byte
    13  
    14  const (
    15  	// None specifies that no contract was witnessed. Only sign the transaction.
    16  	None WitnessScope = 0
    17  	// CalledByEntry witness is only valid in entry script and ones directly called by it.
    18  	// No params is needed, as the witness/permission/signature given on first invocation will
    19  	// automatically expire if entering deeper internal invokes. This can be default safe
    20  	// choice for native NEO/GAS (previously used on Neo 2 as "attach" mode).
    21  	CalledByEntry WitnessScope = 0x01
    22  	// CustomContracts define custom hash for contract-specific.
    23  	CustomContracts WitnessScope = 0x10
    24  	// CustomGroups define custom pubkey for group members.
    25  	CustomGroups WitnessScope = 0x20
    26  	// Rules is a set of conditions with boolean operators.
    27  	Rules WitnessScope = 0x40 // WitnessRules
    28  	// Global allows this witness in all contexts (default Neo2 behavior).
    29  	// This cannot be combined with other flags.
    30  	Global WitnessScope = 0x80
    31  )
    32  
    33  // ScopesFromByte converts byte to a set of WitnessScopes and performs validity
    34  // check.
    35  func ScopesFromByte(b byte) (WitnessScope, error) {
    36  	var res = WitnessScope(b)
    37  	if (res&Global != 0) && (res&(None|CalledByEntry|CustomContracts|CustomGroups|Rules) != 0) {
    38  		return 0, errors.New("Global scope can not be combined with other scopes")
    39  	}
    40  	if res&^(None|CalledByEntry|CustomContracts|CustomGroups|Rules|Global) != 0 {
    41  		return 0, fmt.Errorf("invalid scope %d", res)
    42  	}
    43  	return res, nil
    44  }
    45  
    46  // ScopesFromString converts string of comma-separated scopes to a set of scopes
    47  // (case-sensitive). String can combine several scopes, e.g. be any of: 'Global',
    48  // 'CalledByEntry,CustomGroups' etc. In case of an empty string an error will be
    49  // returned.
    50  func ScopesFromString(s string) (WitnessScope, error) {
    51  	var result WitnessScope
    52  	scopes := strings.Split(s, ",")
    53  	for i, scope := range scopes {
    54  		scopes[i] = strings.TrimSpace(scope)
    55  	}
    56  	dict := map[string]WitnessScope{
    57  		Global.String():          Global,
    58  		CalledByEntry.String():   CalledByEntry,
    59  		CustomContracts.String(): CustomContracts,
    60  		CustomGroups.String():    CustomGroups,
    61  		Rules.String():           Rules,
    62  		None.String():            None,
    63  	}
    64  	var isGlobal bool
    65  	for _, scopeStr := range scopes {
    66  		scope, ok := dict[scopeStr]
    67  		if !ok {
    68  			return result, fmt.Errorf("invalid witness scope: %v", scopeStr)
    69  		}
    70  		if isGlobal && !(scope == Global) {
    71  			return result, errors.New("Global scope can not be combined with other scopes")
    72  		}
    73  		result |= scope
    74  		if scope == Global {
    75  			isGlobal = true
    76  		}
    77  	}
    78  	return result, nil
    79  }
    80  
    81  func appendScopeString(str string, scopes WitnessScope, scope WitnessScope) string {
    82  	if scopes&scope != 0 {
    83  		if len(str) != 0 {
    84  			str += ", "
    85  		}
    86  		str += scope.String()
    87  	}
    88  	return str
    89  }
    90  
    91  // scopesToString converts witness scope to it's string representation. It uses
    92  // `, ` to separate scope names.
    93  func scopesToString(scopes WitnessScope) string {
    94  	if scopes&Global != 0 || scopes == None {
    95  		return scopes.String()
    96  	}
    97  	var res string
    98  	res = appendScopeString(res, scopes, CalledByEntry)
    99  	res = appendScopeString(res, scopes, CustomContracts)
   100  	res = appendScopeString(res, scopes, CustomGroups)
   101  	res = appendScopeString(res, scopes, Rules)
   102  	return res
   103  }
   104  
   105  // MarshalJSON implements the json.Marshaler interface.
   106  func (s WitnessScope) MarshalJSON() ([]byte, error) {
   107  	return []byte(`"` + scopesToString(s) + `"`), nil
   108  }
   109  
   110  // UnmarshalJSON implements the json.Unmarshaler interface.
   111  func (s *WitnessScope) UnmarshalJSON(data []byte) error {
   112  	var js string
   113  	if err := json.Unmarshal(data, &js); err != nil {
   114  		return err
   115  	}
   116  	scopes, err := ScopesFromString(js)
   117  	if err != nil {
   118  		return err
   119  	}
   120  	*s = scopes
   121  	return nil
   122  }