solidity.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // Copyright 2015 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 compiler
  17. import (
  18. "bytes"
  19. "encoding/json"
  20. "errors"
  21. "fmt"
  22. "io/ioutil"
  23. "os"
  24. "os/exec"
  25. "path/filepath"
  26. "regexp"
  27. "strings"
  28. "github.com/ethereum/go-ethereum/common"
  29. "github.com/ethereum/go-ethereum/crypto"
  30. "github.com/ethereum/go-ethereum/logger"
  31. "github.com/ethereum/go-ethereum/logger/glog"
  32. )
  33. const (
  34. // flair = "Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com> (c) 2014-2015"
  35. flair = ""
  36. languageVersion = "0"
  37. )
  38. var (
  39. versionRegExp = regexp.MustCompile("[0-9]+.[0-9]+.[0-9]+")
  40. params = []string{
  41. "--binary", // Request to output the contract in binary (hexadecimal).
  42. "file", //
  43. "--json-abi", // Request to output the contract's JSON ABI interface.
  44. "file", //
  45. "--natspec-user", // Request to output the contract's Natspec user documentation.
  46. "file", //
  47. "--natspec-dev", // Request to output the contract's Natspec developer documentation.
  48. "file",
  49. "--add-std",
  50. "1",
  51. }
  52. )
  53. type Contract struct {
  54. Code string `json:"code"`
  55. Info ContractInfo `json:"info"`
  56. }
  57. type ContractInfo struct {
  58. Source string `json:"source"`
  59. Language string `json:"language"`
  60. LanguageVersion string `json:"languageVersion"`
  61. CompilerVersion string `json:"compilerVersion"`
  62. AbiDefinition interface{} `json:"abiDefinition"`
  63. UserDoc interface{} `json:"userDoc"`
  64. DeveloperDoc interface{} `json:"developerDoc"`
  65. }
  66. type Solidity struct {
  67. solcPath string
  68. version string
  69. }
  70. func New(solcPath string) (sol *Solidity, err error) {
  71. // set default solc
  72. if len(solcPath) == 0 {
  73. solcPath = "solc"
  74. }
  75. solcPath, err = exec.LookPath(solcPath)
  76. if err != nil {
  77. return
  78. }
  79. cmd := exec.Command(solcPath, "--version")
  80. var out bytes.Buffer
  81. cmd.Stdout = &out
  82. err = cmd.Run()
  83. if err != nil {
  84. return
  85. }
  86. version := versionRegExp.FindString(out.String())
  87. sol = &Solidity{
  88. solcPath: solcPath,
  89. version: version,
  90. }
  91. glog.V(logger.Info).Infoln(sol.Info())
  92. return
  93. }
  94. func (sol *Solidity) Info() string {
  95. return fmt.Sprintf("solc v%s\nSolidity Compiler: %s\n%s", sol.version, sol.solcPath, flair)
  96. }
  97. func (sol *Solidity) Version() string {
  98. return sol.version
  99. }
  100. // Compile builds and returns all the contracts contained within a source string.
  101. func (sol *Solidity) Compile(source string) (map[string]*Contract, error) {
  102. // Short circuit if no source code was specified
  103. if len(source) == 0 {
  104. return nil, errors.New("solc: empty source string")
  105. }
  106. // Create a safe place to dump compilation output
  107. wd, err := ioutil.TempDir("", "solc")
  108. if err != nil {
  109. return nil, fmt.Errorf("solc: failed to create temporary build folder: %v", err)
  110. }
  111. defer os.RemoveAll(wd)
  112. // Assemble the compiler command, change to the temp folder and capture any errors
  113. stderr := new(bytes.Buffer)
  114. cmd := exec.Command(sol.solcPath, params...)
  115. cmd.Dir = wd
  116. cmd.Stdin = strings.NewReader(source)
  117. cmd.Stderr = stderr
  118. if err := cmd.Run(); err != nil {
  119. return nil, fmt.Errorf("solc: %v\n%s", err, string(stderr.Bytes()))
  120. }
  121. // Sanity check that something was actually built
  122. matches, _ := filepath.Glob(wd + "/*.binary")
  123. if len(matches) < 1 {
  124. return nil, fmt.Errorf("solc: no build results found")
  125. }
  126. // Compilation succeeded, assemble and return the contracts
  127. contracts := make(map[string]*Contract)
  128. for _, path := range matches {
  129. _, file := filepath.Split(path)
  130. base := strings.Split(file, ".")[0]
  131. // Parse the individual compilation results (code binary, ABI definitions, user and dev docs)
  132. var binary []byte
  133. if binary, err = ioutil.ReadFile(filepath.Join(wd, base+".binary")); err != nil {
  134. return nil, fmt.Errorf("solc: error reading compiler output for code: %v", err)
  135. }
  136. var abi interface{}
  137. if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".abi")); err != nil {
  138. return nil, fmt.Errorf("solc: error reading abi definition: %v", err)
  139. } else if err = json.Unmarshal(blob, &abi); err != nil {
  140. return nil, fmt.Errorf("solc: error parsing abi definition: %v", err)
  141. }
  142. var userdoc interface{}
  143. if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".docuser")); err != nil {
  144. return nil, fmt.Errorf("solc: error reading user doc: %v", err)
  145. } else if err = json.Unmarshal(blob, &userdoc); err != nil {
  146. return nil, fmt.Errorf("solc: error parsing user doc: %v", err)
  147. }
  148. var devdoc interface{}
  149. if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".docdev")); err != nil {
  150. return nil, fmt.Errorf("solc: error reading dev doc: %v", err)
  151. } else if err = json.Unmarshal(blob, &devdoc); err != nil {
  152. return nil, fmt.Errorf("solc: error parsing dev doc: %v", err)
  153. }
  154. // Assemble the final contract
  155. contracts[base] = &Contract{
  156. Code: "0x" + string(binary),
  157. Info: ContractInfo{
  158. Source: source,
  159. Language: "Solidity",
  160. LanguageVersion: languageVersion,
  161. CompilerVersion: sol.version,
  162. AbiDefinition: abi,
  163. UserDoc: userdoc,
  164. DeveloperDoc: devdoc,
  165. },
  166. }
  167. }
  168. return contracts, nil
  169. }
  170. func SaveInfo(info *ContractInfo, filename string) (contenthash common.Hash, err error) {
  171. infojson, err := json.Marshal(info)
  172. if err != nil {
  173. return
  174. }
  175. contenthash = common.BytesToHash(crypto.Sha3(infojson))
  176. err = ioutil.WriteFile(filename, infojson, 0600)
  177. return
  178. }