github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/common/compiler/solidity.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 // Package compiler wraps the Solidity and Vyper compiler executables (solc; vyper). 19 package compiler 20 21 import ( 22 "bytes" 23 "encoding/json" 24 "errors" 25 "fmt" 26 "os/exec" 27 "strconv" 28 "strings" 29 ) 30 31 // Solidity contains information about the solidity compiler. 32 type Solidity struct { 33 Path, Version, FullVersion string 34 Major, Minor, Patch int 35 } 36 37 // --combined-output format 38 type solcOutput struct { 39 Contracts map[string]struct { 40 BinRuntime string `json:"bin-runtime"` 41 SrcMapRuntime string `json:"srcmap-runtime"` 42 Bin, SrcMap, Abi, Devdoc, Userdoc, Metadata string 43 Hashes map[string]string 44 } 45 Version string 46 } 47 48 func (s *Solidity) makeArgs() []string { 49 p := []string{ 50 "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", 51 "--optimize", // code optimizer switched on 52 "--allow-paths", "., ./, ../", // default to support relative paths 53 } 54 if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { 55 p[1] += ",metadata,hashes" 56 } 57 return p 58 } 59 60 // SolidityVersion runs solc and parses its version output. 61 func SolidityVersion(solc string) (*Solidity, error) { 62 if solc == "" { 63 solc = "solc" 64 } 65 var out bytes.Buffer 66 cmd := exec.Command(solc, "--version") 67 cmd.Stdout = &out 68 err := cmd.Run() 69 if err != nil { 70 return nil, err 71 } 72 matches := versionRegexp.FindStringSubmatch(out.String()) 73 if len(matches) != 4 { 74 return nil, fmt.Errorf("can't parse solc version %q", out.String()) 75 } 76 s := &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} 77 if s.Major, err = strconv.Atoi(matches[1]); err != nil { 78 return nil, err 79 } 80 if s.Minor, err = strconv.Atoi(matches[2]); err != nil { 81 return nil, err 82 } 83 if s.Patch, err = strconv.Atoi(matches[3]); err != nil { 84 return nil, err 85 } 86 return s, nil 87 } 88 89 // CompileSolidityString builds and returns all the contracts contained within a source string. 90 func CompileSolidityString(solc, source string) (map[string]*Contract, error) { 91 if len(source) == 0 { 92 return nil, errors.New("solc: empty source string") 93 } 94 s, err := SolidityVersion(solc) 95 if err != nil { 96 return nil, err 97 } 98 args := append(s.makeArgs(), "--") 99 cmd := exec.Command(s.Path, append(args, "-")...) 100 cmd.Stdin = strings.NewReader(source) 101 return s.run(cmd, source) 102 } 103 104 // CompileSolidity compiles all given Solidity source files. 105 func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, error) { 106 if len(sourcefiles) == 0 { 107 return nil, errors.New("solc: no source files") 108 } 109 source, err := slurpFiles(sourcefiles) 110 if err != nil { 111 return nil, err 112 } 113 s, err := SolidityVersion(solc) 114 if err != nil { 115 return nil, err 116 } 117 args := append(s.makeArgs(), "--") 118 cmd := exec.Command(s.Path, append(args, sourcefiles...)...) 119 return s.run(cmd, source) 120 } 121 122 func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { 123 var stderr, stdout bytes.Buffer 124 cmd.Stderr = &stderr 125 cmd.Stdout = &stdout 126 if err := cmd.Run(); err != nil { 127 return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) 128 } 129 130 return ParseCombinedJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) 131 } 132 133 // ParseCombinedJSON takes the direct output of a solc --combined-output run and 134 // parses it into a map of string contract name to Contract structs. The 135 // provided source, language and compiler version, and compiler options are all 136 // passed through into the Contract structs. 137 // 138 // The solc output is expected to contain ABI, source mapping, user docs, and dev docs. 139 // 140 // Returns an error if the JSON is malformed or missing data, or if the JSON 141 // embedded within the JSON is malformed. 142 func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { 143 var output solcOutput 144 if err := json.Unmarshal(combinedJSON, &output); err != nil { 145 return nil, err 146 } 147 // Compilation succeeded, assemble and return the contracts. 148 contracts := make(map[string]*Contract) 149 for name, info := range output.Contracts { 150 // Parse the individual compilation results. 151 var abi interface{} 152 if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { 153 return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) 154 } 155 var userdoc, devdoc interface{} 156 json.Unmarshal([]byte(info.Userdoc), &userdoc) 157 json.Unmarshal([]byte(info.Devdoc), &devdoc) 158 159 contracts[name] = &Contract{ 160 Code: "0x" + info.Bin, 161 RuntimeCode: "0x" + info.BinRuntime, 162 Hashes: info.Hashes, 163 Info: ContractInfo{ 164 Source: source, 165 Language: "Solidity", 166 LanguageVersion: languageVersion, 167 CompilerVersion: compilerVersion, 168 CompilerOptions: compilerOptions, 169 SrcMap: info.SrcMap, 170 SrcMapRuntime: info.SrcMapRuntime, 171 AbiDefinition: abi, 172 UserDoc: userdoc, 173 DeveloperDoc: devdoc, 174 Metadata: info.Metadata, 175 }, 176 } 177 } 178 return contracts, nil 179 }