github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/core/chaincode/platforms/util/utils_test.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 util
    18  
    19  import (
    20  	"archive/tar"
    21  	"bytes"
    22  	"compress/gzip"
    23  	"encoding/hex"
    24  	"fmt"
    25  	"io"
    26  	"io/ioutil"
    27  	"math/rand"
    28  	"os"
    29  	"strings"
    30  	"testing"
    31  	"time"
    32  
    33  	"github.com/hyperledger/fabric/common/util"
    34  	"github.com/hyperledger/fabric/core/config"
    35  	cutil "github.com/hyperledger/fabric/core/container/util"
    36  	"github.com/spf13/viper"
    37  	"github.com/stretchr/testify/assert"
    38  )
    39  
    40  // TestHashContentChange changes a random byte in a content and checks for hash change
    41  func TestHashContentChange(t *testing.T) {
    42  	b := []byte("firstcontent")
    43  	hash := util.ComputeSHA256(b)
    44  
    45  	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.")
    46  
    47  	h1 := ComputeHash(b2, hash)
    48  
    49  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
    50  	randIndex := (int(r.Uint32())) % len(b2)
    51  
    52  	randByte := byte((int(r.Uint32())) % 128)
    53  
    54  	//make sure the two bytes are different
    55  	for {
    56  		if randByte != b2[randIndex] {
    57  			break
    58  		}
    59  
    60  		randByte = byte((int(r.Uint32())) % 128)
    61  	}
    62  
    63  	//change a random byte
    64  	b2[randIndex] = randByte
    65  
    66  	//this is the core hash func under test
    67  	h2 := ComputeHash(b2, hash)
    68  
    69  	//the two hashes should be different
    70  	if bytes.Compare(h1, h2) == 0 {
    71  		t.Error("Hash expected to be different but is same")
    72  	}
    73  }
    74  
    75  // TestHashLenChange changes a random length of a content and checks for hash change
    76  func TestHashLenChange(t *testing.T) {
    77  	b := []byte("firstcontent")
    78  	hash := util.ComputeSHA256(b)
    79  
    80  	b2 := []byte("To be, or not to be-")
    81  
    82  	h1 := ComputeHash(b2, hash)
    83  
    84  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
    85  	randIndex := (int(r.Uint32())) % len(b2)
    86  
    87  	b2 = b2[0:randIndex]
    88  
    89  	h2 := ComputeHash(b2, hash)
    90  
    91  	//hash should be different
    92  	if bytes.Compare(h1, h2) == 0 {
    93  		t.Error("Hash expected to be different but is same")
    94  	}
    95  }
    96  
    97  // TestHashOrderChange changes a order of hash computation over a list of lines and checks for hash change
    98  func TestHashOrderChange(t *testing.T) {
    99  	b := []byte("firstcontent")
   100  	hash := util.ComputeSHA256(b)
   101  
   102  	b2 := [][]byte{[]byte("To be, or not to be- that is the question:"),
   103  		[]byte("Whether 'tis nobler in the mind to suffer"),
   104  		[]byte("The slings and arrows of outrageous fortune"),
   105  		[]byte("Or to take arms against a sea of troubles,"),
   106  		[]byte("And by opposing end them."),
   107  		[]byte("To die- to sleep- No more; and by a sleep to say we end"),
   108  		[]byte("The heartache, and the thousand natural shocks"),
   109  		[]byte("That flesh is heir to."),
   110  		[]byte("'Tis a consummation Devoutly to be wish'd.")}
   111  	h1 := hash
   112  
   113  	for _, l := range b2 {
   114  		h1 = ComputeHash(l, h1)
   115  	}
   116  
   117  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   118  	randIndex1 := (int(r.Uint32())) % len(b2)
   119  	randIndex2 := (int(r.Uint32())) % len(b2)
   120  
   121  	//make sure the two indeces are different
   122  	for {
   123  		if randIndex2 != randIndex1 {
   124  			break
   125  		}
   126  
   127  		randIndex2 = (int(r.Uint32())) % len(b2)
   128  	}
   129  
   130  	//switch two arbitrary lines
   131  	tmp := b2[randIndex2]
   132  	b2[randIndex2] = b2[randIndex1]
   133  	b2[randIndex1] = tmp
   134  
   135  	h2 := hash
   136  	for _, l := range b2 {
   137  		h2 = ComputeHash(l, hash)
   138  	}
   139  
   140  	//hash should be different
   141  	if bytes.Compare(h1, h2) == 0 {
   142  		t.Error("Hash expected to be different but is same")
   143  	}
   144  }
   145  
   146  // TestHashOverFiles computes hash over a directory and ensures it matches precomputed, hardcoded, hash
   147  func TestHashOverFiles(t *testing.T) {
   148  	b := []byte("firstcontent")
   149  	hash := util.ComputeSHA256(b)
   150  
   151  	hash, err := HashFilesInDir(".", "hashtestfiles1", hash, nil)
   152  
   153  	if err != nil {
   154  		t.Fail()
   155  		t.Logf("error : %s", err)
   156  	}
   157  
   158  	//as long as no files under "hashtestfiles1" are changed, hash should always compute to the following
   159  	expectedHash := "0c92180028200dfabd08d606419737f5cdecfcbab403e3f0d79e8d949f4775bc"
   160  
   161  	computedHash := hex.EncodeToString(hash[:])
   162  
   163  	if expectedHash != computedHash {
   164  		t.Error("Hash expected to be unchanged")
   165  	}
   166  }
   167  
   168  func TestHashDiffDir(t *testing.T) {
   169  	b := []byte("firstcontent")
   170  	hash := util.ComputeSHA256(b)
   171  
   172  	hash1, err := HashFilesInDir(".", "hashtestfiles1", hash, nil)
   173  	if err != nil {
   174  		t.Errorf("Error getting code %s", err)
   175  	}
   176  	hash2, err := HashFilesInDir(".", "hashtestfiles2", hash, nil)
   177  	if err != nil {
   178  		t.Errorf("Error getting code %s", err)
   179  	}
   180  	if bytes.Compare(hash1, hash2) == 0 {
   181  		t.Error("Hash should be different for 2 different remote repos")
   182  	}
   183  }
   184  
   185  func TestHashSameDir(t *testing.T) {
   186  	assert := assert.New(t)
   187  
   188  	b := []byte("firstcontent")
   189  	hash := util.ComputeSHA256(b)
   190  	hash1, err := HashFilesInDir(".", "hashtestfiles1", hash, nil)
   191  	assert.NoError(err, "Error getting code")
   192  
   193  	fname := os.TempDir() + "/hash.tar"
   194  	w, err := os.Create(fname)
   195  	if err != nil {
   196  		t.Fatal(err)
   197  	}
   198  	defer os.Remove(fname)
   199  	tw := tar.NewWriter(w)
   200  	defer w.Close()
   201  	defer tw.Close()
   202  	hash2, err := HashFilesInDir(".", "hashtestfiles1", hash, tw)
   203  	assert.NoError(err, "Error getting code")
   204  
   205  	assert.Equal(bytes.Compare(hash1, hash2), 0,
   206  		"Hash should be same across multiple downloads")
   207  }
   208  
   209  func TestHashBadWriter(t *testing.T) {
   210  	b := []byte("firstcontent")
   211  	hash := util.ComputeSHA256(b)
   212  
   213  	fname := os.TempDir() + "/hash.tar"
   214  	w, err := os.Create(fname)
   215  	if err != nil {
   216  		t.Fatal(err)
   217  	}
   218  	defer os.Remove(fname)
   219  	tw := tar.NewWriter(w)
   220  	defer w.Close()
   221  	tw.Close()
   222  
   223  	_, err = HashFilesInDir(".", "hashtestfiles1", hash, tw)
   224  	assert.Error(t, err,
   225  		"HashFilesInDir invoked with closed writer, should have failed")
   226  }
   227  
   228  // TestHashNonExistentDir tests HashFilesInDir with non existant directory
   229  func TestHashNonExistentDir(t *testing.T) {
   230  	b := []byte("firstcontent")
   231  	hash := util.ComputeSHA256(b)
   232  	_, err := HashFilesInDir(".", "idontexist", hash, nil)
   233  	assert.Error(t, err, "Expected an error for non existent directory %s", "idontexist")
   234  }
   235  
   236  // TestIsCodeExist tests isCodeExist function
   237  func TestIsCodeExist(t *testing.T) {
   238  	assert := assert.New(t)
   239  	path := os.TempDir()
   240  	err := IsCodeExist(path)
   241  	assert.NoError(err,
   242  		"%s directory exists, IsCodeExist should not have returned error: %v",
   243  		path, err)
   244  
   245  	dir, err := ioutil.TempDir(os.TempDir(), "iscodeexist")
   246  	assert.NoError(err)
   247  	defer os.RemoveAll(dir)
   248  	path = dir + "/blah"
   249  	err = IsCodeExist(path)
   250  	assert.Error(err,
   251  		"%s directory does not exist, IsCodeExist should have returned error", path)
   252  
   253  	f := createTempFile(t)
   254  	defer os.Remove(f)
   255  	err = IsCodeExist(f)
   256  	assert.Error(err, "%s is a file, IsCodeExist should have returned error", f)
   257  }
   258  
   259  // TestDockerBuild tests DockerBuild function
   260  func TestDockerBuild(t *testing.T) {
   261  	assert := assert.New(t)
   262  	var err error
   263  
   264  	ldflags := "-linkmode external -extldflags '-static'"
   265  	codepackage := bytes.NewReader(getDeploymentPayload())
   266  	binpackage := bytes.NewBuffer(nil)
   267  	if err != nil {
   268  		t.Fatal(err)
   269  	}
   270  	err = DockerBuild(DockerBuildOptions{
   271  		Cmd: fmt.Sprintf("GOPATH=/chaincode/input:$GOPATH go build -ldflags \"%s\" -o /chaincode/output/chaincode helloworld",
   272  			ldflags),
   273  		InputStream:  codepackage,
   274  		OutputStream: binpackage,
   275  	})
   276  	assert.NoError(err, "DockerBuild failed")
   277  }
   278  
   279  func getDeploymentPayload() []byte {
   280  	var goprog = `
   281  	package main
   282  	import "fmt"
   283  	func main() {
   284  		fmt.Println("Hello World")
   285  	}
   286  	`
   287  	var zeroTime time.Time
   288  	payload := bytes.NewBufferString(goprog).Bytes()
   289  	inputbuf := bytes.NewBuffer(nil)
   290  	gw := gzip.NewWriter(inputbuf)
   291  	tw := tar.NewWriter(gw)
   292  	tw.WriteHeader(&tar.Header{
   293  		Name:       "src/helloworld/helloworld.go",
   294  		Size:       int64(len(payload)),
   295  		Mode:       0600,
   296  		ModTime:    zeroTime,
   297  		AccessTime: zeroTime,
   298  		ChangeTime: zeroTime,
   299  	})
   300  	tw.Write(payload)
   301  	tw.Close()
   302  	gw.Close()
   303  	return inputbuf.Bytes()
   304  }
   305  
   306  func createTempFile(t *testing.T) string {
   307  	tmpfile, err := ioutil.TempFile("", "test")
   308  	if err != nil {
   309  		t.Fatal(err)
   310  		return ""
   311  	}
   312  	if err := tmpfile.Close(); err != nil {
   313  		t.Fatal(err)
   314  	}
   315  	return tmpfile.Name()
   316  }
   317  
   318  func TestDockerPull(t *testing.T) {
   319  	codepackage, output := io.Pipe()
   320  	go func() {
   321  		tw := tar.NewWriter(output)
   322  
   323  		tw.Close()
   324  		output.Close()
   325  	}()
   326  
   327  	binpackage := bytes.NewBuffer(nil)
   328  
   329  	// Perform a nop operation within a fixed target.  We choose 1.0.0-alpha2 because we know it's
   330  	// published and available.  Ideally we could choose something that we know is both multi-arch
   331  	// and ok to delete prior to executing DockerBuild.  This would ensure that we exercise the
   332  	// image pull logic.  However, no suitable target exists that meets all the criteria.  Therefore
   333  	// we settle on using a known released image.  We don't know if the image is already
   334  	// downloaded per se, and we don't want to explicitly delete this particular image first since
   335  	// it could be in use legitimately elsewhere.  Instead, we just know that this should always
   336  	// work and call that "close enough".
   337  	//
   338  	// Future considerations: publish a known dummy image that is multi-arch and free to randomly
   339  	// delete, and use that here instead.
   340  	err := DockerBuild(DockerBuildOptions{
   341  		Image:        cutil.ParseDockerfileTemplate("hyperledger/fabric-ccenv:$(ARCH)-1.0.0-alpha2"),
   342  		Cmd:          "/bin/true",
   343  		InputStream:  codepackage,
   344  		OutputStream: binpackage,
   345  	})
   346  	if err != nil {
   347  		t.Errorf("Error during build: %s", err)
   348  	}
   349  }
   350  
   351  func TestMain(m *testing.M) {
   352  	viper.SetConfigName("core")
   353  	viper.SetEnvPrefix("CORE")
   354  	config.AddDevConfigPath(nil)
   355  	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   356  	viper.AutomaticEnv()
   357  	if err := viper.ReadInConfig(); err != nil {
   358  		fmt.Printf("could not read config %s\n", err)
   359  		os.Exit(-1)
   360  	}
   361  	os.Exit(m.Run())
   362  }