From 240b6b4b2a7d7b523a30eb0c4088b75d987d89aa Mon Sep 17 00:00:00 2001 From: Steve Waldman Date: Wed, 25 Jan 2017 14:57:13 -0800 Subject: [PATCH 1/4] Include metadata with solc compiler outputs --- common/compiler/solidity.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index d27bddd9ff..f49f7a6219 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -34,7 +34,7 @@ import ( var ( versionRegexp = regexp.MustCompile(`[0-9]+\.[0-9]+\.[0-9]+`) solcParams = []string{ - "--combined-json", "bin,abi,userdoc,devdoc", + "--combined-json", "bin,abi,userdoc,devdoc,metadata", "--add-std", // include standard lib contracts "--optimize", // code optimizer switched on } @@ -54,6 +54,7 @@ type ContractInfo struct { AbiDefinition interface{} `json:"abiDefinition"` UserDoc interface{} `json:"userDoc"` DeveloperDoc interface{} `json:"developerDoc"` + Metadata interface{} `json:"metadata"` } // Solidity contains information about the solidity compiler. @@ -63,7 +64,7 @@ type Solidity struct { // --combined-output format type solcOutput struct { - Contracts map[string]struct{ Bin, Abi, Devdoc, Userdoc string } + Contracts map[string]struct{ Bin, Abi, Devdoc, Userdoc, Metadata string } Version string } @@ -146,6 +147,10 @@ func runsolc(cmd *exec.Cmd, source string) (map[string]*Contract, error) { if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil { return nil, fmt.Errorf("solc: error reading dev doc: %v", err) } + var metadata interface{} + if err := json.Unmarshal([]byte(info.Metadata), &metadata); err != nil { + return nil, fmt.Errorf("solc: error reading metadata: %v", err) + } contracts[name] = &Contract{ Code: "0x" + info.Bin, Info: ContractInfo{ @@ -157,6 +162,7 @@ func runsolc(cmd *exec.Cmd, source string) (map[string]*Contract, error) { AbiDefinition: abi, UserDoc: userdoc, DeveloperDoc: devdoc, + Metadata: metadata, }, } } From 9b9131604c6832e5ed3ad8c09630806a50088eb1 Mon Sep 17 00:00:00 2001 From: Steve Waldman Date: Fri, 27 Jan 2017 00:47:11 -0800 Subject: [PATCH 2/4] Check for presence of metadata before trying to deserialize it --- common/compiler/solidity.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index f49f7a6219..7d3c58197b 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -148,8 +148,10 @@ func runsolc(cmd *exec.Cmd, source string) (map[string]*Contract, error) { return nil, fmt.Errorf("solc: error reading dev doc: %v", err) } var metadata interface{} - if err := json.Unmarshal([]byte(info.Metadata), &metadata); err != nil { - return nil, fmt.Errorf("solc: error reading metadata: %v", err) + if info.Metadata != "" { + if err := json.Unmarshal([]byte(info.Metadata), &metadata); err != nil { + return nil, fmt.Errorf("solc: error reading metadata: %v", err) + } } contracts[name] = &Contract{ Code: "0x" + info.Bin, From 87a22c400fd3bac8dfd1066ea9031ace8dfd90e5 Mon Sep 17 00:00:00 2001 From: Steve Waldman Date: Sat, 28 Jan 2017 13:23:26 -0800 Subject: [PATCH 3/4] Request solidity metadata only for compiler versions 0.4.7 and higher --- common/compiler/solidity.go | 72 ++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index 7d3c58197b..8d44c1a463 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -25,21 +25,21 @@ import ( "io/ioutil" "os/exec" "regexp" + "strconv" "strings" + "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) var ( - versionRegexp = regexp.MustCompile(`[0-9]+\.[0-9]+\.[0-9]+`) - solcParams = []string{ - "--combined-json", "bin,abi,userdoc,devdoc,metadata", - "--add-std", // include standard lib contracts - "--optimize", // code optimizer switched on - } + versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) + spmu = &sync.Mutex{} // protects access to mutating _solcParams variable (contents are never mutated) + _solcParams = []string{} // zero-len means uninitialized, protected by spmu ) + type Contract struct { Code string `json:"code"` Info ContractInfo `json:"info"` @@ -68,6 +68,58 @@ type solcOutput struct { Version string } +func findSolcParams(solc string) []string { + spmu.Lock() + defer spmu.Unlock() + + if ( len(_solcParams) != 0 ) { + return _solcParams + } else { + recent, _ := atLeast047(solc) // on err, we default to considering the solidity version recent, see atLeast047 + var mbmeta string + if ( recent ) { + mbmeta = ",metadata" + } else { + mbmeta = "" + } + _solcParams = []string{ + "--combined-json", "bin,abi,userdoc,devdoc" + mbmeta, + "--add-std", // include standard lib contracts + "--optimize", // code optimizer switched on + } + return _solcParams + } +} + +func atLeast047(solc string) (bool, error) { + var ( + solidity *Solidity + major int + minor int + patch int + err error + ) + solidity, err = SolidityVersion(solc) + if ( err != nil ) { + return true, err + } + + matches := versionRegexp.FindStringSubmatch( solidity.Version ) + major, err = strconv.Atoi( matches[1] ) + if ( err != nil ) { + return true, err + } + minor, err = strconv.Atoi( matches[2] ) + if ( err != nil ) { + return true, err + } + patch, err = strconv.Atoi( matches[3] ) + if ( err != nil ) { + return true, err + } + return (major > 0 || minor > 4 || patch > 6), nil +} + // SolidityVersion runs solc and parses its version output. func SolidityVersion(solc string) (*Solidity, error) { if solc == "" { @@ -95,10 +147,11 @@ func CompileSolidityString(solc, source string) (map[string]*Contract, error) { if solc == "" { solc = "solc" } + solcParams := findSolcParams(solc) args := append(solcParams, "--") cmd := exec.Command(solc, append(args, "-")...) cmd.Stdin = strings.NewReader(source) - return runsolc(cmd, source) + return runsolc(cmd, solcParams, source) } // CompileSolidity compiles all given Solidity source files. @@ -113,12 +166,13 @@ func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, if solc == "" { solc = "solc" } + solcParams := findSolcParams(solc) args := append(solcParams, "--") cmd := exec.Command(solc, append(args, sourcefiles...)...) - return runsolc(cmd, source) + return runsolc(cmd, solcParams, source) } -func runsolc(cmd *exec.Cmd, source string) (map[string]*Contract, error) { +func runsolc(cmd *exec.Cmd, solcParams []string, source string) (map[string]*Contract, error) { var stderr, stdout bytes.Buffer cmd.Stderr = &stderr cmd.Stdout = &stdout From c10f65a1a97e791d63a6ec24f22ea4a16eb3eefb Mon Sep 17 00:00:00 2001 From: Steve Waldman Date: Sat, 28 Jan 2017 22:19:32 -0800 Subject: [PATCH 4/4] Encode metadata as JSON String, rather than as JSON object Ensures that we can decode to a set of bytes that will be consistent with the swarm hash embedded in the code, without worrying about ambiguities of spacing, ordering, or escaping. --- common/compiler/solidity.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index 8d44c1a463..5e20c5b208 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -54,7 +54,7 @@ type ContractInfo struct { AbiDefinition interface{} `json:"abiDefinition"` UserDoc interface{} `json:"userDoc"` DeveloperDoc interface{} `json:"developerDoc"` - Metadata interface{} `json:"metadata"` + Metadata string `json:"metadata"` } // Solidity contains information about the solidity compiler. @@ -201,9 +201,13 @@ func runsolc(cmd *exec.Cmd, solcParams []string, source string) (map[string]*Con if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil { return nil, fmt.Errorf("solc: error reading dev doc: %v", err) } - var metadata interface{} + var metadata string if info.Metadata != "" { - if err := json.Unmarshal([]byte(info.Metadata), &metadata); err != nil { + jstring, err := json.Marshal( string(info.Metadata) ) + if ( err != nil ) { + return nil, fmt.Errorf("solc: error coercing metadata to string: %v", err) + } + if err := json.Unmarshal(jstring, &metadata); err != nil { return nil, fmt.Errorf("solc: error reading metadata: %v", err) } }