github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/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 12:09:32</date> 10 //</624342608012906496> 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 Info ContractInfo `json:"info"` 34 } 35 36 //收缩信息包含有关已编译合同的信息,包括访问 37 //到ABI定义、用户和开发人员文档以及元数据。 38 // 39 //取决于源、语言版本、编译器版本和编译器 40 //选项将提供有关如何编译合同的信息。 41 type ContractInfo struct { 42 Source string `json:"source"` 43 Language string `json:"language"` 44 LanguageVersion string `json:"languageVersion"` 45 CompilerVersion string `json:"compilerVersion"` 46 CompilerOptions string `json:"compilerOptions"` 47 AbiDefinition interface{} `json:"abiDefinition"` 48 UserDoc interface{} `json:"userDoc"` 49 DeveloperDoc interface{} `json:"developerDoc"` 50 Metadata string `json:"metadata"` 51 } 52 53 // 54 type Solidity struct { 55 Path, Version, FullVersion string 56 Major, Minor, Patch int 57 } 58 59 //——组合输出格式 60 type solcOutput struct { 61 Contracts map[string]struct { 62 Bin, Abi, Devdoc, Userdoc, Metadata string 63 } 64 Version string 65 } 66 67 func (s *Solidity) makeArgs() []string { 68 p := []string{ 69 "--combined-json", "bin,abi,userdoc,devdoc", 70 "--optimize", //代码优化器已打开 71 } 72 if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { 73 p[1] += ",metadata" 74 } 75 return p 76 } 77 78 //solidityversion运行solc并解析其版本输出。 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生成并返回源字符串中包含的所有协定。 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编译所有给定的solidity源文件。 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 148 return ParseCombinedJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) 149 } 150 151 //parsecombinedjson接受solc的直接输出——组合输出运行和 152 //将其解析为字符串协定名称到协定结构的映射。这个 153 //提供的源、语言和编译器版本以及编译器选项都是 154 //传递到合同结构中。 155 // 156 //SOLC输出应该包含ABI、用户文档和开发文档。 157 // 158 //如果JSON格式不正确或缺少数据,或者如果JSON 159 //嵌入在JSON中的格式不正确。 160 func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { 161 var output solcOutput 162 if err := json.Unmarshal(combinedJSON, &output); err != nil { 163 return nil, err 164 } 165 166 //编译成功,组装并返回合同。 167 contracts := make(map[string]*Contract) 168 for name, info := range output.Contracts { 169 //分析各个编译结果。 170 var abi interface{} 171 if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { 172 return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) 173 } 174 var userdoc interface{} 175 if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil { 176 return nil, fmt.Errorf("solc: error reading user doc: %v", err) 177 } 178 var devdoc interface{} 179 if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil { 180 return nil, fmt.Errorf("solc: error reading dev doc: %v", err) 181 } 182 contracts[name] = &Contract{ 183 Code: "0x" + info.Bin, 184 Info: ContractInfo{ 185 Source: source, 186 Language: "Solidity", 187 LanguageVersion: languageVersion, 188 CompilerVersion: compilerVersion, 189 CompilerOptions: compilerOptions, 190 AbiDefinition: abi, 191 UserDoc: userdoc, 192 DeveloperDoc: devdoc, 193 Metadata: info.Metadata, 194 }, 195 } 196 } 197 return contracts, nil 198 } 199 200 func slurpFiles(files []string) (string, error) { 201 var concat bytes.Buffer 202 for _, file := range files { 203 content, err := ioutil.ReadFile(file) 204 if err != nil { 205 return "", err 206 } 207 concat.Write(content) 208 } 209 return concat.String(), nil 210 } 211