github.com/codingfuture/orig-energi3@v0.8.4/accounts/abi/bind/bind.go (about) 1 // Copyright 2018 The Energi Core Authors 2 // Copyright 2016 The go-ethereum Authors 3 // This file is part of the Energi Core library. 4 // 5 // The Energi Core library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The Energi Core library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 17 18 // Package bind generates Ethereum contract Go bindings. 19 // 20 // Detailed usage document and tutorial available on the go-ethereum Wiki page: 21 // https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts 22 package bind 23 24 import ( 25 "bytes" 26 "fmt" 27 "go/format" 28 "regexp" 29 "strings" 30 "text/template" 31 "unicode" 32 33 "github.com/ethereum/go-ethereum/accounts/abi" 34 ) 35 36 // Lang is a target programming language selector to generate bindings for. 37 type Lang int 38 39 const ( 40 LangGo Lang = iota 41 LangJava 42 LangObjC 43 ) 44 45 // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant 46 // to be used as is in client code, but rather as an intermediate struct which 47 // enforces compile time type safety and naming convention opposed to having to 48 // manually maintain hard coded strings that break on runtime. 49 func Bind(types []string, abis []string, bytecodes []string, runtimecodes []string, pkg string, lang Lang) (string, error) { 50 // Process each individual contract requested binding 51 contracts := make(map[string]*tmplContract) 52 53 for i := 0; i < len(types); i++ { 54 // Parse the actual ABI to generate the binding for 55 evmABI, err := abi.JSON(strings.NewReader(abis[i])) 56 if err != nil { 57 return "", err 58 } 59 // Strip any whitespace from the JSON ABI 60 strippedABI := strings.Map(func(r rune) rune { 61 if unicode.IsSpace(r) { 62 return -1 63 } 64 return r 65 }, abis[i]) 66 67 // Extract the call and transact methods; events; and sort them alphabetically 68 var ( 69 calls = make(map[string]*tmplMethod) 70 transacts = make(map[string]*tmplMethod) 71 events = make(map[string]*tmplEvent) 72 ) 73 for _, original := range evmABI.Methods { 74 // Normalize the method for capital cases and non-anonymous inputs/outputs 75 normalized := original 76 normalized.Name = methodNormalizer[lang](original.Name) 77 78 normalized.Inputs = make([]abi.Argument, len(original.Inputs)) 79 copy(normalized.Inputs, original.Inputs) 80 for j, input := range normalized.Inputs { 81 if input.Name == "" { 82 normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) 83 } 84 } 85 normalized.Outputs = make([]abi.Argument, len(original.Outputs)) 86 copy(normalized.Outputs, original.Outputs) 87 for j, output := range normalized.Outputs { 88 if output.Name != "" { 89 normalized.Outputs[j].Name = capitalise(output.Name) 90 } 91 } 92 // Append the methods to the call or transact lists 93 if original.Const { 94 calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} 95 } else { 96 transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} 97 } 98 } 99 for _, original := range evmABI.Events { 100 // Skip anonymous events as they don't support explicit filtering 101 if original.Anonymous { 102 continue 103 } 104 // Normalize the event for capital cases and non-anonymous outputs 105 normalized := original 106 normalized.Name = methodNormalizer[lang](original.Name) 107 108 normalized.Inputs = make([]abi.Argument, len(original.Inputs)) 109 copy(normalized.Inputs, original.Inputs) 110 for j, input := range normalized.Inputs { 111 // Indexed fields are input, non-indexed ones are outputs 112 if input.Indexed { 113 if input.Name == "" { 114 normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) 115 } 116 } 117 } 118 // Append the event to the accumulator list 119 events[original.Name] = &tmplEvent{Original: original, Normalized: normalized} 120 } 121 contracts[types[i]] = &tmplContract{ 122 Type: capitalise(types[i]), 123 InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1), 124 InputBin: strings.TrimSpace(bytecodes[i]), 125 RuntimeBin: strings.TrimSpace(runtimecodes[i]), 126 Constructor: evmABI.Constructor, 127 Calls: calls, 128 Transacts: transacts, 129 Events: events, 130 } 131 } 132 // Generate the contract template data content and render it 133 data := &tmplData{ 134 Package: pkg, 135 Contracts: contracts, 136 } 137 buffer := new(bytes.Buffer) 138 139 funcs := map[string]interface{}{ 140 "bindtype": bindType[lang], 141 "bindtopictype": bindTopicType[lang], 142 "namedtype": namedType[lang], 143 "capitalise": capitalise, 144 "decapitalise": decapitalise, 145 } 146 tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang])) 147 if err := tmpl.Execute(buffer, data); err != nil { 148 return "", err 149 } 150 // For Go bindings pass the code through gofmt to clean it up 151 if lang == LangGo { 152 code, err := format.Source(buffer.Bytes()) 153 if err != nil { 154 return "", fmt.Errorf("%v\n%s", err, buffer) 155 } 156 return string(code), nil 157 } 158 // For all others just return as is for now 159 return buffer.String(), nil 160 } 161 162 // bindType is a set of type binders that convert Solidity types to some supported 163 // programming language types. 164 var bindType = map[Lang]func(kind abi.Type) string{ 165 LangGo: bindTypeGo, 166 LangJava: bindTypeJava, 167 } 168 169 // Helper function for the binding generators. 170 // It reads the unmatched characters after the inner type-match, 171 // (since the inner type is a prefix of the total type declaration), 172 // looks for valid arrays (possibly a dynamic one) wrapping the inner type, 173 // and returns the sizes of these arrays. 174 // 175 // Returned array sizes are in the same order as solidity signatures; inner array size first. 176 // Array sizes may also be "", indicating a dynamic array. 177 func wrapArray(stringKind string, innerLen int, innerMapping string) (string, []string) { 178 remainder := stringKind[innerLen:] 179 //find all the sizes 180 matches := regexp.MustCompile(`\[(\d*)\]`).FindAllStringSubmatch(remainder, -1) 181 parts := make([]string, 0, len(matches)) 182 for _, match := range matches { 183 //get group 1 from the regex match 184 parts = append(parts, match[1]) 185 } 186 return innerMapping, parts 187 } 188 189 // Translates the array sizes to a Go-lang declaration of a (nested) array of the inner type. 190 // Simply returns the inner type if arraySizes is empty. 191 func arrayBindingGo(inner string, arraySizes []string) string { 192 out := "" 193 //prepend all array sizes, from outer (end arraySizes) to inner (start arraySizes) 194 for i := len(arraySizes) - 1; i >= 0; i-- { 195 out += "[" + arraySizes[i] + "]" 196 } 197 out += inner 198 return out 199 } 200 201 // bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping 202 // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly 203 // mapped will use an upscaled type (e.g. *big.Int). 204 func bindTypeGo(kind abi.Type) string { 205 stringKind := kind.String() 206 innerLen, innerMapping := bindUnnestedTypeGo(stringKind) 207 return arrayBindingGo(wrapArray(stringKind, innerLen, innerMapping)) 208 } 209 210 // The inner function of bindTypeGo, this finds the inner type of stringKind. 211 // (Or just the type itself if it is not an array or slice) 212 // The length of the matched part is returned, with the translated type. 213 func bindUnnestedTypeGo(stringKind string) (int, string) { 214 215 switch { 216 case strings.HasPrefix(stringKind, "address"): 217 return len("address"), "common.Address" 218 219 case strings.HasPrefix(stringKind, "bytes"): 220 parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind) 221 return len(parts[0]), fmt.Sprintf("[%s]byte", parts[1]) 222 223 case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): 224 parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind) 225 switch parts[2] { 226 case "8", "16", "32", "64": 227 return len(parts[0]), fmt.Sprintf("%sint%s", parts[1], parts[2]) 228 } 229 return len(parts[0]), "*big.Int" 230 231 case strings.HasPrefix(stringKind, "bool"): 232 return len("bool"), "bool" 233 234 case strings.HasPrefix(stringKind, "string"): 235 return len("string"), "string" 236 237 default: 238 return len(stringKind), stringKind 239 } 240 } 241 242 // Translates the array sizes to a Java declaration of a (nested) array of the inner type. 243 // Simply returns the inner type if arraySizes is empty. 244 func arrayBindingJava(inner string, arraySizes []string) string { 245 // Java array type declarations do not include the length. 246 return inner + strings.Repeat("[]", len(arraySizes)) 247 } 248 249 // bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping 250 // from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly 251 // mapped will use an upscaled type (e.g. BigDecimal). 252 func bindTypeJava(kind abi.Type) string { 253 stringKind := kind.String() 254 innerLen, innerMapping := bindUnnestedTypeJava(stringKind) 255 return arrayBindingJava(wrapArray(stringKind, innerLen, innerMapping)) 256 } 257 258 // The inner function of bindTypeJava, this finds the inner type of stringKind. 259 // (Or just the type itself if it is not an array or slice) 260 // The length of the matched part is returned, with the translated type. 261 func bindUnnestedTypeJava(stringKind string) (int, string) { 262 263 switch { 264 case strings.HasPrefix(stringKind, "address"): 265 parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind) 266 if len(parts) != 2 { 267 return len(stringKind), stringKind 268 } 269 if parts[1] == "" { 270 return len("address"), "Address" 271 } 272 return len(parts[0]), "Addresses" 273 274 case strings.HasPrefix(stringKind, "bytes"): 275 parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind) 276 if len(parts) != 2 { 277 return len(stringKind), stringKind 278 } 279 return len(parts[0]), "byte[]" 280 281 case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): 282 //Note that uint and int (without digits) are also matched, 283 // these are size 256, and will translate to BigInt (the default). 284 parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind) 285 if len(parts) != 3 { 286 return len(stringKind), stringKind 287 } 288 289 namedSize := map[string]string{ 290 "8": "byte", 291 "16": "short", 292 "32": "int", 293 "64": "long", 294 }[parts[2]] 295 296 //default to BigInt 297 if namedSize == "" { 298 namedSize = "BigInt" 299 } 300 return len(parts[0]), namedSize 301 302 case strings.HasPrefix(stringKind, "bool"): 303 return len("bool"), "boolean" 304 305 case strings.HasPrefix(stringKind, "string"): 306 return len("string"), "String" 307 308 default: 309 return len(stringKind), stringKind 310 } 311 } 312 313 // bindTopicType is a set of type binders that convert Solidity types to some 314 // supported programming language topic types. 315 var bindTopicType = map[Lang]func(kind abi.Type) string{ 316 LangGo: bindTopicTypeGo, 317 LangJava: bindTopicTypeJava, 318 } 319 320 // bindTypeGo converts a Solidity topic type to a Go one. It is almost the same 321 // funcionality as for simple types, but dynamic types get converted to hashes. 322 func bindTopicTypeGo(kind abi.Type) string { 323 bound := bindTypeGo(kind) 324 if bound == "string" || bound == "[]byte" { 325 bound = "common.Hash" 326 } 327 return bound 328 } 329 330 // bindTypeGo converts a Solidity topic type to a Java one. It is almost the same 331 // funcionality as for simple types, but dynamic types get converted to hashes. 332 func bindTopicTypeJava(kind abi.Type) string { 333 bound := bindTypeJava(kind) 334 if bound == "String" || bound == "Bytes" { 335 bound = "Hash" 336 } 337 return bound 338 } 339 340 // namedType is a set of functions that transform language specific types to 341 // named versions that my be used inside method names. 342 var namedType = map[Lang]func(string, abi.Type) string{ 343 LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, 344 LangJava: namedTypeJava, 345 } 346 347 // namedTypeJava converts some primitive data types to named variants that can 348 // be used as parts of method names. 349 func namedTypeJava(javaKind string, solKind abi.Type) string { 350 switch javaKind { 351 case "byte[]": 352 return "Binary" 353 case "byte[][]": 354 return "Binaries" 355 case "string": 356 return "String" 357 case "string[]": 358 return "Strings" 359 case "boolean": 360 return "Bool" 361 case "boolean[]": 362 return "Bools" 363 case "BigInt[]": 364 return "BigInts" 365 default: 366 parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String()) 367 if len(parts) != 4 { 368 return javaKind 369 } 370 switch parts[2] { 371 case "8", "16", "32", "64": 372 if parts[3] == "" { 373 return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2])) 374 } 375 return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2])) 376 377 default: 378 return javaKind 379 } 380 } 381 } 382 383 // methodNormalizer is a name transformer that modifies Solidity method names to 384 // conform to target language naming concentions. 385 var methodNormalizer = map[Lang]func(string) string{ 386 LangGo: abi.ToCamelCase, 387 LangJava: decapitalise, 388 } 389 390 // capitalise makes a camel-case string which starts with an upper case character. 391 func capitalise(input string) string { 392 return abi.ToCamelCase(input) 393 } 394 395 // decapitalise makes a camel-case string which starts with a lower case character. 396 func decapitalise(input string) string { 397 if len(input) == 0 { 398 return input 399 } 400 401 goForm := abi.ToCamelCase(input) 402 return strings.ToLower(goForm[:1]) + goForm[1:] 403 } 404 405 // structured checks whether a list of ABI data types has enough information to 406 // operate through a proper Go struct or if flat returns are needed. 407 func structured(args abi.Arguments) bool { 408 if len(args) < 2 { 409 return false 410 } 411 exists := make(map[string]bool) 412 for _, out := range args { 413 // If the name is anonymous, we can't organize into a struct 414 if out.Name == "" { 415 return false 416 } 417 // If the field name is empty when normalized or collides (var, Var, _var, _Var), 418 // we can't organize into a struct 419 field := capitalise(out.Name) 420 if field == "" || exists[field] { 421 return false 422 } 423 exists[field] = true 424 } 425 return true 426 }