github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/common/compiler/solidity.go (about) 1 // Package compiler wraps the Solidity compiler executable (solc). 2 package compiler 3 4 import ( 5 "bytes" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io/ioutil" 10 "os/exec" 11 "regexp" 12 "strconv" 13 "strings" 14 ) 15 16 var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) 17 18 type Contract struct { 19 Code string `json:"code"` 20 Info ContractInfo `json:"info"` 21 } 22 23 type ContractInfo struct { 24 Source string `json:"source"` 25 Language string `json:"language"` 26 LanguageVersion string `json:"languageVersion"` 27 CompilerVersion string `json:"compilerVersion"` 28 CompilerOptions string `json:"compilerOptions"` 29 AbiDefinition interface{} `json:"abiDefinition"` 30 UserDoc interface{} `json:"userDoc"` 31 DeveloperDoc interface{} `json:"developerDoc"` 32 Metadata string `json:"metadata"` 33 } 34 35 // Solidity contains information about the solidity compiler. 36 type Solidity struct { 37 Path, Version, FullVersion string 38 Major, Minor, Patch int 39 } 40 41 // --combined-output format 42 type solcOutput struct { 43 Contracts map[string]struct { 44 Bin, Abi, Devdoc, Userdoc, Metadata string 45 } 46 Version string 47 } 48 49 func (s *Solidity) makeArgs() []string { 50 p := []string{ 51 "--combined-json", "bin,abi,userdoc,devdoc", 52 "--optimize", // code optimizer switched on 53 } 54 if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { 55 p[1] += ",metadata" 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 var output solcOutput 130 if err := json.Unmarshal(stdout.Bytes(), &output); err != nil { 131 return nil, err 132 } 133 134 // Compilation succeeded, assemble and return the contracts. 135 contracts := make(map[string]*Contract) 136 for name, info := range output.Contracts { 137 // Parse the individual compilation results. 138 var abi interface{} 139 if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { 140 return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) 141 } 142 var userdoc interface{} 143 if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil { 144 return nil, fmt.Errorf("solc: error reading user doc: %v", err) 145 } 146 var devdoc interface{} 147 if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil { 148 return nil, fmt.Errorf("solc: error reading dev doc: %v", err) 149 } 150 contracts[name] = &Contract{ 151 Code: "0x" + info.Bin, 152 Info: ContractInfo{ 153 Source: source, 154 Language: "Solidity", 155 LanguageVersion: s.Version, 156 CompilerVersion: s.Version, 157 CompilerOptions: strings.Join(s.makeArgs(), " "), 158 AbiDefinition: abi, 159 UserDoc: userdoc, 160 DeveloperDoc: devdoc, 161 Metadata: info.Metadata, 162 }, 163 } 164 } 165 return contracts, nil 166 } 167 168 func slurpFiles(files []string) (string, error) { 169 var concat bytes.Buffer 170 for _, file := range files { 171 content, err := ioutil.ReadFile(file) 172 if err != nil { 173 return "", err 174 } 175 concat.Write(content) 176 } 177 return concat.String(), nil 178 }