gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/common/compiler/solidity.go (about) 1 // Copyright 2018 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain 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 aquachain 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 aquachain library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package compiler wraps the Solidity compiler executable (solc). 18 package compiler 19 20 import ( 21 "bytes" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "io/ioutil" 26 "os/exec" 27 "regexp" 28 "strconv" 29 "strings" 30 ) 31 32 var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) 33 34 type Contract struct { 35 Code string `json:"code"` 36 Info ContractInfo `json:"info"` 37 } 38 39 type ContractInfo struct { 40 Source string `json:"source"` 41 Language string `json:"language"` 42 LanguageVersion string `json:"languageVersion"` 43 CompilerVersion string `json:"compilerVersion"` 44 CompilerOptions string `json:"compilerOptions"` 45 AbiDefinition interface{} `json:"abiDefinition"` 46 UserDoc interface{} `json:"userDoc"` 47 DeveloperDoc interface{} `json:"developerDoc"` 48 Metadata string `json:"metadata"` 49 } 50 51 // Solidity contains information about the solidity compiler. 52 type Solidity struct { 53 Path, Version, FullVersion string 54 Major, Minor, Patch int 55 } 56 57 // --combined-output format 58 type solcOutput struct { 59 Contracts map[string]struct { 60 Bin, Abi, Devdoc, Userdoc, Metadata string 61 } 62 Version string 63 } 64 65 func (s *Solidity) makeArgs() []string { 66 p := []string{ 67 "--combined-json", "bin,abi,userdoc,devdoc", 68 "--evm-version", "byzantium", 69 // "--add-std", // include standard lib contracts 70 "--optimize", // code optimizer switched on 71 } 72 // if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { 73 // // p[1] += ",metadata" 74 // } 75 return p 76 } 77 78 // SolidityVersion runs solc and parses its version output. 79 func SolidityVersion(solc string) (*Solidity, error) { 80 if solc == "" { 81 solc = "solc" 82 } 83 var out bytes.Buffer 84 cmd := exec.Command(solc, "--version") 85 cmd.Stdout = &out 86 err := cmd.Run() 87 if err != nil { 88 return nil, err 89 } 90 matches := versionRegexp.FindStringSubmatch(out.String()) 91 if len(matches) != 4 { 92 return nil, fmt.Errorf("can't parse solc version %q", out.String()) 93 } 94 s := &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} 95 if s.Major, err = strconv.Atoi(matches[1]); err != nil { 96 return nil, err 97 } 98 if s.Minor, err = strconv.Atoi(matches[2]); err != nil { 99 return nil, err 100 } 101 if s.Patch, err = strconv.Atoi(matches[3]); err != nil { 102 return nil, err 103 } 104 return s, nil 105 } 106 107 // CompileSolidityString builds and returns all the contracts contained within a source string. 108 func CompileSolidityString(solc, source string) (map[string]*Contract, error) { 109 if len(source) == 0 { 110 return nil, errors.New("solc: empty source string") 111 } 112 s, err := SolidityVersion(solc) 113 if err != nil { 114 return nil, err 115 } 116 args := append(s.makeArgs(), "--") 117 cmd := exec.Command(s.Path, append(args, "-")...) 118 cmd.Stdin = strings.NewReader(source) 119 return s.run(cmd, source) 120 } 121 122 // CompileSolidity compiles all given Solidity source files. 123 func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, error) { 124 if len(sourcefiles) == 0 { 125 return nil, errors.New("solc: no source files") 126 } 127 source, err := slurpFiles(sourcefiles) 128 if err != nil { 129 return nil, err 130 } 131 s, err := SolidityVersion(solc) 132 if err != nil { 133 return nil, err 134 } 135 args := append(s.makeArgs(), "--") 136 cmd := exec.Command(s.Path, append(args, sourcefiles...)...) 137 return s.run(cmd, source) 138 } 139 140 func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { 141 var stderr, stdout bytes.Buffer 142 cmd.Stderr = &stderr 143 cmd.Stdout = &stdout 144 if err := cmd.Run(); err != nil { 145 return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) 146 } 147 var output solcOutput 148 if err := json.Unmarshal(stdout.Bytes(), &output); err != nil { 149 return nil, err 150 } 151 152 // Compilation succeeded, assemble and return the contracts. 153 contracts := make(map[string]*Contract) 154 for name, info := range output.Contracts { 155 // Parse the individual compilation results. 156 var abi interface{} 157 if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { 158 return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) 159 } 160 var userdoc interface{} 161 if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil { 162 return nil, fmt.Errorf("solc: error reading user doc: %v", err) 163 } 164 var devdoc interface{} 165 if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil { 166 return nil, fmt.Errorf("solc: error reading dev doc: %v", err) 167 } 168 contracts[name] = &Contract{ 169 Code: "0x" + info.Bin, 170 Info: ContractInfo{ 171 Source: source, 172 Language: "Solidity", 173 LanguageVersion: s.Version, 174 CompilerVersion: s.Version, 175 CompilerOptions: strings.Join(s.makeArgs(), " "), 176 AbiDefinition: abi, 177 UserDoc: userdoc, 178 DeveloperDoc: devdoc, 179 Metadata: info.Metadata, 180 }, 181 } 182 } 183 return contracts, nil 184 } 185 186 func slurpFiles(files []string) (string, error) { 187 var concat bytes.Buffer 188 for _, file := range files { 189 content, err := ioutil.ReadFile(file) 190 if err != nil { 191 return "", err 192 } 193 concat.Write(content) 194 } 195 return concat.String(), nil 196 }