github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/common/compiler/solidity.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:33</date> 10 //</624450073169760256> 11 12 13 //包编译器包装solidity编译器可执行文件(solc)。 14 package compiler 15 16 import ( 17 "bytes" 18 "encoding/json" 19 "errors" 20 "fmt" 21 "io/ioutil" 22 "os/exec" 23 "regexp" 24 "strconv" 25 "strings" 26 ) 27 28 var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) 29 30 //合同包含有关已编译合同的信息,以及其代码和运行时代码。 31 type Contract struct { 32 Code string `json:"code"` 33 RuntimeCode string `json:"runtime-code"` 34 Info ContractInfo `json:"info"` 35 } 36 37 //收缩信息包含有关已编译合同的信息,包括访问 38 // 39 // 40 //取决于源、语言版本、编译器版本和编译器 41 //选项将提供有关如何编译合同的信息。 42 type ContractInfo struct { 43 Source string `json:"source"` 44 Language string `json:"language"` 45 LanguageVersion string `json:"languageVersion"` 46 CompilerVersion string `json:"compilerVersion"` 47 CompilerOptions string `json:"compilerOptions"` 48 SrcMap string `json:"srcMap"` 49 SrcMapRuntime string `json:"srcMapRuntime"` 50 AbiDefinition interface{} `json:"abiDefinition"` 51 UserDoc interface{} `json:"userDoc"` 52 DeveloperDoc interface{} `json:"developerDoc"` 53 Metadata string `json:"metadata"` 54 } 55 56 //solidity包含有关solidity编译器的信息。 57 type Solidity struct { 58 Path, Version, FullVersion string 59 Major, Minor, Patch int 60 } 61 62 //——组合输出格式 63 type solcOutput struct { 64 Contracts map[string]struct { 65 BinRuntime string `json:"bin-runtime"` 66 SrcMapRuntime string `json:"srcmap-runtime"` 67 Bin, SrcMap, Abi, Devdoc, Userdoc, Metadata string 68 } 69 Version string 70 } 71 72 func (s *Solidity) makeArgs() []string { 73 p := []string{ 74 "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", 75 "--optimize", //代码优化器已打开 76 } 77 if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { 78 p[1] += ",metadata" 79 } 80 return p 81 } 82 83 //solidityversion运行solc并解析其版本输出。 84 func SolidityVersion(solc string) (*Solidity, error) { 85 if solc == "" { 86 solc = "solc" 87 } 88 var out bytes.Buffer 89 cmd := exec.Command(solc, "--version") 90 cmd.Stdout = &out 91 err := cmd.Run() 92 if err != nil { 93 return nil, err 94 } 95 matches := versionRegexp.FindStringSubmatch(out.String()) 96 if len(matches) != 4 { 97 return nil, fmt.Errorf("can't parse solc version %q", out.String()) 98 } 99 s := &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} 100 if s.Major, err = strconv.Atoi(matches[1]); err != nil { 101 return nil, err 102 } 103 if s.Minor, err = strconv.Atoi(matches[2]); err != nil { 104 return nil, err 105 } 106 if s.Patch, err = strconv.Atoi(matches[3]); err != nil { 107 return nil, err 108 } 109 return s, nil 110 } 111 112 //CompilesOlidityString生成并返回源字符串中包含的所有协定。 113 func CompileSolidityString(solc, source string) (map[string]*Contract, error) { 114 if len(source) == 0 { 115 return nil, errors.New("solc: empty source string") 116 } 117 s, err := SolidityVersion(solc) 118 if err != nil { 119 return nil, err 120 } 121 args := append(s.makeArgs(), "--") 122 cmd := exec.Command(s.Path, append(args, "-")...) 123 cmd.Stdin = strings.NewReader(source) 124 return s.run(cmd, source) 125 } 126 127 //compilesolidity编译所有给定的solidity源文件。 128 func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, error) { 129 if len(sourcefiles) == 0 { 130 return nil, errors.New("solc: no source files") 131 } 132 source, err := slurpFiles(sourcefiles) 133 if err != nil { 134 return nil, err 135 } 136 s, err := SolidityVersion(solc) 137 if err != nil { 138 return nil, err 139 } 140 args := append(s.makeArgs(), "--") 141 cmd := exec.Command(s.Path, append(args, sourcefiles...)...) 142 return s.run(cmd, source) 143 } 144 145 func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { 146 var stderr, stdout bytes.Buffer 147 cmd.Stderr = &stderr 148 cmd.Stdout = &stdout 149 if err := cmd.Run(); err != nil { 150 return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) 151 } 152 153 return ParseCombinedJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) 154 } 155 156 //parsecombinedjson接受solc的直接输出——组合输出运行和 157 //将其解析为字符串协定名称到协定结构的映射。这个 158 //提供的源、语言和编译器版本以及编译器选项都是 159 //传递到合同结构中。 160 // 161 //SOLC输出应该包含ABI、源映射、用户文档和开发文档。 162 // 163 //如果JSON格式不正确或缺少数据,或者如果JSON 164 //嵌入在JSON中的格式不正确。 165 func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { 166 var output solcOutput 167 if err := json.Unmarshal(combinedJSON, &output); err != nil { 168 return nil, err 169 } 170 171 //编译成功,组装并返回合同。 172 contracts := make(map[string]*Contract) 173 for name, info := range output.Contracts { 174 //分析各个编译结果。 175 var abi interface{} 176 if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { 177 return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) 178 } 179 var userdoc interface{} 180 if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil { 181 return nil, fmt.Errorf("solc: error reading user doc: %v", err) 182 } 183 var devdoc interface{} 184 if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil { 185 return nil, fmt.Errorf("solc: error reading dev doc: %v", err) 186 } 187 contracts[name] = &Contract{ 188 Code: "0x" + info.Bin, 189 RuntimeCode: "0x" + info.BinRuntime, 190 Info: ContractInfo{ 191 Source: source, 192 Language: "Solidity", 193 LanguageVersion: languageVersion, 194 CompilerVersion: compilerVersion, 195 CompilerOptions: compilerOptions, 196 SrcMap: info.SrcMap, 197 SrcMapRuntime: info.SrcMapRuntime, 198 AbiDefinition: abi, 199 UserDoc: userdoc, 200 DeveloperDoc: devdoc, 201 Metadata: info.Metadata, 202 }, 203 } 204 } 205 return contracts, nil 206 } 207 208 func slurpFiles(files []string) (string, error) { 209 var concat bytes.Buffer 210 for _, file := range files { 211 content, err := ioutil.ReadFile(file) 212 if err != nil { 213 return "", err 214 } 215 concat.Write(content) 216 } 217 return concat.String(), nil 218 } 219