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