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