diff --git a/core/chaincode/platforms/golang/hash.go b/core/chaincode/platforms/golang/hash.go index 383327f7bfb..f00406fa959 100755 --- a/core/chaincode/platforms/golang/hash.go +++ b/core/chaincode/platforms/golang/hash.go @@ -33,88 +33,13 @@ import ( "github.com/op/go-logging" "github.com/spf13/viper" - cutil "github.com/hyperledger/fabric/core/container/util" + ccutil "github.com/hyperledger/fabric/core/chaincode/platforms/util" "github.com/hyperledger/fabric/core/util" pb "github.com/hyperledger/fabric/protos/peer" ) var logger = logging.MustGetLogger("golang/hash") -//core hash computation factored out for testing -func computeHash(contents []byte, hash []byte) []byte { - newSlice := make([]byte, len(hash)+len(contents)) - - //copy the contents - copy(newSlice[0:len(contents)], contents[:]) - - //add the previous hash - copy(newSlice[len(contents):], hash[:]) - - //compute new hash - hash = util.ComputeCryptoHash(newSlice) - - return hash -} - -//hashFilesInDir computes h=hash(h,file bytes) for each file in a directory -//Directory entries are traversed recursively. In the end a single -//hash value is returned for the entire directory structure -func hashFilesInDir(rootDir string, dir string, hash []byte, tw *tar.Writer) ([]byte, error) { - currentDir := filepath.Join(rootDir, dir) - logger.Debugf("hashFiles %s", currentDir) - //ReadDir returns sorted list of files in dir - fis, err := ioutil.ReadDir(currentDir) - if err != nil { - return hash, fmt.Errorf("ReadDir failed %s\n", err) - } - for _, fi := range fis { - name := filepath.Join(dir, fi.Name()) - if fi.IsDir() { - var err error - hash, err = hashFilesInDir(rootDir, name, hash, tw) - if err != nil { - return hash, err - } - continue - } - fqp := filepath.Join(rootDir, name) - buf, err := ioutil.ReadFile(fqp) - if err != nil { - fmt.Printf("Error reading %s\n", err) - return hash, err - } - - //get the new hash from file contents - hash = computeHash(buf, hash) - - if tw != nil { - is := bytes.NewReader(buf) - if err = cutil.WriteStreamToPackage(is, fqp, filepath.Join("src", name), tw); err != nil { - return hash, fmt.Errorf("Error adding file to tar %s", err) - } - } - } - return hash, nil -} - -func isCodeExist(tmppath string) error { - file, err := os.Open(tmppath) - if err != nil { - return fmt.Errorf("Download failed %s", err) - } - - fi, err := file.Stat() - if err != nil { - return fmt.Errorf("Could not stat file %s", err) - } - - if !fi.IsDir() { - return fmt.Errorf("File %s is not dir\n", file.Name()) - } - - return nil -} - func getCodeFromHTTP(path string) (codegopath string, err error) { codegopath = "" err = nil @@ -265,7 +190,7 @@ func collectChaincodeFiles(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, erro } tmppath := filepath.Join(codegopath, "src", actualcodepath) - if err = isCodeExist(tmppath); err != nil { + if err = ccutil.IsCodeExist(tmppath); err != nil { return "", fmt.Errorf("code does not exist %s", err) } ctorbytes, err := proto.Marshal(ctor) @@ -274,7 +199,7 @@ func collectChaincodeFiles(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, erro } hash := util.GenerateHashFromSignature(actualcodepath, ctorbytes) - hash, err = hashFilesInDir(filepath.Join(codegopath, "src"), actualcodepath, hash, tw) + hash, err = ccutil.HashFilesInDir(filepath.Join(codegopath, "src"), actualcodepath, hash, tw) if err != nil { return "", fmt.Errorf("Could not get hashcode for %s - %s\n", path, err) } diff --git a/core/chaincode/platforms/java/hash.go b/core/chaincode/platforms/java/hash.go index 369ddf448e4..40d1638dfe0 100644 --- a/core/chaincode/platforms/java/hash.go +++ b/core/chaincode/platforms/java/hash.go @@ -27,66 +27,11 @@ import ( "strings" "github.com/golang/protobuf/proto" - cutil "github.com/hyperledger/fabric/core/container/util" + ccutil "github.com/hyperledger/fabric/core/chaincode/platforms/util" "github.com/hyperledger/fabric/core/util" pb "github.com/hyperledger/fabric/protos/peer" ) -//hashFilesInDir computes h=hash(h,file bytes) for each file in a directory -//Directory entries are traversed recursively. In the end a single -//hash value is returned for the entire directory structure -func hashFilesInDir(cutoff string, dir string, hash []byte, tw *tar.Writer) ([]byte, error) { - //ReadDir returns sorted list of files in dir - fis, err := ioutil.ReadDir(dir) - if err != nil { - return hash, fmt.Errorf("ReadDir failed %s\n", err) - } - for _, fi := range fis { - name := fmt.Sprintf("%s/%s", dir, fi.Name()) - if fi.IsDir() { - var err error - hash, err = hashFilesInDir(cutoff, name, hash, tw) - if err != nil { - return hash, err - } - continue - } - buf, err := ioutil.ReadFile(name) - if err != nil { - fmt.Printf("Error reading %s\n", err) - return hash, err - } - - newSlice := make([]byte, len(hash)+len(buf)) - copy(newSlice[len(buf):], hash[:]) - //hash = md5.Sum(newSlice) - hash = util.ComputeCryptoHash(newSlice) - - if tw != nil { - is := bytes.NewReader(buf) - if err = cutil.WriteStreamToPackage(is, name, name[len(cutoff):], tw); err != nil { - return hash, fmt.Errorf("Error adding file to tar %s", err) - } - } - } - return hash, nil -} - -func isCodeExist(tmppath string) error { - file, err := os.Open(tmppath) - if err != nil { - return fmt.Errorf("Download failer %s", err) - } - fi, err := file.Stat() - if err != nil { - return fmt.Errorf("could not stat file %s", err) - } - if !fi.IsDir() { - return fmt.Errorf("file %s is not dir\n", file.Name()) - } - return nil -} - func getCodeFromHTTP(path string) (codegopath string, err error) { var tmp string @@ -153,22 +98,17 @@ func collectChaincodeFiles(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, erro return "", fmt.Errorf("Error getting code %s", err) } - if err = isCodeExist(codepath); err != nil { + if err = ccutil.IsCodeExist(codepath); err != nil { return "", fmt.Errorf("code does not exist %s", err) } - root := codepath - if strings.LastIndex(root, "/") == len(root)-1 { - root = root[:len(root)-1] - } - root = root[:strings.LastIndex(root, "/")+1] ctorbytes, err := proto.Marshal(ctor) if err != nil { return "", fmt.Errorf("Error marshalling constructor: %s", err) } hash := util.GenerateHashFromSignature(codepath, ctorbytes) - hash, err = hashFilesInDir(root, codepath, hash, tw) + hash, err = ccutil.HashFilesInDir("", codepath, hash, tw) if err != nil { return "", fmt.Errorf("Could not get hashcode for %s - %s\n", codepath, err) } diff --git a/core/chaincode/platforms/java/hash_test.go b/core/chaincode/platforms/java/hash_test.go deleted file mode 100644 index 67c05778dbd..00000000000 --- a/core/chaincode/platforms/java/hash_test.go +++ /dev/null @@ -1,124 +0,0 @@ -/* -Copyright IBM Corp. 2016 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package java - -import ( - "testing" - - "encoding/hex" - - "github.com/hyperledger/fabric/core/util" - - "bytes" - "os" -) - -func TestHashDiffRemoteRepo(t *testing.T) { - b := []byte("firstcontent") - hash := util.ComputeCryptoHash(b) - - srcPath1, err := getCodeFromHTTP("https://github.com/hyperledger/fabric-test-resources/") - if err != nil { - t.Logf("Error getting code from remote repo %s", err) - t.Fail() - } - srcPath2, err := getCodeFromHTTP("https://github.com/hyperledger/fabric-sdk-java") - if err != nil { - t.Logf("Error getting code from remote repo %s", err) - t.Fail() - } - - defer func() { - os.RemoveAll(srcPath1) - }() - defer func() { - os.RemoveAll(srcPath2) - }() - hash1, err := hashFilesInDir(srcPath1, srcPath1, hash, nil) - if err != nil { - t.Logf("Error getting code %s", err) - t.Fail() - } - hash2, err := hashFilesInDir(srcPath2, srcPath2, hash, nil) - if err != nil { - t.Logf("Error getting code %s", err) - t.Fail() - } - if bytes.Compare(hash1, hash2) == 0 { - t.Logf("Hash should be different for 2 different remote repos") - t.Fail() - } - -} -func TestHashSameRemoteRepo(t *testing.T) { - b := []byte("firstcontent") - hash := util.ComputeCryptoHash(b) - - srcPath1, err := getCodeFromHTTP("https://github.com/hyperledger/fabric-test-resources") - if err != nil { - t.Logf("Error getting code from remote repo %s", err) - t.Fail() - } - srcPath2, err := getCodeFromHTTP("https://github.com/hyperledger/fabric-test-resources") - - if err != nil { - t.Logf("Error getting code from remote repo %s", err) - t.Fail() - } - - defer func() { - os.RemoveAll(srcPath1) - }() - defer func() { - os.RemoveAll(srcPath2) - }() - hash1, err := hashFilesInDir(srcPath1, srcPath1, hash, nil) - if err != nil { - t.Logf("Error getting code %s", err) - t.Fail() - } - hash2, err := hashFilesInDir(srcPath2, srcPath2, hash, nil) - if err != nil { - t.Logf("Error getting code %s", err) - t.Fail() - } - if bytes.Compare(hash1, hash2) != 0 { - t.Logf("Hash should be same across multiple downloads") - t.Fail() - } -} - -func TestHashOverLocalDir(t *testing.T) { - b := []byte("firstcontent") - hash := util.ComputeCryptoHash(b) - - hash, err := hashFilesInDir(".", "../golang/hashtestfiles", hash, nil) - - if err != nil { - t.Fail() - t.Logf("error : %s", err) - } - - expectedHash := "7b3b2193bed2bd7c19300aa5d6d7f6bb4d61602e4978a78bc08028379cb5cf0ed877bd9db3e990230e8bf6c974edd765f3027f061fd8657d30fc858a676a6f4a" - - computedHash := hex.EncodeToString(hash[:]) - - if expectedHash != computedHash { - t.Fail() - t.Logf("Hash expected to be unchanged") - } -} diff --git a/core/chaincode/platforms/java/platform.go b/core/chaincode/platforms/java/platform.go index 1db2c923151..eae3a1bae38 100644 --- a/core/chaincode/platforms/java/platform.go +++ b/core/chaincode/platforms/java/platform.go @@ -20,7 +20,6 @@ import ( "archive/tar" "fmt" "net/url" - "os" pb "github.com/hyperledger/fabric/protos/peer" // "path/filepath" @@ -30,18 +29,6 @@ import ( type Platform struct { } -// Returns whether the given file or directory exists or not -func pathExists(path string) (bool, error) { - _, err := os.Stat(path) - if err == nil { - return true, nil - } - if os.IsNotExist(err) { - return false, nil - } - return true, err -} - //ValidateSpec validates the java chaincode specs func (javaPlatform *Platform) ValidateSpec(spec *pb.ChaincodeSpec) error { url, err := url.Parse(spec.ChaincodeID.Path) diff --git a/core/chaincode/platforms/golang/hashtestfiles/a.txt b/core/chaincode/platforms/util/hashtestfiles1/a.txt similarity index 100% rename from core/chaincode/platforms/golang/hashtestfiles/a.txt rename to core/chaincode/platforms/util/hashtestfiles1/a.txt diff --git a/core/chaincode/platforms/golang/hashtestfiles/a/a1.txt b/core/chaincode/platforms/util/hashtestfiles1/a/a1.txt similarity index 100% rename from core/chaincode/platforms/golang/hashtestfiles/a/a1.txt rename to core/chaincode/platforms/util/hashtestfiles1/a/a1.txt diff --git a/core/chaincode/platforms/golang/hashtestfiles/a/a2.txt b/core/chaincode/platforms/util/hashtestfiles1/a/a2.txt similarity index 100% rename from core/chaincode/platforms/golang/hashtestfiles/a/a2.txt rename to core/chaincode/platforms/util/hashtestfiles1/a/a2.txt diff --git a/core/chaincode/platforms/golang/hashtestfiles/b.txt b/core/chaincode/platforms/util/hashtestfiles1/b.txt similarity index 100% rename from core/chaincode/platforms/golang/hashtestfiles/b.txt rename to core/chaincode/platforms/util/hashtestfiles1/b.txt diff --git a/core/chaincode/platforms/golang/hashtestfiles/b/c.txt b/core/chaincode/platforms/util/hashtestfiles1/b/c.txt similarity index 100% rename from core/chaincode/platforms/golang/hashtestfiles/b/c.txt rename to core/chaincode/platforms/util/hashtestfiles1/b/c.txt diff --git a/core/chaincode/platforms/golang/hashtestfiles/b/c/c1.txt b/core/chaincode/platforms/util/hashtestfiles1/b/c/c1.txt similarity index 100% rename from core/chaincode/platforms/golang/hashtestfiles/b/c/c1.txt rename to core/chaincode/platforms/util/hashtestfiles1/b/c/c1.txt diff --git a/core/chaincode/platforms/util/hashtestfiles2/x.txt b/core/chaincode/platforms/util/hashtestfiles2/x.txt new file mode 100644 index 00000000000..587be6b4c3f --- /dev/null +++ b/core/chaincode/platforms/util/hashtestfiles2/x.txt @@ -0,0 +1 @@ +x diff --git a/core/chaincode/platforms/util/hashtestfiles2/x/z.txt b/core/chaincode/platforms/util/hashtestfiles2/x/z.txt new file mode 100644 index 00000000000..b68025345d5 --- /dev/null +++ b/core/chaincode/platforms/util/hashtestfiles2/x/z.txt @@ -0,0 +1 @@ +z diff --git a/core/chaincode/platforms/util/hashtestfiles2/x/z/z1.txt b/core/chaincode/platforms/util/hashtestfiles2/x/z/z1.txt new file mode 100644 index 00000000000..57eb3346c0a --- /dev/null +++ b/core/chaincode/platforms/util/hashtestfiles2/x/z/z1.txt @@ -0,0 +1 @@ +z1 diff --git a/core/chaincode/platforms/util/hashtestfiles2/y.txt b/core/chaincode/platforms/util/hashtestfiles2/y.txt new file mode 100644 index 00000000000..975fbec8256 --- /dev/null +++ b/core/chaincode/platforms/util/hashtestfiles2/y.txt @@ -0,0 +1 @@ +y diff --git a/core/chaincode/platforms/util/hashtestfiles2/y/y1.txt b/core/chaincode/platforms/util/hashtestfiles2/y/y1.txt new file mode 100644 index 00000000000..656f4f9dcc6 --- /dev/null +++ b/core/chaincode/platforms/util/hashtestfiles2/y/y1.txt @@ -0,0 +1 @@ +y1 diff --git a/core/chaincode/platforms/util/hashtestfiles2/y/y2.txt b/core/chaincode/platforms/util/hashtestfiles2/y/y2.txt new file mode 100644 index 00000000000..1a78173cc87 --- /dev/null +++ b/core/chaincode/platforms/util/hashtestfiles2/y/y2.txt @@ -0,0 +1 @@ +y2 diff --git a/core/chaincode/platforms/util/utils.go b/core/chaincode/platforms/util/utils.go new file mode 100644 index 00000000000..c2f3c2afaa1 --- /dev/null +++ b/core/chaincode/platforms/util/utils.go @@ -0,0 +1,92 @@ +package util + +import ( + "archive/tar" + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + cutil "github.com/hyperledger/fabric/core/container/util" + "github.com/hyperledger/fabric/core/util" + "github.com/op/go-logging" +) + +var logger = logging.MustGetLogger("util") + +//ComputeHash computes contents hash based on previous hash +func ComputeHash(contents []byte, hash []byte) []byte { + newSlice := make([]byte, len(hash)+len(contents)) + + //copy the contents + copy(newSlice[0:len(contents)], contents[:]) + + //add the previous hash + copy(newSlice[len(contents):], hash[:]) + + //compute new hash + hash = util.ComputeCryptoHash(newSlice) + + return hash +} + +//HashFilesInDir computes h=hash(h,file bytes) for each file in a directory +//Directory entries are traversed recursively. In the end a single +//hash value is returned for the entire directory structure +func HashFilesInDir(rootDir string, dir string, hash []byte, tw *tar.Writer) ([]byte, error) { + currentDir := filepath.Join(rootDir, dir) + logger.Debugf("hashFiles %s", currentDir) + //ReadDir returns sorted list of files in dir + fis, err := ioutil.ReadDir(currentDir) + if err != nil { + return hash, fmt.Errorf("ReadDir failed %s\n", err) + } + for _, fi := range fis { + name := filepath.Join(dir, fi.Name()) + if fi.IsDir() { + var err error + hash, err = HashFilesInDir(rootDir, name, hash, tw) + if err != nil { + return hash, err + } + continue + } + fqp := filepath.Join(rootDir, name) + buf, err := ioutil.ReadFile(fqp) + if err != nil { + logger.Errorf("Error reading %s\n", err) + return hash, err + } + + //get the new hash from file contents + hash = ComputeHash(buf, hash) + + if tw != nil { + is := bytes.NewReader(buf) + if err = cutil.WriteStreamToPackage(is, fqp, filepath.Join("src", name), tw); err != nil { + return hash, fmt.Errorf("Error adding file to tar %s", err) + } + } + } + return hash, nil +} + +//IsCodeExist checks the chaincode if exists +func IsCodeExist(tmppath string) error { + file, err := os.Open(tmppath) + if err != nil { + return fmt.Errorf("Could not open file %s", err) + } + + fi, err := file.Stat() + if err != nil { + return fmt.Errorf("Could not stat file %s", err) + } + + if !fi.IsDir() { + return fmt.Errorf("File %s is not dir\n", file.Name()) + } + + return nil +} diff --git a/core/chaincode/platforms/golang/hash_test.go b/core/chaincode/platforms/util/utils_test.go similarity index 69% rename from core/chaincode/platforms/golang/hash_test.go rename to core/chaincode/platforms/util/utils_test.go index 0c49157944c..a7cfb4b400b 100644 --- a/core/chaincode/platforms/golang/hash_test.go +++ b/core/chaincode/platforms/util/utils_test.go @@ -1,20 +1,4 @@ -/* -Copyright IBM Corp. 2016 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package golang +package util import ( "bytes" @@ -33,7 +17,7 @@ func TestHashContentChange(t *testing.T) { b2 := []byte("To be, or not to be- that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune Or to take arms against a sea of troubles, And by opposing end them. To die- to sleep- No more; and by a sleep to say we end The heartache, and the thousand natural shocks That flesh is heir to. 'Tis a consummation Devoutly to be wish'd.") - h1 := computeHash(b2, hash) + h1 := ComputeHash(b2, hash) r := rand.New(rand.NewSource(time.Now().UnixNano())) randIndex := (int(r.Uint32())) % len(b2) @@ -53,12 +37,11 @@ func TestHashContentChange(t *testing.T) { b2[randIndex] = randByte //this is the core hash func under test - h2 := computeHash(b2, hash) + h2 := ComputeHash(b2, hash) //the two hashes should be different if bytes.Compare(h1, h2) == 0 { - t.Fail() - t.Logf("Hash expected to be different but is same") + t.Error("Hash expected to be different but is same") } } @@ -69,19 +52,18 @@ func TestHashLenChange(t *testing.T) { b2 := []byte("To be, or not to be-") - h1 := computeHash(b2, hash) + h1 := ComputeHash(b2, hash) r := rand.New(rand.NewSource(time.Now().UnixNano())) randIndex := (int(r.Uint32())) % len(b2) b2 = b2[0:randIndex] - h2 := computeHash(b2, hash) + h2 := ComputeHash(b2, hash) //hash should be different if bytes.Compare(h1, h2) == 0 { - t.Fail() - t.Logf("Hash expected to be different but is same") + t.Error("Hash expected to be different but is same") } } @@ -102,7 +84,7 @@ func TestHashOrderChange(t *testing.T) { h1 := hash for _, l := range b2 { - h1 = computeHash(l, h1) + h1 = ComputeHash(l, h1) } r := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -125,13 +107,12 @@ func TestHashOrderChange(t *testing.T) { h2 := hash for _, l := range b2 { - h2 = computeHash(l, hash) + h2 = ComputeHash(l, hash) } //hash should be different if bytes.Compare(h1, h2) == 0 { - t.Fail() - t.Logf("Hash expected to be different but is same") + t.Error("Hash expected to be different but is same") } } @@ -140,20 +121,53 @@ func TestHashOverFiles(t *testing.T) { b := []byte("firstcontent") hash := util.ComputeCryptoHash(b) - hash, err := hashFilesInDir(".", "hashtestfiles", hash, nil) + hash, err := HashFilesInDir(".", "hashtestfiles1", hash, nil) if err != nil { t.Fail() t.Logf("error : %s", err) } - //as long as no files under "hashtestfiles" are changed, hash should always compute to the following + //as long as no files under "hashtestfiles1" are changed, hash should always compute to the following expectedHash := "a4fe18bebf3d7e1c030c042903bdda9019b33829d03d9b95ab1edc8957be70dee6d786ab27b207210d29b5d9f88456ff753b8da5c244458cdcca6eb3c28a17ce" computedHash := hex.EncodeToString(hash[:]) if expectedHash != computedHash { - t.Fail() - t.Logf("Hash expected to be unchanged") + t.Error("Hash expected to be unchanged") + } +} + +func TestHashDiffDir(t *testing.T) { + b := []byte("firstcontent") + hash := util.ComputeCryptoHash(b) + + hash1, err := HashFilesInDir(".", "hashtestfiles1", hash, nil) + if err != nil { + t.Errorf("Error getting code %s", err) + } + hash2, err := HashFilesInDir(".", "hashtestfiles2", hash, nil) + if err != nil { + t.Errorf("Error getting code %s", err) + } + if bytes.Compare(hash1, hash2) == 0 { + t.Error("Hash should be different for 2 different remote repos") + } + +} +func TestHashSameDir(t *testing.T) { + b := []byte("firstcontent") + hash := util.ComputeCryptoHash(b) + + hash1, err := HashFilesInDir(".", "hashtestfiles1", hash, nil) + if err != nil { + t.Errorf("Error getting code %s", err) + } + hash2, err := HashFilesInDir(".", "hashtestfiles1", hash, nil) + if err != nil { + t.Errorf("Error getting code %s", err) + } + if bytes.Compare(hash1, hash2) != 0 { + t.Error("Hash should be same across multiple downloads") } }