github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/smartcontract/param_type.go (about)

     1  package smartcontract
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"math/big"
     9  	"strings"
    10  	"unicode/utf8"
    11  
    12  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    13  	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
    14  	"github.com/nspcc-dev/neo-go/pkg/io"
    15  	"github.com/nspcc-dev/neo-go/pkg/util"
    16  	"github.com/nspcc-dev/neo-go/pkg/vm/emit"
    17  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    18  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    19  )
    20  
    21  // ParamType represents the Type of the smart contract parameter.
    22  type ParamType int
    23  
    24  // A list of supported smart contract parameter types.
    25  const (
    26  	UnknownType          ParamType = -1
    27  	AnyType              ParamType = 0x00
    28  	BoolType             ParamType = 0x10
    29  	IntegerType          ParamType = 0x11
    30  	ByteArrayType        ParamType = 0x12
    31  	StringType           ParamType = 0x13
    32  	Hash160Type          ParamType = 0x14
    33  	Hash256Type          ParamType = 0x15
    34  	PublicKeyType        ParamType = 0x16
    35  	SignatureType        ParamType = 0x17
    36  	ArrayType            ParamType = 0x20
    37  	MapType              ParamType = 0x22
    38  	InteropInterfaceType ParamType = 0x30
    39  	VoidType             ParamType = 0xff
    40  )
    41  
    42  // Lengths (in bytes) of fixed-size types.
    43  const (
    44  	Hash160Len   = util.Uint160Size
    45  	Hash256Len   = util.Uint256Size
    46  	PublicKeyLen = 33
    47  	SignatureLen = keys.SignatureLen
    48  )
    49  
    50  // fileBytesParamType is a string representation of `filebytes` parameter type used in cli.
    51  const fileBytesParamType string = "filebytes"
    52  
    53  // validParamTypes contains a map of known ParamTypes.
    54  var validParamTypes = map[ParamType]bool{
    55  	UnknownType:          true,
    56  	AnyType:              true,
    57  	BoolType:             true,
    58  	IntegerType:          true,
    59  	ByteArrayType:        true,
    60  	StringType:           true,
    61  	Hash160Type:          true,
    62  	Hash256Type:          true,
    63  	PublicKeyType:        true,
    64  	SignatureType:        true,
    65  	ArrayType:            true,
    66  	MapType:              true,
    67  	InteropInterfaceType: true,
    68  	VoidType:             true,
    69  }
    70  
    71  // String implements the stringer interface.
    72  func (pt ParamType) String() string {
    73  	switch pt {
    74  	case SignatureType:
    75  		return "Signature"
    76  	case BoolType:
    77  		return "Boolean"
    78  	case IntegerType:
    79  		return "Integer"
    80  	case Hash160Type:
    81  		return "Hash160"
    82  	case Hash256Type:
    83  		return "Hash256"
    84  	case ByteArrayType:
    85  		return "ByteArray"
    86  	case PublicKeyType:
    87  		return "PublicKey"
    88  	case StringType:
    89  		return "String"
    90  	case ArrayType:
    91  		return "Array"
    92  	case MapType:
    93  		return "Map"
    94  	case InteropInterfaceType:
    95  		return "InteropInterface"
    96  	case VoidType:
    97  		return "Void"
    98  	case AnyType:
    99  		return "Any"
   100  	default:
   101  		return ""
   102  	}
   103  }
   104  
   105  // MarshalJSON implements the json.Marshaler interface.
   106  func (pt ParamType) MarshalJSON() ([]byte, error) {
   107  	return []byte(`"` + pt.String() + `"`), nil
   108  }
   109  
   110  // UnmarshalJSON implements the json.Unmarshaler interface.
   111  func (pt *ParamType) UnmarshalJSON(data []byte) error {
   112  	var s string
   113  	if err := json.Unmarshal(data, &s); err != nil {
   114  		return err
   115  	}
   116  
   117  	p, err := ParseParamType(s)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	*pt = p
   123  	return nil
   124  }
   125  
   126  // MarshalYAML implements the YAML Marshaler interface.
   127  func (pt ParamType) MarshalYAML() (any, error) {
   128  	return pt.String(), nil
   129  }
   130  
   131  // UnmarshalYAML implements the YAML Unmarshaler interface.
   132  func (pt *ParamType) UnmarshalYAML(unmarshal func(any) error) error {
   133  	var name string
   134  
   135  	err := unmarshal(&name)
   136  	if err != nil {
   137  		return err
   138  	}
   139  	*pt, err = ParseParamType(name)
   140  	return err
   141  }
   142  
   143  // EncodeBinary implements the io.Serializable interface.
   144  func (pt ParamType) EncodeBinary(w *io.BinWriter) {
   145  	w.WriteB(byte(pt))
   146  }
   147  
   148  // DecodeBinary implements the io.Serializable interface.
   149  func (pt *ParamType) DecodeBinary(r *io.BinReader) {
   150  	*pt = ParamType(r.ReadB())
   151  }
   152  
   153  // EncodeDefaultValue writes a script to push the default parameter value onto
   154  // the evaluation stack into the given writer. It's mostly useful for constructing
   155  // dummy invocation scripts when parameter types are known, but they can't be
   156  // filled in. A best effort approach is used, it can't be perfect since for many
   157  // types the exact values can be arbitrarily long, but it tries to do something
   158  // reasonable in each case. For signatures, strings, arrays and "any" type a 64-byte
   159  // zero-filled value is used, hash160 and hash256 use appropriately sized values,
   160  // public key is represented by 33-byte value while 32 bytes are used for integer
   161  // and a simple push+convert is used for boolean. Other types produce no code at all.
   162  func (pt ParamType) EncodeDefaultValue(w *io.BinWriter) {
   163  	var b [SignatureLen]byte
   164  
   165  	switch pt {
   166  	case AnyType, SignatureType, StringType, ByteArrayType:
   167  		emit.Bytes(w, b[:])
   168  	case BoolType:
   169  		emit.Bool(w, true)
   170  	case IntegerType:
   171  		emit.Instruction(w, opcode.PUSHINT256, b[:32])
   172  	case Hash160Type:
   173  		emit.Bytes(w, b[:Hash160Len])
   174  	case Hash256Type:
   175  		emit.Bytes(w, b[:Hash256Len])
   176  	case PublicKeyType:
   177  		emit.Bytes(w, b[:PublicKeyLen])
   178  	case ArrayType, MapType, InteropInterfaceType, VoidType:
   179  	}
   180  }
   181  
   182  func checkBytesWithLen(vt stackitem.Type, v stackitem.Item, l int) bool {
   183  	if vt == stackitem.AnyT {
   184  		return true
   185  	}
   186  	if vt != stackitem.ByteArrayT && vt != stackitem.BufferT {
   187  		return false
   188  	}
   189  	b, _ := v.TryBytes() // Can't fail, we know the type exactly.
   190  	return len(b) == l
   191  }
   192  
   193  func (pt ParamType) Match(v stackitem.Item) bool {
   194  	vt := v.Type()
   195  
   196  	// Pointer can't be matched at all.
   197  	if vt == stackitem.PointerT {
   198  		return false
   199  	}
   200  	switch pt {
   201  	case AnyType:
   202  		return true
   203  	case BoolType:
   204  		return vt == stackitem.BooleanT
   205  	case IntegerType:
   206  		return vt == stackitem.IntegerT
   207  	case ByteArrayType:
   208  		return vt == stackitem.ByteArrayT || vt == stackitem.BufferT || vt == stackitem.AnyT
   209  	case StringType:
   210  		return (vt == stackitem.ByteArrayT || vt == stackitem.BufferT) && utf8.Valid(v.Value().([]byte))
   211  	case Hash160Type:
   212  		return checkBytesWithLen(vt, v, Hash160Len)
   213  	case Hash256Type:
   214  		return checkBytesWithLen(vt, v, Hash256Len)
   215  	case PublicKeyType:
   216  		return checkBytesWithLen(vt, v, PublicKeyLen)
   217  	case SignatureType:
   218  		return checkBytesWithLen(vt, v, SignatureLen)
   219  	case ArrayType:
   220  		return vt == stackitem.AnyT || vt == stackitem.ArrayT || vt == stackitem.StructT
   221  	case MapType:
   222  		return vt == stackitem.AnyT || vt == stackitem.MapT
   223  	case InteropInterfaceType:
   224  		return vt == stackitem.AnyT || vt == stackitem.InteropT
   225  	default:
   226  		return false
   227  	}
   228  }
   229  
   230  // ParseParamType is a user-friendly string to ParamType converter, it's
   231  // case-insensitive and makes the following conversions:
   232  //
   233  //	signature -> SignatureType
   234  //	bool, boolean -> BoolType
   235  //	int, integer -> IntegerType
   236  //	hash160 -> Hash160Type
   237  //	hash256 -> Hash256Type
   238  //	bytes, bytearray, filebytes -> ByteArrayType
   239  //	key, publickey -> PublicKeyType
   240  //	string -> StringType
   241  //	array, struct -> ArrayType
   242  //	map -> MapType
   243  //	interopinterface -> InteropInterfaceType
   244  //	void -> VoidType
   245  //
   246  // anything else generates an error.
   247  func ParseParamType(typ string) (ParamType, error) {
   248  	switch strings.ToLower(typ) {
   249  	case "signature":
   250  		return SignatureType, nil
   251  	case "bool", "boolean":
   252  		return BoolType, nil
   253  	case "int", "integer":
   254  		return IntegerType, nil
   255  	case "hash160":
   256  		return Hash160Type, nil
   257  	case "hash256":
   258  		return Hash256Type, nil
   259  	case "bytes", "bytearray", "bytestring", fileBytesParamType:
   260  		return ByteArrayType, nil
   261  	case "key", "publickey":
   262  		return PublicKeyType, nil
   263  	case "string":
   264  		return StringType, nil
   265  	case "array", "struct":
   266  		return ArrayType, nil
   267  	case "map":
   268  		return MapType, nil
   269  	case "interopinterface":
   270  		return InteropInterfaceType, nil
   271  	case "void":
   272  		return VoidType, nil
   273  	case "any":
   274  		return AnyType, nil
   275  	default:
   276  		return UnknownType, fmt.Errorf("bad parameter type: %s", typ)
   277  	}
   278  }
   279  
   280  // adjustValToType is a value type-checker and converter.
   281  func adjustValToType(typ ParamType, val string) (any, error) {
   282  	switch typ {
   283  	case SignatureType:
   284  		b, err := hex.DecodeString(val)
   285  		if err != nil {
   286  			return nil, err
   287  		}
   288  		if len(b) != SignatureLen {
   289  			return nil, errors.New("not a signature")
   290  		}
   291  		return b, nil
   292  	case BoolType:
   293  		switch val {
   294  		case "true":
   295  			return true, nil
   296  		case "false":
   297  			return false, nil
   298  		default:
   299  			return nil, errors.New("invalid boolean value")
   300  		}
   301  	case IntegerType:
   302  		bi, ok := new(big.Int).SetString(val, 10)
   303  		if !ok || stackitem.CheckIntegerSize(bi) != nil {
   304  			return nil, errors.New("invalid integer value")
   305  		}
   306  		return bi, nil
   307  	case Hash160Type:
   308  		u, err := address.StringToUint160(val)
   309  		if err == nil {
   310  			return u, nil
   311  		}
   312  		u, err = util.Uint160DecodeStringLE(val)
   313  		if err != nil {
   314  			return nil, err
   315  		}
   316  		return u, nil
   317  	case Hash256Type:
   318  		u, err := util.Uint256DecodeStringLE(val)
   319  		if err != nil {
   320  			return nil, err
   321  		}
   322  		return u, nil
   323  	case ByteArrayType:
   324  		return hex.DecodeString(val)
   325  	case PublicKeyType:
   326  		pub, err := keys.NewPublicKeyFromString(val)
   327  		if err != nil {
   328  			return nil, err
   329  		}
   330  		return pub.Bytes(), nil
   331  	case StringType:
   332  		return val, nil
   333  	case AnyType:
   334  		return nil, nil
   335  	default:
   336  		return nil, errors.New("unsupported parameter type")
   337  	}
   338  }
   339  
   340  // inferParamType tries to infer the value type from its contents. It returns
   341  // IntegerType for anything that looks like a decimal integer (can be converted
   342  // with strconv.Atoi), BoolType for true and false values, Hash160Type for
   343  // addresses and hex strings encoding 20 bytes long values, PublicKeyType for
   344  // valid hex-encoded public keys, Hash256Type for hex-encoded 32 bytes values,
   345  // SignatureType for hex-encoded 64 bytes values, ByteArrayType for any other
   346  // valid hex-encoded values and StringType for anything else.
   347  func inferParamType(val string) ParamType {
   348  	var err error
   349  
   350  	bi, ok := new(big.Int).SetString(val, 10)
   351  	if ok && stackitem.CheckIntegerSize(bi) == nil {
   352  		return IntegerType
   353  	}
   354  
   355  	if val == "nil" {
   356  		return AnyType
   357  	}
   358  
   359  	if val == "true" || val == "false" {
   360  		return BoolType
   361  	}
   362  
   363  	_, err = address.StringToUint160(val)
   364  	if err == nil {
   365  		return Hash160Type
   366  	}
   367  
   368  	_, err = keys.NewPublicKeyFromString(val)
   369  	if err == nil {
   370  		return PublicKeyType
   371  	}
   372  
   373  	unhexed, err := hex.DecodeString(val)
   374  	if err == nil {
   375  		switch len(unhexed) {
   376  		case Hash160Len:
   377  			return Hash160Type
   378  		case Hash256Len:
   379  			return Hash256Type
   380  		case SignatureLen:
   381  			return SignatureType
   382  		default:
   383  			return ByteArrayType
   384  		}
   385  	}
   386  	// Anything can be a string.
   387  	return StringType
   388  }
   389  
   390  // ConvertToParamType converts the provided value to the parameter type if it's a valid type.
   391  func ConvertToParamType(val int) (ParamType, error) {
   392  	if validParamTypes[ParamType(val)] {
   393  		return ParamType(val), nil
   394  	}
   395  	return UnknownType, errors.New("unknown parameter type")
   396  }
   397  
   398  // ConvertToStackitemType converts ParamType to corresponding Stackitem.Type.
   399  func (pt ParamType) ConvertToStackitemType() stackitem.Type {
   400  	switch pt {
   401  	case SignatureType:
   402  		return stackitem.ByteArrayT
   403  	case BoolType:
   404  		return stackitem.BooleanT
   405  	case IntegerType:
   406  		return stackitem.IntegerT
   407  	case Hash160Type:
   408  		return stackitem.ByteArrayT
   409  	case Hash256Type:
   410  		return stackitem.ByteArrayT
   411  	case ByteArrayType:
   412  		return stackitem.ByteArrayT
   413  	case PublicKeyType:
   414  		return stackitem.ByteArrayT
   415  	case StringType:
   416  		// Do not use BufferT to match System.Runtime.Notify conversion rules.
   417  		return stackitem.ByteArrayT
   418  	case ArrayType:
   419  		return stackitem.ArrayT
   420  	case MapType:
   421  		return stackitem.MapT
   422  	case InteropInterfaceType:
   423  		return stackitem.InteropT
   424  	case VoidType:
   425  		return stackitem.AnyT
   426  	case AnyType:
   427  		return stackitem.AnyT
   428  	default:
   429  		panic(fmt.Sprintf("unknown param type %d", pt))
   430  	}
   431  }