| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of go-ethereum. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // go-ethereum is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // 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. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // go-ethereum is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | package compiler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"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" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2015-05-20 02:15:33 +01:00
										 |  |  | 	// flair           = "Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com> (c) 2014-2015" | 
					
						
							|  |  |  | 	flair           = "" | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	languageVersion = "0" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	versionRegExp = regexp.MustCompile("[0-9]+.[0-9]+.[0-9]+") | 
					
						
							|  |  |  | 	params        = []string{ | 
					
						
							|  |  |  | 		"--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
										 |  |  | 	} | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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"` | 
					
						
							|  |  |  | 	AbiDefinition   interface{} `json:"abiDefinition"` | 
					
						
							|  |  |  | 	UserDoc         interface{} `json:"userDoc"` | 
					
						
							|  |  |  | 	DeveloperDoc    interface{} `json:"developerDoc"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Solidity struct { | 
					
						
							|  |  |  | 	solcPath string | 
					
						
							|  |  |  | 	version  string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	version := versionRegExp.FindString(out.String()) | 
					
						
							|  |  |  | 	sol = &Solidity{ | 
					
						
							|  |  |  | 		solcPath: solcPath, | 
					
						
							|  |  |  | 		version:  version, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	glog.V(logger.Info).Infoln(sol.Info()) | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (sol *Solidity) Info() string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("solc v%s\nSolidity Compiler: %s\n%s", sol.version, sol.solcPath, flair) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-10 13:46:38 +02:00
										 |  |  | func (sol *Solidity) Version() string { | 
					
						
							|  |  |  | 	return sol.version | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-20 04:11:48 +01:00
										 |  |  | func (sol *Solidity) Compile(source string) (contracts map[string]*Contract, err error) { | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if len(source) == 0 { | 
					
						
							|  |  |  | 		err = fmt.Errorf("empty source") | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wd, err := ioutil.TempDir("", "solc") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer os.RemoveAll(wd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	in := strings.NewReader(source) | 
					
						
							|  |  |  | 	var out bytes.Buffer | 
					
						
							|  |  |  | 	// cwd set to temp dir | 
					
						
							|  |  |  | 	cmd := exec.Command(sol.solcPath, params...) | 
					
						
							|  |  |  | 	cmd.Dir = wd | 
					
						
							|  |  |  | 	cmd.Stdin = in | 
					
						
							|  |  |  | 	cmd.Stdout = &out | 
					
						
							|  |  |  | 	err = cmd.Run() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		err = fmt.Errorf("solc error: %v", err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	matches, _ := filepath.Glob(wd + "/*.binary") | 
					
						
							|  |  |  | 	if len(matches) < 1 { | 
					
						
							|  |  |  | 		err = fmt.Errorf("solc error: missing code output") | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-20 04:11:48 +01:00
										 |  |  | 	contracts = make(map[string]*Contract) | 
					
						
							|  |  |  | 	for _, path := range matches { | 
					
						
							|  |  |  | 		_, file := filepath.Split(path) | 
					
						
							|  |  |  | 		base := strings.Split(file, ".")[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		codeFile := filepath.Join(wd, base+".binary") | 
					
						
							|  |  |  | 		abiDefinitionFile := filepath.Join(wd, base+".abi") | 
					
						
							|  |  |  | 		userDocFile := filepath.Join(wd, base+".docuser") | 
					
						
							|  |  |  | 		developerDocFile := filepath.Join(wd, base+".docdev") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var code, abiDefinitionJson, userDocJson, developerDocJson []byte | 
					
						
							|  |  |  | 		code, err = ioutil.ReadFile(codeFile) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			err = fmt.Errorf("error reading compiler output for code: %v", err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		abiDefinitionJson, err = ioutil.ReadFile(abiDefinitionFile) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			err = fmt.Errorf("error reading compiler output for abiDefinition: %v", err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var abiDefinition interface{} | 
					
						
							|  |  |  | 		err = json.Unmarshal(abiDefinitionJson, &abiDefinition) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		userDocJson, err = ioutil.ReadFile(userDocFile) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			err = fmt.Errorf("error reading compiler output for userDoc: %v", err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var userDoc interface{} | 
					
						
							|  |  |  | 		err = json.Unmarshal(userDocJson, &userDoc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		developerDocJson, err = ioutil.ReadFile(developerDocFile) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			err = fmt.Errorf("error reading compiler output for developerDoc: %v", err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var developerDoc interface{} | 
					
						
							|  |  |  | 		err = json.Unmarshal(developerDocJson, &developerDoc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		contract := &Contract{ | 
					
						
							|  |  |  | 			Code: "0x" + string(code), | 
					
						
							|  |  |  | 			Info: ContractInfo{ | 
					
						
							|  |  |  | 				Source:          source, | 
					
						
							|  |  |  | 				Language:        "Solidity", | 
					
						
							|  |  |  | 				LanguageVersion: languageVersion, | 
					
						
							|  |  |  | 				CompilerVersion: sol.version, | 
					
						
							|  |  |  | 				AbiDefinition:   abiDefinition, | 
					
						
							|  |  |  | 				UserDoc:         userDoc, | 
					
						
							|  |  |  | 				DeveloperDoc:    developerDoc, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		contracts[base] = contract | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-23 15:48:33 +01:00
										 |  |  | 	contenthash = common.BytesToHash(crypto.Sha3(infojson)) | 
					
						
							|  |  |  | 	err = ioutil.WriteFile(filename, infojson, 0600) | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	return | 
					
						
							|  |  |  | } |