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