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 }