github.com/karalabe/go-ethereum@v0.8.5/accounts/abi/type.go (about) 1 package abi 2 3 import ( 4 "fmt" 5 "reflect" 6 "regexp" 7 "strconv" 8 9 "github.com/ethereum/go-ethereum/ethutil" 10 ) 11 12 const ( 13 IntTy byte = iota 14 UintTy 15 BoolTy 16 SliceTy 17 AddressTy 18 RealTy 19 ) 20 21 // Type is the reflection of the supported argument type 22 type Type struct { 23 Kind reflect.Kind 24 Type reflect.Type 25 Size int 26 T byte // Our own type checking 27 stringKind string // holds the unparsed string for deriving signatures 28 } 29 30 // New type returns a fully parsed Type given by the input string or an error if it can't be parsed. 31 // 32 // Strings can be in the format of: 33 // 34 // Input = Type [ "[" [ Number ] "]" ] Name . 35 // Type = [ "u" ] "int" [ Number ] . 36 // 37 // Examples: 38 // 39 // string int uint real 40 // string32 int8 uint8 uint[] 41 // address int256 uint256 real[2] 42 func NewType(t string) (typ Type, err error) { 43 // 1. full string 2. type 3. (opt.) is slice 4. (opt.) size 44 freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?") 45 if err != nil { 46 return Type{}, err 47 } 48 res := freg.FindAllStringSubmatch(t, -1)[0] 49 var ( 50 isslice bool 51 size int 52 ) 53 switch { 54 case res[3] != "": 55 // err is ignored. Already checked for number through the regexp 56 size, _ = strconv.Atoi(res[3]) 57 isslice = true 58 case res[2] != "": 59 isslice = true 60 size = -1 61 case res[0] == "": 62 return Type{}, fmt.Errorf("type parse error for `%s`", t) 63 } 64 65 treg, err := regexp.Compile("([a-zA-Z]+)([0-9]*)?") 66 if err != nil { 67 return Type{}, err 68 } 69 70 parsedType := treg.FindAllStringSubmatch(res[1], -1)[0] 71 vsize, _ := strconv.Atoi(parsedType[2]) 72 vtype := parsedType[1] 73 // substitute canonical representation 74 if vsize == 0 && (vtype == "int" || vtype == "uint") { 75 vsize = 256 76 t += "256" 77 } 78 79 if isslice { 80 typ.Kind = reflect.Slice 81 typ.Size = size 82 switch vtype { 83 case "int": 84 typ.Type = big_ts 85 case "uint": 86 typ.Type = ubig_ts 87 default: 88 return Type{}, fmt.Errorf("unsupported arg slice type: %s", t) 89 } 90 } else { 91 switch vtype { 92 case "int": 93 typ.Kind = reflect.Ptr 94 typ.Type = big_t 95 typ.Size = 256 96 typ.T = IntTy 97 case "uint": 98 typ.Kind = reflect.Ptr 99 typ.Type = ubig_t 100 typ.Size = 256 101 typ.T = UintTy 102 case "bool": 103 typ.Kind = reflect.Bool 104 case "real": // TODO 105 typ.Kind = reflect.Invalid 106 case "address": 107 typ.Kind = reflect.Slice 108 typ.Type = byte_ts 109 typ.Size = 20 110 typ.T = AddressTy 111 case "string": 112 typ.Kind = reflect.String 113 typ.Size = -1 114 if vsize > 0 { 115 typ.Size = 32 116 } 117 default: 118 return Type{}, fmt.Errorf("unsupported arg type: %s", t) 119 } 120 } 121 typ.stringKind = t 122 123 return 124 } 125 126 func (t Type) String() (out string) { 127 return t.stringKind 128 } 129 130 // Test the given input parameter `v` and checks if it matches certain 131 // criteria 132 // * Big integers are checks for ptr types and if the given value is 133 // assignable 134 // * Integer are checked for size 135 // * Strings, addresses and bytes are checks for type and size 136 func (t Type) pack(v interface{}) ([]byte, error) { 137 value := reflect.ValueOf(v) 138 switch kind := value.Kind(); kind { 139 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 140 if t.Type != ubig_t { 141 return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) 142 } 143 return packNum(value, t.T), nil 144 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 145 if t.Type != ubig_t { 146 return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) 147 } 148 return packNum(value, t.T), nil 149 case reflect.Ptr: 150 // If the value is a ptr do a assign check (only used by 151 // big.Int for now) 152 if t.Type == ubig_t && value.Type() != ubig_t { 153 return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) 154 } 155 return packNum(value, t.T), nil 156 case reflect.String: 157 if t.Size > -1 && value.Len() > t.Size { 158 return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size) 159 } 160 return []byte(ethutil.LeftPadString(t.String(), 32)), nil 161 case reflect.Slice: 162 if t.Size > -1 && value.Len() > t.Size { 163 return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size) 164 } 165 166 // Address is a special slice. The slice acts as one rather than a list of elements. 167 if t.T == AddressTy { 168 return ethutil.LeftPadBytes(v.([]byte), 32), nil 169 } 170 171 // Signed / Unsigned check 172 if (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) { 173 return nil, fmt.Errorf("slice of incompatible types.") 174 } 175 176 var packed []byte 177 for i := 0; i < value.Len(); i++ { 178 packed = append(packed, packNum(value.Index(i), t.T)...) 179 } 180 return packed, nil 181 case reflect.Bool: 182 if value.Bool() { 183 return ethutil.LeftPadBytes(ethutil.Big1.Bytes(), 32), nil 184 } else { 185 return ethutil.LeftPadBytes(ethutil.Big0.Bytes(), 32), nil 186 } 187 } 188 189 panic("unreached") 190 }