package smartcontract import ( "encoding/hex" "encoding/json" "strconv" "strings" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/encoding/address" "github.com/CityOfZion/neo-go/pkg/io" "github.com/pkg/errors" ) // ParamType represents the Type of the smart contract parameter. type ParamType int // A list of supported smart contract parameter types. const ( UnknownType ParamType = -1 SignatureType ParamType = 0x00 BoolType ParamType = 0x01 IntegerType ParamType = 0x02 Hash160Type ParamType = 0x03 Hash256Type ParamType = 0x04 ByteArrayType ParamType = 0x05 PublicKeyType ParamType = 0x06 StringType ParamType = 0x07 ArrayType ParamType = 0x10 MapType ParamType = 0x12 InteropInterfaceType ParamType = 0xf0 VoidType ParamType = 0xff ) // String implements the stringer interface. func (pt ParamType) String() string { switch pt { case SignatureType: return "Signature" case BoolType: return "Boolean" case IntegerType: return "Integer" case Hash160Type: return "Hash160" case Hash256Type: return "Hash256" case ByteArrayType: return "ByteArray" case PublicKeyType: return "PublicKey" case StringType: return "String" case ArrayType: return "Array" case MapType: return "Map" case InteropInterfaceType: return "InteropInterface" case VoidType: return "Void" default: return "" } } // MarshalJSON implements the json.Marshaler interface. func (pt ParamType) MarshalJSON() ([]byte, error) { return []byte(`"` + pt.String() + `"`), nil } // UnmarshalJSON implements json.Unmarshaler interface. func (pt *ParamType) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } p, err := ParseParamType(s) if err != nil { return err } *pt = p return nil } // MarshalYAML implements the YAML Marshaler interface. func (pt *ParamType) MarshalYAML() (interface{}, error) { return pt.String(), nil } // UnmarshalYAML implements the YAML Unmarshaler interface. func (pt *ParamType) UnmarshalYAML(unmarshal func(interface{}) error) error { var name string err := unmarshal(&name) if err != nil { return err } *pt, err = ParseParamType(name) return err } // EncodeBinary implements io.Serializable interface. func (pt ParamType) EncodeBinary(w *io.BinWriter) { w.WriteB(byte(pt)) } // DecodeBinary implements io.Serializable interface. func (pt *ParamType) DecodeBinary(r *io.BinReader) { *pt = ParamType(r.ReadB()) } // ParseParamType is a user-friendly string to ParamType converter, it's // case-insensitive and makes the following conversions: // signature -> SignatureType // bool, boolean -> BoolType // int, integer -> IntegerType // hash160 -> Hash160Type // hash256 -> Hash256Type // bytes, bytearray -> ByteArrayType // key, publickey -> PublicKeyType // string -> StringType // array -> ArrayType // map -> MapType // interopinterface -> InteropInterfaceType // void -> VoidType // anything else generates an error. func ParseParamType(typ string) (ParamType, error) { switch strings.ToLower(typ) { case "signature": return SignatureType, nil case "bool", "boolean": return BoolType, nil case "int", "integer": return IntegerType, nil case "hash160": return Hash160Type, nil case "hash256": return Hash256Type, nil case "bytes", "bytearray": return ByteArrayType, nil case "key", "publickey": return PublicKeyType, nil case "string": return StringType, nil case "array": return ArrayType, nil case "map": return MapType, nil case "interopinterface": return InteropInterfaceType, nil case "void": return VoidType, nil default: return UnknownType, errors.Errorf("Unknown contract parameter type: %s", typ) } } // adjustValToType is a value type-checker and converter. func adjustValToType(typ ParamType, val string) (interface{}, error) { switch typ { case SignatureType: b, err := hex.DecodeString(val) if err != nil { return nil, err } if len(b) != 64 { return nil, errors.New("not a signature") } return val, nil case BoolType: switch val { case "true": return true, nil case "false": return false, nil default: return nil, errors.New("invalid boolean value") } case IntegerType: return strconv.Atoi(val) case Hash160Type: u, err := address.StringToUint160(val) if err == nil { return hex.EncodeToString(u.BytesBE()), nil } b, err := hex.DecodeString(val) if err != nil { return nil, err } if len(b) != 20 { return nil, errors.New("not a hash160") } return val, nil case Hash256Type: b, err := hex.DecodeString(val) if err != nil { return nil, err } if len(b) != 32 { return nil, errors.New("not a hash256") } return val, nil case ByteArrayType: _, err := hex.DecodeString(val) if err != nil { return nil, err } return val, nil case PublicKeyType: _, err := keys.NewPublicKeyFromString(val) if err != nil { return nil, err } return val, nil case StringType: return val, nil default: return nil, errors.New("unsupported parameter type") } } // inferParamType tries to infer the value type from its contents. It returns // IntegerType for anything that looks like decimal integer (can be converted // with strconv.Atoi), BoolType for true and false values, Hash160Type for // addresses and hex strings encoding 20 bytes long values, PublicKeyType for // valid hex-encoded public keys, Hash256Type for hex-encoded 32 bytes values, // SignatureType for hex-encoded 64 bytes values, ByteArrayType for any other // valid hex-encoded values and StringType for anything else. func inferParamType(val string) ParamType { var err error _, err = strconv.Atoi(val) if err == nil { return IntegerType } if val == "true" || val == "false" { return BoolType } _, err = address.StringToUint160(val) if err == nil { return Hash160Type } _, err = keys.NewPublicKeyFromString(val) if err == nil { return PublicKeyType } unhexed, err := hex.DecodeString(val) if err == nil { switch len(unhexed) { case 20: return Hash160Type case 32: return Hash256Type case 64: return SignatureType default: return ByteArrayType } } // Anything can be a string. return StringType }