github.com/amazechain/amc@v0.1.3/internal/avm/common/compiler/solidity.go (about) 1 // Copyright 2023 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain 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 AmazeChain 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 AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package compiler wraps the Solidity and Vyper compiler executables (solc; vyper). 18 package compiler 19 20 import ( 21 "bytes" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "os/exec" 26 "strconv" 27 "strings" 28 ) 29 30 // Solidity contains information about the solidity compiler. 31 type Solidity struct { 32 Path, Version, FullVersion string 33 Major, Minor, Patch int 34 ExtraAllowedPath []string 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 // solidity v.0.8 changes the way ABI, Devdoc and Userdoc are serialized 49 type solcOutputV8 struct { 50 Contracts map[string]struct { 51 BinRuntime string `json:"bin-runtime"` 52 SrcMapRuntime string `json:"srcmap-runtime"` 53 Bin, SrcMap, Metadata string 54 Abi interface{} 55 Devdoc interface{} 56 Userdoc interface{} 57 Hashes map[string]string 58 } 59 Version string 60 } 61 62 func (s *Solidity) allowedPaths() string { 63 paths := []string{".", "./", "../"} // default to support relative paths 64 if len(s.ExtraAllowedPath) > 0 { 65 paths = append(paths, s.ExtraAllowedPath...) 66 } 67 return strings.Join(paths, ", ") 68 } 69 70 func (s *Solidity) makeArgs() []string { 71 p := []string{ 72 "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", 73 "--optimize", // code optimizer switched on 74 "--allow-paths", s.allowedPaths(), 75 } 76 if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { 77 p[1] += ",metadata,hashes" 78 } 79 return p 80 } 81 82 // SolidityVersion runs solc and parses its version output. 83 func SolidityVersion(solc string) (*Solidity, error) { 84 if solc == "" { 85 solc = "solc" 86 } 87 var out bytes.Buffer 88 cmd := exec.Command(solc, "--version") 89 cmd.Stdout = &out 90 err := cmd.Run() 91 if err != nil { 92 return nil, err 93 } 94 matches := versionRegexp.FindStringSubmatch(out.String()) 95 if len(matches) != 4 { 96 return nil, fmt.Errorf("can't parse solc version %q", out.String()) 97 } 98 s := &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} 99 if s.Major, err = strconv.Atoi(matches[1]); err != nil { 100 return nil, err 101 } 102 if s.Minor, err = strconv.Atoi(matches[2]); err != nil { 103 return nil, err 104 } 105 if s.Patch, err = strconv.Atoi(matches[3]); err != nil { 106 return nil, err 107 } 108 return s, nil 109 } 110 111 // CompileSolidityString builds and returns all the contracts contained within a source string. 112 func CompileSolidityString(solc, source string) (map[string]*Contract, error) { 113 if len(source) == 0 { 114 return nil, errors.New("solc: empty source string") 115 } 116 s, err := SolidityVersion(solc) 117 if err != nil { 118 return nil, err 119 } 120 return s.CompileSource(source) 121 } 122 123 // CompileSolidity compiles all given Solidity source files. 124 func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, error) { 125 if len(sourcefiles) == 0 { 126 return nil, errors.New("solc: no source files") 127 } 128 s, err := SolidityVersion(solc) 129 if err != nil { 130 return nil, err 131 } 132 133 return s.CompileFiles(sourcefiles...) 134 } 135 136 // CompileSource builds and returns all the contracts contained within a source string. 137 func (s *Solidity) CompileSource(source string) (map[string]*Contract, error) { 138 args := append(s.makeArgs(), "--") 139 cmd := exec.Command(s.Path, append(args, "-")...) 140 cmd.Stdin = strings.NewReader(source) 141 return s.run(cmd, source) 142 } 143 144 // CompileFiles compiles all given Solidity source files. 145 func (s *Solidity) CompileFiles(sourcefiles ...string) (map[string]*Contract, error) { 146 source, err := slurpFiles(sourcefiles) 147 if err != nil { 148 return nil, err 149 } 150 args := append(s.makeArgs(), "--") 151 cmd := exec.Command(s.Path, append(args, sourcefiles...)...) 152 return s.run(cmd, source) 153 } 154 155 func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { 156 var stderr, stdout bytes.Buffer 157 cmd.Stderr = &stderr 158 cmd.Stdout = &stdout 159 if err := cmd.Run(); err != nil { 160 return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) 161 } 162 return ParseCombinedJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) 163 } 164 165 // ParseCombinedJSON takes the direct output of a solc --combined-output run and 166 // parses it into a map of string contract name to Contract structs. The 167 // provided source, language and compiler version, and compiler options are all 168 // passed through into the Contract structs. 169 // 170 // The solc output is expected to contain ABI, source mapping, user docs, and dev docs. 171 // 172 // Returns an error if the JSON is malformed or missing data, or if the JSON 173 // embedded within the JSON is malformed. 174 func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { 175 var output solcOutput 176 if err := json.Unmarshal(combinedJSON, &output); err != nil { 177 // Try to parse the output with the new solidity v.0.8.0 rules 178 return parseCombinedJSONV8(combinedJSON, source, languageVersion, compilerVersion, compilerOptions) 179 } 180 // Compilation succeeded, assemble and return the contracts. 181 contracts := make(map[string]*Contract) 182 for name, info := range output.Contracts { 183 // Parse the individual compilation results. 184 var abi interface{} 185 if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { 186 return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) 187 } 188 var userdoc, devdoc interface{} 189 json.Unmarshal([]byte(info.Userdoc), &userdoc) 190 json.Unmarshal([]byte(info.Devdoc), &devdoc) 191 192 contracts[name] = &Contract{ 193 Code: "0x" + info.Bin, 194 RuntimeCode: "0x" + info.BinRuntime, 195 Hashes: info.Hashes, 196 Info: ContractInfo{ 197 Source: source, 198 Language: "Solidity", 199 LanguageVersion: languageVersion, 200 CompilerVersion: compilerVersion, 201 CompilerOptions: compilerOptions, 202 SrcMap: info.SrcMap, 203 SrcMapRuntime: info.SrcMapRuntime, 204 AbiDefinition: abi, 205 UserDoc: userdoc, 206 DeveloperDoc: devdoc, 207 Metadata: info.Metadata, 208 }, 209 } 210 } 211 return contracts, nil 212 } 213 214 // parseCombinedJSONV8 parses the direct output of solc --combined-output 215 // and parses it using the rules from solidity v.0.8.0 and later. 216 func parseCombinedJSONV8(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { 217 var output solcOutputV8 218 if err := json.Unmarshal(combinedJSON, &output); err != nil { 219 return nil, err 220 } 221 // Compilation succeeded, assemble and return the contracts. 222 contracts := make(map[string]*Contract) 223 for name, info := range output.Contracts { 224 contracts[name] = &Contract{ 225 Code: "0x" + info.Bin, 226 RuntimeCode: "0x" + info.BinRuntime, 227 Hashes: info.Hashes, 228 Info: ContractInfo{ 229 Source: source, 230 Language: "Solidity", 231 LanguageVersion: languageVersion, 232 CompilerVersion: compilerVersion, 233 CompilerOptions: compilerOptions, 234 SrcMap: info.SrcMap, 235 SrcMapRuntime: info.SrcMapRuntime, 236 AbiDefinition: info.Abi, 237 UserDoc: info.Userdoc, 238 DeveloperDoc: info.Devdoc, 239 Metadata: info.Metadata, 240 }, 241 } 242 } 243 return contracts, nil 244 }