github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/accounts/abi/bind/bind.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar 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 go-aigar 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 go-aigar 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/AigarNetwork/aigar/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts 22 package bind 23 24 import ( 25 "bytes" 26 "errors" 27 "fmt" 28 "go/format" 29 "regexp" 30 "strings" 31 "text/template" 32 "unicode" 33 34 "github.com/AigarNetwork/aigar/accounts/abi" 35 "github.com/AigarNetwork/aigar/log" 36 ) 37 38 // Lang is a target programming language selector to generate bindings for. 39 type Lang int 40 41 const ( 42 LangGo Lang = iota 43 LangJava 44 LangObjC 45 ) 46 47 // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant 48 // to be used as is in client code, but rather as an intermediate struct which 49 // enforces compile time type safety and naming convention opposed to having to 50 // manually maintain hard coded strings that break on runtime. 51 func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) { 52 // Process each individual contract requested binding 53 contracts := make(map[string]*tmplContract) 54 55 // Map used to flag each encountered library as such 56 isLib := make(map[string]struct{}) 57 58 for i := 0; i < len(types); i++ { 59 // Parse the actual ABI to generate the binding for 60 evmABI, err := abi.JSON(strings.NewReader(abis[i])) 61 if err != nil { 62 return "", err 63 } 64 // Strip any whitespace from the JSON ABI 65 strippedABI := strings.Map(func(r rune) rune { 66 if unicode.IsSpace(r) { 67 return -1 68 } 69 return r 70 }, abis[i]) 71 72 // Extract the call and transact methods; events, struct definitions; and sort them alphabetically 73 var ( 74 calls = make(map[string]*tmplMethod) 75 transacts = make(map[string]*tmplMethod) 76 events = make(map[string]*tmplEvent) 77 structs = make(map[string]*tmplStruct) 78 79 // identifiers are used to detect duplicated identifier of function 80 // and event. For all calls, transacts and events, abigen will generate 81 // corresponding bindings. However we have to ensure there is no 82 // identifier coliision in the bindings of these categories. 83 callIdentifiers = make(map[string]bool) 84 transactIdentifiers = make(map[string]bool) 85 eventIdentifiers = make(map[string]bool) 86 ) 87 for _, original := range evmABI.Methods { 88 // Normalize the method for capital cases and non-anonymous inputs/outputs 89 normalized := original 90 normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) 91 // Ensure there is no duplicated identifier 92 var identifiers = callIdentifiers 93 if !original.Const { 94 identifiers = transactIdentifiers 95 } 96 if identifiers[normalizedName] { 97 return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) 98 } 99 identifiers[normalizedName] = true 100 normalized.Name = normalizedName 101 normalized.Inputs = make([]abi.Argument, len(original.Inputs)) 102 copy(normalized.Inputs, original.Inputs) 103 for j, input := range normalized.Inputs { 104 if input.Name == "" { 105 normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) 106 } 107 if hasStruct(input.Type) { 108 bindStructType[lang](input.Type, structs) 109 } 110 } 111 normalized.Outputs = make([]abi.Argument, len(original.Outputs)) 112 copy(normalized.Outputs, original.Outputs) 113 for j, output := range normalized.Outputs { 114 if output.Name != "" { 115 normalized.Outputs[j].Name = capitalise(output.Name) 116 } 117 if hasStruct(output.Type) { 118 bindStructType[lang](output.Type, structs) 119 } 120 } 121 // Append the methods to the call or transact lists 122 if original.Const { 123 calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} 124 } else { 125 transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} 126 } 127 } 128 for _, original := range evmABI.Events { 129 // Skip anonymous events as they don't support explicit filtering 130 if original.Anonymous { 131 continue 132 } 133 // Normalize the event for capital cases and non-anonymous outputs 134 normalized := original 135 136 // Ensure there is no duplicated identifier 137 normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) 138 if eventIdentifiers[normalizedName] { 139 return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) 140 } 141 eventIdentifiers[normalizedName] = true 142 normalized.Name = normalizedName 143 144 normalized.Inputs = make([]abi.Argument, len(original.Inputs)) 145 copy(normalized.Inputs, original.Inputs) 146 for j, input := range normalized.Inputs { 147 if input.Name == "" { 148 normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) 149 } 150 if hasStruct(input.Type) { 151 bindStructType[lang](input.Type, structs) 152 } 153 } 154 // Append the event to the accumulator list 155 events[original.Name] = &tmplEvent{Original: original, Normalized: normalized} 156 } 157 158 // There is no easy way to pass arbitrary java objects to the Go side. 159 if len(structs) > 0 && lang == LangJava { 160 return "", errors.New("java binding for tuple arguments is not supported yet") 161 } 162 163 contracts[types[i]] = &tmplContract{ 164 Type: capitalise(types[i]), 165 InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1), 166 InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"), 167 Constructor: evmABI.Constructor, 168 Calls: calls, 169 Transacts: transacts, 170 Events: events, 171 Libraries: make(map[string]string), 172 Structs: structs, 173 } 174 // Function 4-byte signatures are stored in the same sequence 175 // as types, if available. 176 if len(fsigs) > i { 177 contracts[types[i]].FuncSigs = fsigs[i] 178 } 179 // Parse library references. 180 for pattern, name := range libs { 181 matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin)) 182 if err != nil { 183 log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err) 184 } 185 if matched { 186 contracts[types[i]].Libraries[pattern] = name 187 // keep track that this type is a library 188 if _, ok := isLib[name]; !ok { 189 isLib[name] = struct{}{} 190 } 191 } 192 } 193 } 194 // Check if that type has already been identified as a library 195 for i := 0; i < len(types); i++ { 196 _, ok := isLib[types[i]] 197 contracts[types[i]].Library = ok 198 } 199 // Generate the contract template data content and render it 200 data := &tmplData{ 201 Package: pkg, 202 Contracts: contracts, 203 Libraries: libs, 204 } 205 buffer := new(bytes.Buffer) 206 207 funcs := map[string]interface{}{ 208 "bindtype": bindType[lang], 209 "bindtopictype": bindTopicType[lang], 210 "namedtype": namedType[lang], 211 "formatmethod": formatMethod, 212 "formatevent": formatEvent, 213 "capitalise": capitalise, 214 "decapitalise": decapitalise, 215 } 216 tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang])) 217 if err := tmpl.Execute(buffer, data); err != nil { 218 return "", err 219 } 220 // For Go bindings pass the code through gofmt to clean it up 221 if lang == LangGo { 222 code, err := format.Source(buffer.Bytes()) 223 if err != nil { 224 return "", fmt.Errorf("%v\n%s", err, buffer) 225 } 226 return string(code), nil 227 } 228 // For all others just return as is for now 229 return buffer.String(), nil 230 } 231 232 // bindType is a set of type binders that convert Solidity types to some supported 233 // programming language types. 234 var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ 235 LangGo: bindTypeGo, 236 LangJava: bindTypeJava, 237 } 238 239 // bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go one. 240 func bindBasicTypeGo(kind abi.Type) string { 241 switch kind.T { 242 case abi.AddressTy: 243 return "common.Address" 244 case abi.IntTy, abi.UintTy: 245 parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String()) 246 switch parts[2] { 247 case "8", "16", "32", "64": 248 return fmt.Sprintf("%sint%s", parts[1], parts[2]) 249 } 250 return "*big.Int" 251 case abi.FixedBytesTy: 252 return fmt.Sprintf("[%d]byte", kind.Size) 253 case abi.BytesTy: 254 return "[]byte" 255 case abi.FunctionTy: 256 return "[24]byte" 257 default: 258 // string, bool types 259 return kind.String() 260 } 261 } 262 263 // bindTypeGo converts solidity types to Go ones. Since there is no clear mapping 264 // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly 265 // mapped will use an upscaled type (e.g. BigDecimal). 266 func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { 267 switch kind.T { 268 case abi.TupleTy: 269 return structs[kind.TupleRawName+kind.String()].Name 270 case abi.ArrayTy: 271 return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs) 272 case abi.SliceTy: 273 return "[]" + bindTypeGo(*kind.Elem, structs) 274 default: 275 return bindBasicTypeGo(kind) 276 } 277 } 278 279 // bindBasicTypeJava converts basic solidity types(except array, slice and tuple) to Java one. 280 func bindBasicTypeJava(kind abi.Type) string { 281 switch kind.T { 282 case abi.AddressTy: 283 return "Address" 284 case abi.IntTy, abi.UintTy: 285 // Note that uint and int (without digits) are also matched, 286 // these are size 256, and will translate to BigInt (the default). 287 parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String()) 288 if len(parts) != 3 { 289 return kind.String() 290 } 291 // All unsigned integers should be translated to BigInt since gomobile doesn't 292 // support them. 293 if parts[1] == "u" { 294 return "BigInt" 295 } 296 297 namedSize := map[string]string{ 298 "8": "byte", 299 "16": "short", 300 "32": "int", 301 "64": "long", 302 }[parts[2]] 303 304 // default to BigInt 305 if namedSize == "" { 306 namedSize = "BigInt" 307 } 308 return namedSize 309 case abi.FixedBytesTy, abi.BytesTy: 310 return "byte[]" 311 case abi.BoolTy: 312 return "boolean" 313 case abi.StringTy: 314 return "String" 315 case abi.FunctionTy: 316 return "byte[24]" 317 default: 318 return kind.String() 319 } 320 } 321 322 // pluralizeJavaType explicitly converts multidimensional types to predefined 323 // type in go side. 324 func pluralizeJavaType(typ string) string { 325 switch typ { 326 case "boolean": 327 return "Bools" 328 case "String": 329 return "Strings" 330 case "Address": 331 return "Addresses" 332 case "byte[]": 333 return "Binaries" 334 case "BigInt": 335 return "BigInts" 336 } 337 return typ + "[]" 338 } 339 340 // bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping 341 // from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly 342 // mapped will use an upscaled type (e.g. BigDecimal). 343 func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string { 344 switch kind.T { 345 case abi.TupleTy: 346 return structs[kind.TupleRawName+kind.String()].Name 347 case abi.ArrayTy, abi.SliceTy: 348 return pluralizeJavaType(bindTypeJava(*kind.Elem, structs)) 349 default: 350 return bindBasicTypeJava(kind) 351 } 352 } 353 354 // bindTopicType is a set of type binders that convert Solidity types to some 355 // supported programming language topic types. 356 var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ 357 LangGo: bindTopicTypeGo, 358 LangJava: bindTopicTypeJava, 359 } 360 361 // bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same 362 // funcionality as for simple types, but dynamic types get converted to hashes. 363 func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { 364 bound := bindTypeGo(kind, structs) 365 366 // todo(rjl493456442) according solidity documentation, indexed event 367 // parameters that are not value types i.e. arrays and structs are not 368 // stored directly but instead a keccak256-hash of an encoding is stored. 369 // 370 // We only convert stringS and bytes to hash, still need to deal with 371 // array(both fixed-size and dynamic-size) and struct. 372 if bound == "string" || bound == "[]byte" { 373 bound = "common.Hash" 374 } 375 return bound 376 } 377 378 // bindTopicTypeJava converts a Solidity topic type to a Java one. It is almost the same 379 // funcionality as for simple types, but dynamic types get converted to hashes. 380 func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string { 381 bound := bindTypeJava(kind, structs) 382 383 // todo(rjl493456442) according solidity documentation, indexed event 384 // parameters that are not value types i.e. arrays and structs are not 385 // stored directly but instead a keccak256-hash of an encoding is stored. 386 // 387 // We only convert stringS and bytes to hash, still need to deal with 388 // array(both fixed-size and dynamic-size) and struct. 389 if bound == "String" || bound == "byte[]" { 390 bound = "Hash" 391 } 392 return bound 393 } 394 395 // bindStructType is a set of type binders that convert Solidity tuple types to some supported 396 // programming language struct definition. 397 var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ 398 LangGo: bindStructTypeGo, 399 LangJava: bindStructTypeJava, 400 } 401 402 // bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping 403 // in the given map. 404 // Notably, this function will resolve and record nested struct recursively. 405 func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { 406 switch kind.T { 407 case abi.TupleTy: 408 // We compose raw struct name and canonical parameter expression 409 // together here. The reason is before solidity v0.5.11, kind.TupleRawName 410 // is empty, so we use canonical parameter expression to distinguish 411 // different struct definition. From the consideration of backward 412 // compatibility, we concat these two together so that if kind.TupleRawName 413 // is not empty, it can have unique id. 414 id := kind.TupleRawName + kind.String() 415 if s, exist := structs[id]; exist { 416 return s.Name 417 } 418 var fields []*tmplField 419 for i, elem := range kind.TupleElems { 420 field := bindStructTypeGo(*elem, structs) 421 fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem}) 422 } 423 name := kind.TupleRawName 424 if name == "" { 425 name = fmt.Sprintf("Struct%d", len(structs)) 426 } 427 structs[id] = &tmplStruct{ 428 Name: name, 429 Fields: fields, 430 } 431 return name 432 case abi.ArrayTy: 433 return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs) 434 case abi.SliceTy: 435 return "[]" + bindStructTypeGo(*kind.Elem, structs) 436 default: 437 return bindBasicTypeGo(kind) 438 } 439 } 440 441 // bindStructTypeJava converts a Solidity tuple type to a Java one and records the mapping 442 // in the given map. 443 // Notably, this function will resolve and record nested struct recursively. 444 func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string { 445 switch kind.T { 446 case abi.TupleTy: 447 // We compose raw struct name and canonical parameter expression 448 // together here. The reason is before solidity v0.5.11, kind.TupleRawName 449 // is empty, so we use canonical parameter expression to distinguish 450 // different struct definition. From the consideration of backward 451 // compatibility, we concat these two together so that if kind.TupleRawName 452 // is not empty, it can have unique id. 453 id := kind.TupleRawName + kind.String() 454 if s, exist := structs[id]; exist { 455 return s.Name 456 } 457 var fields []*tmplField 458 for i, elem := range kind.TupleElems { 459 field := bindStructTypeJava(*elem, structs) 460 fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem}) 461 } 462 name := kind.TupleRawName 463 if name == "" { 464 name = fmt.Sprintf("Class%d", len(structs)) 465 } 466 structs[id] = &tmplStruct{ 467 Name: name, 468 Fields: fields, 469 } 470 return name 471 case abi.ArrayTy, abi.SliceTy: 472 return pluralizeJavaType(bindStructTypeJava(*kind.Elem, structs)) 473 default: 474 return bindBasicTypeJava(kind) 475 } 476 } 477 478 // namedType is a set of functions that transform language specific types to 479 // named versions that my be used inside method names. 480 var namedType = map[Lang]func(string, abi.Type) string{ 481 LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, 482 LangJava: namedTypeJava, 483 } 484 485 // namedTypeJava converts some primitive data types to named variants that can 486 // be used as parts of method names. 487 func namedTypeJava(javaKind string, solKind abi.Type) string { 488 switch javaKind { 489 case "byte[]": 490 return "Binary" 491 case "boolean": 492 return "Bool" 493 default: 494 parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String()) 495 if len(parts) != 4 { 496 return javaKind 497 } 498 switch parts[2] { 499 case "8", "16", "32", "64": 500 if parts[3] == "" { 501 return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2])) 502 } 503 return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2])) 504 505 default: 506 return javaKind 507 } 508 } 509 } 510 511 // alias returns an alias of the given string based on the aliasing rules 512 // or returns itself if no rule is matched. 513 func alias(aliases map[string]string, n string) string { 514 if alias, exist := aliases[n]; exist { 515 return alias 516 } 517 return n 518 } 519 520 // methodNormalizer is a name transformer that modifies Solidity method names to 521 // conform to target language naming concentions. 522 var methodNormalizer = map[Lang]func(string) string{ 523 LangGo: abi.ToCamelCase, 524 LangJava: decapitalise, 525 } 526 527 // capitalise makes a camel-case string which starts with an upper case character. 528 func capitalise(input string) string { 529 return abi.ToCamelCase(input) 530 } 531 532 // decapitalise makes a camel-case string which starts with a lower case character. 533 func decapitalise(input string) string { 534 if len(input) == 0 { 535 return input 536 } 537 538 goForm := abi.ToCamelCase(input) 539 return strings.ToLower(goForm[:1]) + goForm[1:] 540 } 541 542 // structured checks whether a list of ABI data types has enough information to 543 // operate through a proper Go struct or if flat returns are needed. 544 func structured(args abi.Arguments) bool { 545 if len(args) < 2 { 546 return false 547 } 548 exists := make(map[string]bool) 549 for _, out := range args { 550 // If the name is anonymous, we can't organize into a struct 551 if out.Name == "" { 552 return false 553 } 554 // If the field name is empty when normalized or collides (var, Var, _var, _Var), 555 // we can't organize into a struct 556 field := capitalise(out.Name) 557 if field == "" || exists[field] { 558 return false 559 } 560 exists[field] = true 561 } 562 return true 563 } 564 565 // hasStruct returns an indicator whether the given type is struct, struct slice 566 // or struct array. 567 func hasStruct(t abi.Type) bool { 568 switch t.T { 569 case abi.SliceTy: 570 return hasStruct(*t.Elem) 571 case abi.ArrayTy: 572 return hasStruct(*t.Elem) 573 case abi.TupleTy: 574 return true 575 default: 576 return false 577 } 578 } 579 580 // resolveArgName converts a raw argument representation into a user friendly format. 581 func resolveArgName(arg abi.Argument, structs map[string]*tmplStruct) string { 582 var ( 583 prefix string 584 embedded string 585 typ = &arg.Type 586 ) 587 loop: 588 for { 589 switch typ.T { 590 case abi.SliceTy: 591 prefix += "[]" 592 case abi.ArrayTy: 593 prefix += fmt.Sprintf("[%d]", typ.Size) 594 default: 595 embedded = typ.TupleRawName + typ.String() 596 break loop 597 } 598 typ = typ.Elem 599 } 600 if s, exist := structs[embedded]; exist { 601 return prefix + s.Name 602 } else { 603 return arg.Type.String() 604 } 605 } 606 607 // formatMethod transforms raw method representation into a user friendly one. 608 func formatMethod(method abi.Method, structs map[string]*tmplStruct) string { 609 inputs := make([]string, len(method.Inputs)) 610 for i, input := range method.Inputs { 611 inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name) 612 } 613 outputs := make([]string, len(method.Outputs)) 614 for i, output := range method.Outputs { 615 outputs[i] = resolveArgName(output, structs) 616 if len(output.Name) > 0 { 617 outputs[i] += fmt.Sprintf(" %v", output.Name) 618 } 619 } 620 constant := "" 621 if method.Const { 622 constant = "constant " 623 } 624 return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.RawName, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", ")) 625 } 626 627 // formatEvent transforms raw event representation into a user friendly one. 628 func formatEvent(event abi.Event, structs map[string]*tmplStruct) string { 629 inputs := make([]string, len(event.Inputs)) 630 for i, input := range event.Inputs { 631 if input.Indexed { 632 inputs[i] = fmt.Sprintf("%v indexed %v", resolveArgName(input, structs), input.Name) 633 } else { 634 inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name) 635 } 636 } 637 return fmt.Sprintf("event %v(%v)", event.RawName, strings.Join(inputs, ", ")) 638 }