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