| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | package compiler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/exec" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/logger" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/logger/glog" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							| 
									
										
										
										
											2015-09-15 00:35:22 +02:00
										 |  |  | 	versionRegexp = regexp.MustCompile("[0-9]+\\.[0-9]+\\.[0-9]+") | 
					
						
							|  |  |  | 	legacyRegexp  = regexp.MustCompile("0\\.(9\\..*|1\\.[01])") | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 	paramsLegacy  = []string{ | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 		"--binary",       // Request to output the contract in binary (hexadecimal). | 
					
						
							|  |  |  | 		"file",           // | 
					
						
							|  |  |  | 		"--json-abi",     // Request to output the contract's JSON ABI interface. | 
					
						
							|  |  |  | 		"file",           // | 
					
						
							|  |  |  | 		"--natspec-user", // Request to output the contract's Natspec user documentation. | 
					
						
							|  |  |  | 		"file",           // | 
					
						
							|  |  |  | 		"--natspec-dev",  // Request to output the contract's Natspec developer documentation. | 
					
						
							|  |  |  | 		"file", | 
					
						
							| 
									
										
										
										
											2015-05-24 19:12:18 +02:00
										 |  |  | 		"--add-std", | 
					
						
							|  |  |  | 		"1", | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 	paramsNew = []string{ | 
					
						
							| 
									
										
										
										
											2015-09-15 00:35:22 +02:00
										 |  |  | 		"--bin",      // Request to output the contract in binary (hexadecimal). | 
					
						
							|  |  |  | 		"--abi",      // Request to output the contract's JSON ABI interface. | 
					
						
							|  |  |  | 		"--userdoc",  // Request to output the contract's Natspec user documentation. | 
					
						
							|  |  |  | 		"--devdoc",   // Request to output the contract's Natspec developer documentation. | 
					
						
							|  |  |  | 		"--add-std",  // include standard lib contracts | 
					
						
							|  |  |  | 		"--optimize", // code optimizer switched on | 
					
						
							|  |  |  | 		"-o",         // output directory | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Contract struct { | 
					
						
							|  |  |  | 	Code string       `json:"code"` | 
					
						
							|  |  |  | 	Info ContractInfo `json:"info"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type ContractInfo struct { | 
					
						
							|  |  |  | 	Source          string      `json:"source"` | 
					
						
							|  |  |  | 	Language        string      `json:"language"` | 
					
						
							|  |  |  | 	LanguageVersion string      `json:"languageVersion"` | 
					
						
							|  |  |  | 	CompilerVersion string      `json:"compilerVersion"` | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 	CompilerOptions string      `json:"compilerOptions"` | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	AbiDefinition   interface{} `json:"abiDefinition"` | 
					
						
							|  |  |  | 	UserDoc         interface{} `json:"userDoc"` | 
					
						
							|  |  |  | 	DeveloperDoc    interface{} `json:"developerDoc"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Solidity struct { | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 	solcPath    string | 
					
						
							|  |  |  | 	version     string | 
					
						
							|  |  |  | 	fullVersion string | 
					
						
							|  |  |  | 	legacy      bool | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func New(solcPath string) (sol *Solidity, err error) { | 
					
						
							|  |  |  | 	// set default solc | 
					
						
							|  |  |  | 	if len(solcPath) == 0 { | 
					
						
							|  |  |  | 		solcPath = "solc" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	solcPath, err = exec.LookPath(solcPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd := exec.Command(solcPath, "--version") | 
					
						
							|  |  |  | 	var out bytes.Buffer | 
					
						
							|  |  |  | 	cmd.Stdout = &out | 
					
						
							|  |  |  | 	err = cmd.Run() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 	fullVersion := out.String() | 
					
						
							| 
									
										
										
										
											2015-09-15 00:35:22 +02:00
										 |  |  | 	version := versionRegexp.FindString(fullVersion) | 
					
						
							|  |  |  | 	legacy := legacyRegexp.MatchString(version) | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	sol = &Solidity{ | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 		solcPath:    solcPath, | 
					
						
							|  |  |  | 		version:     version, | 
					
						
							|  |  |  | 		fullVersion: fullVersion, | 
					
						
							|  |  |  | 		legacy:      legacy, | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	glog.V(logger.Info).Infoln(sol.Info()) | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (sol *Solidity) Info() string { | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 	return fmt.Sprintf("%s\npath: %s", sol.fullVersion, sol.solcPath) | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-10 13:46:38 +02:00
										 |  |  | func (sol *Solidity) Version() string { | 
					
						
							|  |  |  | 	return sol.version | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | // Compile builds and returns all the contracts contained within a source string. | 
					
						
							|  |  |  | func (sol *Solidity) Compile(source string) (map[string]*Contract, error) { | 
					
						
							|  |  |  | 	// Short circuit if no source code was specified | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	if len(source) == 0 { | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 		return nil, errors.New("solc: empty source string") | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 	// Create a safe place to dump compilation output | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	wd, err := ioutil.TempDir("", "solc") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 		return nil, fmt.Errorf("solc: failed to create temporary build folder: %v", err) | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	defer os.RemoveAll(wd) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 	// Assemble the compiler command, change to the temp folder and capture any errors | 
					
						
							|  |  |  | 	stderr := new(bytes.Buffer) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 	var params []string | 
					
						
							|  |  |  | 	if sol.legacy { | 
					
						
							|  |  |  | 		params = paramsLegacy | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		params = paramsNew | 
					
						
							|  |  |  | 		params = append(params, wd) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	compilerOptions := strings.Join(params, " ") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	cmd := exec.Command(sol.solcPath, params...) | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 	cmd.Stdin = strings.NewReader(source) | 
					
						
							|  |  |  | 	cmd.Stderr = stderr | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 	if err := cmd.Run(); err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("solc: %v\n%s", err, string(stderr.Bytes())) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Sanity check that something was actually built | 
					
						
							| 
									
										
										
										
											2016-03-04 14:50:56 -05:00
										 |  |  | 	matches, _ := filepath.Glob(filepath.Join(wd, "*.bin*")) | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	if len(matches) < 1 { | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 		return nil, fmt.Errorf("solc: no build results found") | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 	// Compilation succeeded, assemble and return the contracts | 
					
						
							|  |  |  | 	contracts := make(map[string]*Contract) | 
					
						
							| 
									
										
										
										
											2015-05-20 04:11:48 +01:00
										 |  |  | 	for _, path := range matches { | 
					
						
							|  |  |  | 		_, file := filepath.Split(path) | 
					
						
							|  |  |  | 		base := strings.Split(file, ".")[0] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 		// Parse the individual compilation results (code binary, ABI definitions, user and dev docs) | 
					
						
							|  |  |  | 		var binary []byte | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 		binext := ".bin" | 
					
						
							|  |  |  | 		if sol.legacy { | 
					
						
							|  |  |  | 			binext = ".binary" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if binary, err = ioutil.ReadFile(filepath.Join(wd, base+binext)); err != nil { | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 			return nil, fmt.Errorf("solc: error reading compiler output for code: %v", err) | 
					
						
							| 
									
										
										
										
											2015-05-20 04:11:48 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 		var abi interface{} | 
					
						
							|  |  |  | 		if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".abi")); err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("solc: error reading abi definition: %v", err) | 
					
						
							|  |  |  | 		} else if err = json.Unmarshal(blob, &abi); err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("solc: error parsing abi definition: %v", err) | 
					
						
							| 
									
										
										
										
											2015-05-20 04:11:48 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 		var userdoc interface{} | 
					
						
							|  |  |  | 		if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".docuser")); err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("solc: error reading user doc: %v", err) | 
					
						
							|  |  |  | 		} else if err = json.Unmarshal(blob, &userdoc); err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("solc: error parsing user doc: %v", err) | 
					
						
							| 
									
										
										
										
											2015-05-20 04:11:48 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 		var devdoc interface{} | 
					
						
							|  |  |  | 		if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".docdev")); err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("solc: error reading dev doc: %v", err) | 
					
						
							|  |  |  | 		} else if err = json.Unmarshal(blob, &devdoc); err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("solc: error parsing dev doc: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Assemble the final contract | 
					
						
							|  |  |  | 		contracts[base] = &Contract{ | 
					
						
							|  |  |  | 			Code: "0x" + string(binary), | 
					
						
							| 
									
										
										
										
											2015-05-20 04:11:48 +01:00
										 |  |  | 			Info: ContractInfo{ | 
					
						
							|  |  |  | 				Source:          source, | 
					
						
							|  |  |  | 				Language:        "Solidity", | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 				LanguageVersion: sol.version, | 
					
						
							| 
									
										
										
										
											2015-05-20 04:11:48 +01:00
										 |  |  | 				CompilerVersion: sol.version, | 
					
						
							| 
									
										
										
										
											2015-09-10 14:12:41 +02:00
										 |  |  | 				CompilerOptions: compilerOptions, | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 				AbiDefinition:   abi, | 
					
						
							|  |  |  | 				UserDoc:         userdoc, | 
					
						
							|  |  |  | 				DeveloperDoc:    devdoc, | 
					
						
							| 
									
										
										
										
											2015-05-20 04:11:48 +01:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-25 19:42:05 +03:00
										 |  |  | 	return contracts, nil | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-23 15:48:33 +01:00
										 |  |  | func SaveInfo(info *ContractInfo, filename string) (contenthash common.Hash, err error) { | 
					
						
							|  |  |  | 	infojson, err := json.Marshal(info) | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-21 18:40:27 +00:00
										 |  |  | 	contenthash = common.BytesToHash(crypto.Keccak256(infojson)) | 
					
						
							| 
									
										
										
										
											2015-06-23 15:48:33 +01:00
										 |  |  | 	err = ioutil.WriteFile(filename, infojson, 0600) | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	return | 
					
						
							|  |  |  | } |