main.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. // Copyright 2016 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU 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. // go-ethereum 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 General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. package main
  17. import (
  18. "encoding/json"
  19. "fmt"
  20. "io/ioutil"
  21. "os"
  22. "path/filepath"
  23. "regexp"
  24. "strings"
  25. "github.com/ethereum/go-ethereum/accounts/abi"
  26. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  27. "github.com/ethereum/go-ethereum/cmd/utils"
  28. "github.com/ethereum/go-ethereum/common/compiler"
  29. "github.com/ethereum/go-ethereum/crypto"
  30. "github.com/ethereum/go-ethereum/internal/flags"
  31. "github.com/ethereum/go-ethereum/log"
  32. "gopkg.in/urfave/cli.v1"
  33. )
  34. var (
  35. // Git SHA1 commit hash of the release (set via linker flags)
  36. gitCommit = ""
  37. gitDate = ""
  38. app *cli.App
  39. // Flags needed by abigen
  40. abiFlag = cli.StringFlag{
  41. Name: "abi",
  42. Usage: "Path to the Ethereum contract ABI json to bind, - for STDIN",
  43. }
  44. binFlag = cli.StringFlag{
  45. Name: "bin",
  46. Usage: "Path to the Ethereum contract bytecode (generate deploy method)",
  47. }
  48. typeFlag = cli.StringFlag{
  49. Name: "type",
  50. Usage: "Struct name for the binding (default = package name)",
  51. }
  52. jsonFlag = cli.StringFlag{
  53. Name: "combined-json",
  54. Usage: "Path to the combined-json file generated by compiler",
  55. }
  56. solFlag = cli.StringFlag{
  57. Name: "sol",
  58. Usage: "Path to the Ethereum contract Solidity source to build and bind",
  59. }
  60. solcFlag = cli.StringFlag{
  61. Name: "solc",
  62. Usage: "Solidity compiler to use if source builds are requested",
  63. Value: "solc",
  64. }
  65. vyFlag = cli.StringFlag{
  66. Name: "vy",
  67. Usage: "Path to the Ethereum contract Vyper source to build and bind",
  68. }
  69. vyperFlag = cli.StringFlag{
  70. Name: "vyper",
  71. Usage: "Vyper compiler to use if source builds are requested",
  72. Value: "vyper",
  73. }
  74. excFlag = cli.StringFlag{
  75. Name: "exc",
  76. Usage: "Comma separated types to exclude from binding",
  77. }
  78. pkgFlag = cli.StringFlag{
  79. Name: "pkg",
  80. Usage: "Package name to generate the binding into",
  81. }
  82. outFlag = cli.StringFlag{
  83. Name: "out",
  84. Usage: "Output file for the generated binding (default = stdout)",
  85. }
  86. langFlag = cli.StringFlag{
  87. Name: "lang",
  88. Usage: "Destination language for the bindings (go, java, objc)",
  89. Value: "go",
  90. }
  91. aliasFlag = cli.StringFlag{
  92. Name: "alias",
  93. Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2",
  94. }
  95. )
  96. func init() {
  97. app = flags.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
  98. app.Flags = []cli.Flag{
  99. abiFlag,
  100. binFlag,
  101. typeFlag,
  102. jsonFlag,
  103. solFlag,
  104. solcFlag,
  105. vyFlag,
  106. vyperFlag,
  107. excFlag,
  108. pkgFlag,
  109. outFlag,
  110. langFlag,
  111. aliasFlag,
  112. }
  113. app.Action = utils.MigrateFlags(abigen)
  114. cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
  115. }
  116. func abigen(c *cli.Context) error {
  117. utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag) // Only one source can be selected.
  118. if c.GlobalString(pkgFlag.Name) == "" {
  119. utils.Fatalf("No destination package specified (--pkg)")
  120. }
  121. var lang bind.Lang
  122. switch c.GlobalString(langFlag.Name) {
  123. case "go":
  124. lang = bind.LangGo
  125. case "java":
  126. lang = bind.LangJava
  127. case "objc":
  128. lang = bind.LangObjC
  129. utils.Fatalf("Objc binding generation is uncompleted")
  130. default:
  131. utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.GlobalString(langFlag.Name))
  132. }
  133. // If the entire solidity code was specified, build and bind based on that
  134. var (
  135. abis []string
  136. bins []string
  137. types []string
  138. sigs []map[string]string
  139. libs = make(map[string]string)
  140. aliases = make(map[string]string)
  141. )
  142. if c.GlobalString(abiFlag.Name) != "" {
  143. // Load up the ABI, optional bytecode and type name from the parameters
  144. var (
  145. abi []byte
  146. err error
  147. )
  148. input := c.GlobalString(abiFlag.Name)
  149. if input == "-" {
  150. abi, err = ioutil.ReadAll(os.Stdin)
  151. } else {
  152. abi, err = ioutil.ReadFile(input)
  153. }
  154. if err != nil {
  155. utils.Fatalf("Failed to read input ABI: %v", err)
  156. }
  157. abis = append(abis, string(abi))
  158. var bin []byte
  159. if binFile := c.GlobalString(binFlag.Name); binFile != "" {
  160. if bin, err = ioutil.ReadFile(binFile); err != nil {
  161. utils.Fatalf("Failed to read input bytecode: %v", err)
  162. }
  163. if strings.Contains(string(bin), "//") {
  164. utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
  165. }
  166. }
  167. bins = append(bins, string(bin))
  168. kind := c.GlobalString(typeFlag.Name)
  169. if kind == "" {
  170. kind = c.GlobalString(pkgFlag.Name)
  171. }
  172. types = append(types, kind)
  173. } else {
  174. // Generate the list of types to exclude from binding
  175. exclude := make(map[string]bool)
  176. for _, kind := range strings.Split(c.GlobalString(excFlag.Name), ",") {
  177. exclude[strings.ToLower(kind)] = true
  178. }
  179. var err error
  180. var contracts map[string]*compiler.Contract
  181. switch {
  182. case c.GlobalIsSet(solFlag.Name):
  183. contracts, err = compiler.CompileSolidity(c.GlobalString(solcFlag.Name), c.GlobalString(solFlag.Name))
  184. if err != nil {
  185. utils.Fatalf("Failed to build Solidity contract: %v", err)
  186. }
  187. case c.GlobalIsSet(vyFlag.Name):
  188. output, err := compiler.CompileVyper(c.GlobalString(vyperFlag.Name), c.GlobalString(vyFlag.Name))
  189. if err != nil {
  190. utils.Fatalf("Failed to build Vyper contract: %v", err)
  191. }
  192. contracts = make(map[string]*compiler.Contract)
  193. for n, contract := range output {
  194. name := n
  195. // Sanitize the combined json names to match the
  196. // format expected by solidity.
  197. if !strings.Contains(n, ":") {
  198. // Remove extra path components
  199. name = abi.ToCamelCase(strings.TrimSuffix(filepath.Base(name), ".vy"))
  200. }
  201. contracts[name] = contract
  202. }
  203. case c.GlobalIsSet(jsonFlag.Name):
  204. jsonOutput, err := ioutil.ReadFile(c.GlobalString(jsonFlag.Name))
  205. if err != nil {
  206. utils.Fatalf("Failed to read combined-json from compiler: %v", err)
  207. }
  208. contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
  209. if err != nil {
  210. utils.Fatalf("Failed to read contract information from json output: %v", err)
  211. }
  212. }
  213. // Gather all non-excluded contract for binding
  214. for name, contract := range contracts {
  215. if exclude[strings.ToLower(name)] {
  216. continue
  217. }
  218. abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
  219. if err != nil {
  220. utils.Fatalf("Failed to parse ABIs from compiler output: %v", err)
  221. }
  222. abis = append(abis, string(abi))
  223. bins = append(bins, contract.Code)
  224. sigs = append(sigs, contract.Hashes)
  225. nameParts := strings.Split(name, ":")
  226. types = append(types, nameParts[len(nameParts)-1])
  227. libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36]
  228. libs[libPattern] = nameParts[len(nameParts)-1]
  229. }
  230. }
  231. // Extract all aliases from the flags
  232. if c.GlobalIsSet(aliasFlag.Name) {
  233. // We support multi-versions for aliasing
  234. // e.g.
  235. // foo=bar,foo2=bar2
  236. // foo:bar,foo2:bar2
  237. re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
  238. submatches := re.FindAllStringSubmatch(c.GlobalString(aliasFlag.Name), -1)
  239. for _, match := range submatches {
  240. aliases[match[1]] = match[2]
  241. }
  242. }
  243. // Generate the contract binding
  244. code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs, aliases)
  245. if err != nil {
  246. utils.Fatalf("Failed to generate ABI binding: %v", err)
  247. }
  248. // Either flush it out to a file or display on the standard output
  249. if !c.GlobalIsSet(outFlag.Name) {
  250. fmt.Printf("%s\n", code)
  251. return nil
  252. }
  253. if err := ioutil.WriteFile(c.GlobalString(outFlag.Name), []byte(code), 0600); err != nil {
  254. utils.Fatalf("Failed to write ABI binding: %v", err)
  255. }
  256. return nil
  257. }
  258. func main() {
  259. log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
  260. if err := app.Run(os.Args); err != nil {
  261. fmt.Fprintln(os.Stderr, err)
  262. os.Exit(1)
  263. }
  264. }