bind.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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. "fmt"
  24. "go/format"
  25. "regexp"
  26. "strings"
  27. "text/template"
  28. "unicode"
  29. "github.com/ethereum/go-ethereum/accounts/abi"
  30. )
  31. // Lang is a target programming language selector to generate bindings for.
  32. type Lang int
  33. const (
  34. LangGo Lang = iota
  35. LangJava
  36. LangObjC
  37. )
  38. // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
  39. // to be used as is in client code, but rather as an intermediate struct which
  40. // enforces compile time type safety and naming convention opposed to having to
  41. // manually maintain hard coded strings that break on runtime.
  42. func Bind(types []string, abis []string, bytecodes []string, pkg string, lang Lang) (string, error) {
  43. // Process each individual contract requested binding
  44. contracts := make(map[string]*tmplContract)
  45. for i := 0; i < len(types); i++ {
  46. // Parse the actual ABI to generate the binding for
  47. evmABI, err := abi.JSON(strings.NewReader(abis[i]))
  48. if err != nil {
  49. return "", err
  50. }
  51. // Strip any whitespace from the JSON ABI
  52. strippedABI := strings.Map(func(r rune) rune {
  53. if unicode.IsSpace(r) {
  54. return -1
  55. }
  56. return r
  57. }, abis[i])
  58. // Extract the call and transact methods; events; and sort them alphabetically
  59. var (
  60. calls = make(map[string]*tmplMethod)
  61. transacts = make(map[string]*tmplMethod)
  62. events = make(map[string]*tmplEvent)
  63. )
  64. for _, original := range evmABI.Methods {
  65. // Normalize the method for capital cases and non-anonymous inputs/outputs
  66. normalized := original
  67. normalized.Name = methodNormalizer[lang](original.Name)
  68. normalized.Inputs = make([]abi.Argument, len(original.Inputs))
  69. copy(normalized.Inputs, original.Inputs)
  70. for j, input := range normalized.Inputs {
  71. if input.Name == "" {
  72. normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
  73. }
  74. }
  75. normalized.Outputs = make([]abi.Argument, len(original.Outputs))
  76. copy(normalized.Outputs, original.Outputs)
  77. for j, output := range normalized.Outputs {
  78. if output.Name != "" {
  79. normalized.Outputs[j].Name = capitalise(output.Name)
  80. }
  81. }
  82. // Append the methods to the call or transact lists
  83. if original.Const {
  84. calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
  85. } else {
  86. transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
  87. }
  88. }
  89. for _, original := range evmABI.Events {
  90. // Skip anonymous events as they don't support explicit filtering
  91. if original.Anonymous {
  92. continue
  93. }
  94. // Normalize the event for capital cases and non-anonymous outputs
  95. normalized := original
  96. normalized.Name = methodNormalizer[lang](original.Name)
  97. normalized.Inputs = make([]abi.Argument, len(original.Inputs))
  98. copy(normalized.Inputs, original.Inputs)
  99. for j, input := range normalized.Inputs {
  100. // Indexed fields are input, non-indexed ones are outputs
  101. if input.Indexed {
  102. if input.Name == "" {
  103. normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
  104. }
  105. }
  106. }
  107. // Append the event to the accumulator list
  108. events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
  109. }
  110. contracts[types[i]] = &tmplContract{
  111. Type: capitalise(types[i]),
  112. InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
  113. InputBin: strings.TrimSpace(bytecodes[i]),
  114. Constructor: evmABI.Constructor,
  115. Calls: calls,
  116. Transacts: transacts,
  117. Events: events,
  118. }
  119. }
  120. // Generate the contract template data content and render it
  121. data := &tmplData{
  122. Package: pkg,
  123. Contracts: contracts,
  124. }
  125. buffer := new(bytes.Buffer)
  126. funcs := map[string]interface{}{
  127. "bindtype": bindType[lang],
  128. "bindtopictype": bindTopicType[lang],
  129. "namedtype": namedType[lang],
  130. "capitalise": capitalise,
  131. "decapitalise": decapitalise,
  132. }
  133. tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
  134. if err := tmpl.Execute(buffer, data); err != nil {
  135. return "", err
  136. }
  137. // For Go bindings pass the code through gofmt to clean it up
  138. if lang == LangGo {
  139. code, err := format.Source(buffer.Bytes())
  140. if err != nil {
  141. return "", fmt.Errorf("%v\n%s", err, buffer)
  142. }
  143. return string(code), nil
  144. }
  145. // For all others just return as is for now
  146. return buffer.String(), nil
  147. }
  148. // bindType is a set of type binders that convert Solidity types to some supported
  149. // programming language types.
  150. var bindType = map[Lang]func(kind abi.Type) string{
  151. LangGo: bindTypeGo,
  152. LangJava: bindTypeJava,
  153. }
  154. // Helper function for the binding generators.
  155. // It reads the unmatched characters after the inner type-match,
  156. // (since the inner type is a prefix of the total type declaration),
  157. // looks for valid arrays (possibly a dynamic one) wrapping the inner type,
  158. // and returns the sizes of these arrays.
  159. //
  160. // Returned array sizes are in the same order as solidity signatures; inner array size first.
  161. // Array sizes may also be "", indicating a dynamic array.
  162. func wrapArray(stringKind string, innerLen int, innerMapping string) (string, []string) {
  163. remainder := stringKind[innerLen:]
  164. //find all the sizes
  165. matches := regexp.MustCompile(`\[(\d*)\]`).FindAllStringSubmatch(remainder, -1)
  166. parts := make([]string, 0, len(matches))
  167. for _, match := range matches {
  168. //get group 1 from the regex match
  169. parts = append(parts, match[1])
  170. }
  171. return innerMapping, parts
  172. }
  173. // Translates the array sizes to a Go-lang declaration of a (nested) array of the inner type.
  174. // Simply returns the inner type if arraySizes is empty.
  175. func arrayBindingGo(inner string, arraySizes []string) string {
  176. out := ""
  177. //prepend all array sizes, from outer (end arraySizes) to inner (start arraySizes)
  178. for i := len(arraySizes) - 1; i >= 0; i-- {
  179. out += "[" + arraySizes[i] + "]"
  180. }
  181. out += inner
  182. return out
  183. }
  184. // bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping
  185. // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
  186. // mapped will use an upscaled type (e.g. *big.Int).
  187. func bindTypeGo(kind abi.Type) string {
  188. stringKind := kind.String()
  189. innerLen, innerMapping := bindUnnestedTypeGo(stringKind)
  190. return arrayBindingGo(wrapArray(stringKind, innerLen, innerMapping))
  191. }
  192. // The inner function of bindTypeGo, this finds the inner type of stringKind.
  193. // (Or just the type itself if it is not an array or slice)
  194. // The length of the matched part is returned, with the translated type.
  195. func bindUnnestedTypeGo(stringKind string) (int, string) {
  196. switch {
  197. case strings.HasPrefix(stringKind, "address"):
  198. return len("address"), "common.Address"
  199. case strings.HasPrefix(stringKind, "bytes"):
  200. parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
  201. return len(parts[0]), fmt.Sprintf("[%s]byte", parts[1])
  202. case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
  203. parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
  204. switch parts[2] {
  205. case "8", "16", "32", "64":
  206. return len(parts[0]), fmt.Sprintf("%sint%s", parts[1], parts[2])
  207. }
  208. return len(parts[0]), "*big.Int"
  209. case strings.HasPrefix(stringKind, "bool"):
  210. return len("bool"), "bool"
  211. case strings.HasPrefix(stringKind, "string"):
  212. return len("string"), "string"
  213. default:
  214. return len(stringKind), stringKind
  215. }
  216. }
  217. // Translates the array sizes to a Java declaration of a (nested) array of the inner type.
  218. // Simply returns the inner type if arraySizes is empty.
  219. func arrayBindingJava(inner string, arraySizes []string) string {
  220. // Java array type declarations do not include the length.
  221. return inner + strings.Repeat("[]", len(arraySizes))
  222. }
  223. // bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
  224. // from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
  225. // mapped will use an upscaled type (e.g. BigDecimal).
  226. func bindTypeJava(kind abi.Type) string {
  227. stringKind := kind.String()
  228. innerLen, innerMapping := bindUnnestedTypeJava(stringKind)
  229. return arrayBindingJava(wrapArray(stringKind, innerLen, innerMapping))
  230. }
  231. // The inner function of bindTypeJava, this finds the inner type of stringKind.
  232. // (Or just the type itself if it is not an array or slice)
  233. // The length of the matched part is returned, with the translated type.
  234. func bindUnnestedTypeJava(stringKind string) (int, string) {
  235. switch {
  236. case strings.HasPrefix(stringKind, "address"):
  237. parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
  238. if len(parts) != 2 {
  239. return len(stringKind), stringKind
  240. }
  241. if parts[1] == "" {
  242. return len("address"), "Address"
  243. }
  244. return len(parts[0]), "Addresses"
  245. case strings.HasPrefix(stringKind, "bytes"):
  246. parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
  247. if len(parts) != 2 {
  248. return len(stringKind), stringKind
  249. }
  250. return len(parts[0]), "byte[]"
  251. case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
  252. //Note that uint and int (without digits) are also matched,
  253. // these are size 256, and will translate to BigInt (the default).
  254. parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
  255. if len(parts) != 3 {
  256. return len(stringKind), stringKind
  257. }
  258. namedSize := map[string]string{
  259. "8": "byte",
  260. "16": "short",
  261. "32": "int",
  262. "64": "long",
  263. }[parts[2]]
  264. //default to BigInt
  265. if namedSize == "" {
  266. namedSize = "BigInt"
  267. }
  268. return len(parts[0]), namedSize
  269. case strings.HasPrefix(stringKind, "bool"):
  270. return len("bool"), "boolean"
  271. case strings.HasPrefix(stringKind, "string"):
  272. return len("string"), "String"
  273. default:
  274. return len(stringKind), stringKind
  275. }
  276. }
  277. // bindTopicType is a set of type binders that convert Solidity types to some
  278. // supported programming language topic types.
  279. var bindTopicType = map[Lang]func(kind abi.Type) string{
  280. LangGo: bindTopicTypeGo,
  281. LangJava: bindTopicTypeJava,
  282. }
  283. // bindTypeGo converts a Solidity topic type to a Go one. It is almost the same
  284. // funcionality as for simple types, but dynamic types get converted to hashes.
  285. func bindTopicTypeGo(kind abi.Type) string {
  286. bound := bindTypeGo(kind)
  287. if bound == "string" || bound == "[]byte" {
  288. bound = "common.Hash"
  289. }
  290. return bound
  291. }
  292. // bindTypeGo converts a Solidity topic type to a Java one. It is almost the same
  293. // funcionality as for simple types, but dynamic types get converted to hashes.
  294. func bindTopicTypeJava(kind abi.Type) string {
  295. bound := bindTypeJava(kind)
  296. if bound == "String" || bound == "Bytes" {
  297. bound = "Hash"
  298. }
  299. return bound
  300. }
  301. // namedType is a set of functions that transform language specific types to
  302. // named versions that my be used inside method names.
  303. var namedType = map[Lang]func(string, abi.Type) string{
  304. LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
  305. LangJava: namedTypeJava,
  306. }
  307. // namedTypeJava converts some primitive data types to named variants that can
  308. // be used as parts of method names.
  309. func namedTypeJava(javaKind string, solKind abi.Type) string {
  310. switch javaKind {
  311. case "byte[]":
  312. return "Binary"
  313. case "byte[][]":
  314. return "Binaries"
  315. case "string":
  316. return "String"
  317. case "string[]":
  318. return "Strings"
  319. case "boolean":
  320. return "Bool"
  321. case "boolean[]":
  322. return "Bools"
  323. case "BigInt[]":
  324. return "BigInts"
  325. default:
  326. parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
  327. if len(parts) != 4 {
  328. return javaKind
  329. }
  330. switch parts[2] {
  331. case "8", "16", "32", "64":
  332. if parts[3] == "" {
  333. return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
  334. }
  335. return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
  336. default:
  337. return javaKind
  338. }
  339. }
  340. }
  341. // methodNormalizer is a name transformer that modifies Solidity method names to
  342. // conform to target language naming concentions.
  343. var methodNormalizer = map[Lang]func(string) string{
  344. LangGo: capitalise,
  345. LangJava: decapitalise,
  346. }
  347. // capitalise makes a camel-case string which starts with an upper case character.
  348. func capitalise(input string) string {
  349. return abi.ToCamelCase(input)
  350. }
  351. // decapitalise makes a camel-case string which starts with a lower case character.
  352. func decapitalise(input string) string {
  353. // NOTE: This is the current behavior, it doesn't match the comment
  354. // above and needs to be investigated.
  355. return abi.ToCamelCase(input)
  356. }
  357. // structured checks whether a list of ABI data types has enough information to
  358. // operate through a proper Go struct or if flat returns are needed.
  359. func structured(args abi.Arguments) bool {
  360. if len(args) < 2 {
  361. return false
  362. }
  363. exists := make(map[string]bool)
  364. for _, out := range args {
  365. // If the name is anonymous, we can't organize into a struct
  366. if out.Name == "" {
  367. return false
  368. }
  369. // If the field name is empty when normalized or collides (var, Var, _var, _Var),
  370. // we can't organize into a struct
  371. field := capitalise(out.Name)
  372. if field == "" || exists[field] {
  373. return false
  374. }
  375. exists[field] = true
  376. }
  377. return true
  378. }