github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/core/chaincode/platforms/golang/hash.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package golang 18 19 import ( 20 "archive/tar" 21 "bytes" 22 "encoding/hex" 23 "errors" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "os/exec" 28 "path/filepath" 29 "strings" 30 "time" 31 32 "github.com/golang/protobuf/proto" 33 "github.com/op/go-logging" 34 "github.com/spf13/viper" 35 36 "github.com/hyperledger/fabric/common/util" 37 ccutil "github.com/hyperledger/fabric/core/chaincode/platforms/util" 38 pb "github.com/hyperledger/fabric/protos/peer" 39 ) 40 41 var logger = logging.MustGetLogger("golang/hash") 42 43 func getCodeFromHTTP(path string) (codegopath string, err error) { 44 codegopath = "" 45 err = nil 46 logger.Debugf("getCodeFromHTTP %s", path) 47 48 // The following could be done with os.Getenv("GOPATH") but we need to change it later so this prepares for that next step 49 env := os.Environ() 50 var origgopath string 51 var gopathenvIndex int 52 for i, v := range env { 53 if strings.Index(v, "GOPATH=") == 0 { 54 p := strings.SplitAfter(v, "GOPATH=") 55 origgopath = p[1] 56 gopathenvIndex = i 57 break 58 } 59 } 60 if origgopath == "" { 61 err = errors.New("GOPATH not defined") 62 return 63 } 64 // Only take the first element of GOPATH 65 gopath := filepath.SplitList(origgopath)[0] 66 67 // Define a new gopath in which to download the code 68 newgopath := filepath.Join(gopath, "_usercode_") 69 70 //ignore errors.. _usercode_ might exist. TempDir will catch any other errors 71 os.Mkdir(newgopath, 0755) 72 73 if codegopath, err = ioutil.TempDir(newgopath, ""); err != nil { 74 err = fmt.Errorf("could not create tmp dir under %s(%s)", newgopath, err) 75 return 76 } 77 78 //go paths can have multiple dirs. We create a GOPATH with two source tree's as follows 79 // 80 // <temporary empty folder to download chaincode source> : <local go path with OBC source> 81 // 82 //This approach has several goodness: 83 // . Go will pick the first path to download user code (which we will delete after processing) 84 // . GO will not download OBC as it is in the second path. GO will use the local OBC for generating chaincode image 85 // . network savings 86 // . more secure 87 // . as we are not downloading OBC, private, password-protected OBC repo's become non-issue 88 89 env[gopathenvIndex] = "GOPATH=" + codegopath + string(os.PathListSeparator) + origgopath 90 91 // Use a 'go get' command to pull the chaincode from the given repo 92 logger.Debugf("go get %s", path) 93 cmd := exec.Command("go", "get", path) 94 cmd.Env = env 95 var out bytes.Buffer 96 cmd.Stdout = &out 97 var errBuf bytes.Buffer 98 cmd.Stderr = &errBuf //capture Stderr and print it on error 99 err = cmd.Start() 100 101 // Create a go routine that will wait for the command to finish 102 done := make(chan error, 1) 103 go func() { 104 done <- cmd.Wait() 105 }() 106 107 select { 108 case <-time.After(time.Duration(viper.GetInt("chaincode.deploytimeout")) * time.Millisecond): 109 // If pulling repos takes too long, we should give up 110 // (This can happen if a repo is private and the git clone asks for credentials) 111 if err = cmd.Process.Kill(); err != nil { 112 err = fmt.Errorf("failed to kill: %s", err) 113 } else { 114 err = errors.New("Getting chaincode took too long") 115 } 116 case err = <-done: 117 // If we're here, the 'go get' command must have finished 118 if err != nil { 119 err = fmt.Errorf("'go get' failed with error: \"%s\"\n%s", err, string(errBuf.Bytes())) 120 } 121 } 122 return 123 } 124 125 func getCodeFromFS(path string) (codegopath string, err error) { 126 logger.Debugf("getCodeFromFS %s", path) 127 gopath := os.Getenv("GOPATH") 128 if gopath == "" { 129 err = errors.New("GOPATH not defined") 130 return 131 } 132 // Only take the first element of GOPATH 133 codegopath = filepath.SplitList(gopath)[0] 134 135 return 136 } 137 138 //collectChaincodeFiles collects chaincode files and generates hashcode for the 139 //package. If path is a HTTP(s) url it downloads the code first. 140 //NOTE: for dev mode, user builds and runs chaincode manually. The name provided 141 //by the user is equivalent to the path. This method will treat the name 142 //as codebytes and compute the hash from it. ie, user cannot run the chaincode 143 //with the same (name, input, args) 144 func collectChaincodeFiles(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, error) { 145 if spec == nil { 146 return "", errors.New("Cannot collect files from nil spec") 147 } 148 149 chaincodeID := spec.ChaincodeId 150 if chaincodeID == nil || chaincodeID.Path == "" { 151 return "", errors.New("Cannot collect files from empty chaincode path") 152 } 153 154 //install will not have inputs and we don't have to collect hash for it 155 var inputbytes []byte 156 157 var err error 158 if spec.Input == nil || len(spec.Input.Args) == 0 { 159 logger.Debugf("not using input for hash computation for %v ", chaincodeID) 160 } else { 161 inputbytes, err = proto.Marshal(spec.Input) 162 if err != nil { 163 return "", fmt.Errorf("Error marshalling constructor: %s", err) 164 } 165 } 166 167 //code root will point to the directory where the code exists 168 //in the case of http it will be a temporary dir that 169 //will have to be deleted 170 var codegopath string 171 172 var ishttp bool 173 defer func() { 174 if ishttp && codegopath != "" { 175 os.RemoveAll(codegopath) 176 } 177 }() 178 179 path := chaincodeID.Path 180 181 var actualcodepath string 182 if strings.HasPrefix(path, "http://") { 183 ishttp = true 184 actualcodepath = path[7:] 185 codegopath, err = getCodeFromHTTP(actualcodepath) 186 } else if strings.HasPrefix(path, "https://") { 187 ishttp = true 188 actualcodepath = path[8:] 189 codegopath, err = getCodeFromHTTP(actualcodepath) 190 } else { 191 actualcodepath = path 192 codegopath, err = getCodeFromFS(path) 193 } 194 195 if err != nil { 196 return "", fmt.Errorf("Error getting code %s", err) 197 } 198 199 tmppath := filepath.Join(codegopath, "src", actualcodepath) 200 if err = ccutil.IsCodeExist(tmppath); err != nil { 201 return "", fmt.Errorf("code does not exist %s", err) 202 } 203 204 hash := []byte{} 205 if inputbytes != nil { 206 hash = util.GenerateHashFromSignature(actualcodepath, inputbytes) 207 } 208 209 hash, err = ccutil.HashFilesInDir(filepath.Join(codegopath, "src"), actualcodepath, hash, tw) 210 if err != nil { 211 return "", fmt.Errorf("Could not get hashcode for %s - %s\n", path, err) 212 } 213 214 return hex.EncodeToString(hash[:]), nil 215 }