bind.go 17 KB

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