github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/core/chaincode/upgrade_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 chaincode
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/hyperledger/fabric/common/util"
    25  	"github.com/hyperledger/fabric/core/common/ccprovider"
    26  	pb "github.com/hyperledger/fabric/protos/peer"
    27  
    28  	"github.com/golang/protobuf/proto"
    29  	"golang.org/x/net/context"
    30  )
    31  
    32  //getUpgradeLSCCSpec gets the spec for the chaincode upgrade to be sent to LSCC
    33  func getUpgradeLSCCSpec(chainID string, cds *pb.ChaincodeDeploymentSpec) (*pb.ChaincodeInvocationSpec, error) {
    34  	b, err := proto.Marshal(cds)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	//wrap the deployment in an invocation spec to lscc...
    40  	lsccSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: "lscc"}, Input: &pb.ChaincodeInput{Args: [][]byte{[]byte("upgrade"), []byte(chainID), b}}}}
    41  
    42  	return lsccSpec, nil
    43  }
    44  
    45  // upgrade a chaincode - i.e., build and initialize.
    46  func upgrade(ctx context.Context, cccid *ccprovider.CCContext, spec *pb.ChaincodeSpec, blockNumber uint64) (*ccprovider.CCContext, error) {
    47  	// First build and get the deployment spec
    48  	chaincodeDeploymentSpec, err := getDeploymentSpec(ctx, spec)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	return upgrade2(ctx, cccid, chaincodeDeploymentSpec, blockNumber)
    54  }
    55  
    56  func upgrade2(ctx context.Context, cccid *ccprovider.CCContext,
    57  	chaincodeDeploymentSpec *pb.ChaincodeDeploymentSpec, blockNumber uint64) (newcccid *ccprovider.CCContext, err error) {
    58  	cis, err := getUpgradeLSCCSpec(cccid.ChainID, chaincodeDeploymentSpec)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("Error creating lscc spec : %s\n", err)
    61  	}
    62  
    63  	ctx, txsim, err := startTxSimulation(ctx, cccid.ChainID)
    64  	if err != nil {
    65  		return nil, fmt.Errorf("Failed to get handle to simulator: %s ", err)
    66  	}
    67  
    68  	uuid := util.GenerateUUID()
    69  
    70  	cccid.TxID = uuid
    71  
    72  	defer func() {
    73  		//no error, lets try commit
    74  		if err == nil {
    75  			//capture returned error from commit
    76  			err = endTxSimulationCDS(cccid.ChainID, uuid, txsim, []byte("upgraded"), true, chaincodeDeploymentSpec, blockNumber)
    77  		} else {
    78  			//there was an error, just close simulation and return that
    79  			endTxSimulationCDS(cccid.ChainID, uuid, txsim, []byte("upgraded"), false, chaincodeDeploymentSpec, blockNumber)
    80  		}
    81  	}()
    82  
    83  	//ignore existence errors
    84  	ccprovider.PutChaincodeIntoFS(chaincodeDeploymentSpec)
    85  
    86  	sysCCVers := util.GetSysCCVersion()
    87  	lsccid := ccprovider.NewCCContext(cccid.ChainID, cis.ChaincodeSpec.ChaincodeId.Name, sysCCVers, uuid, true, nil, nil)
    88  
    89  	var cdbytes []byte
    90  	//write to lscc
    91  	if cdbytes, _, err = ExecuteWithErrorFilter(ctx, lsccid, cis); err != nil {
    92  		return nil, fmt.Errorf("Error executing LSCC for upgrade: %s", err)
    93  	}
    94  
    95  	if cdbytes == nil {
    96  		return nil, fmt.Errorf("Expected ChaincodeData back from LSCC but got nil")
    97  	}
    98  
    99  	cd := &ccprovider.ChaincodeData{}
   100  	if err = proto.Unmarshal(cdbytes, cd); err != nil {
   101  		return nil, fmt.Errorf("getting  ChaincodeData failed")
   102  	}
   103  
   104  	newVersion := string(cd.Version)
   105  	if newVersion == cccid.Version {
   106  		return nil, fmt.Errorf("Expected new version from LSCC but got same %s(%s)", newVersion, cccid.Version)
   107  	}
   108  
   109  	newcccid = ccprovider.NewCCContext(cccid.ChainID, chaincodeDeploymentSpec.ChaincodeSpec.ChaincodeId.Name, newVersion, uuid, false, nil, nil)
   110  
   111  	if _, _, err = ExecuteWithErrorFilter(ctx, newcccid, chaincodeDeploymentSpec); err != nil {
   112  		return nil, fmt.Errorf("Error deploying chaincode for upgrade: %s", err)
   113  	}
   114  	return
   115  }
   116  
   117  //TestUpgradeCC - test basic upgrade
   118  //     deploy example01
   119  //     do a query against 01 that'll fail
   120  //     upgrade to exampl02
   121  //     show the upgrade worked using the same query successfully
   122  //This test a variety of things in addition to basic upgrade
   123  //     uses next version from lscc
   124  //     re-initializtion of the same chaincode "mycc"
   125  //     upgrade when "mycc" is up and running (test version based namespace)
   126  func TestUpgradeCC(t *testing.T) {
   127  	chainID := util.GetTestChainID()
   128  
   129  	lis, err := initPeer(chainID)
   130  	if err != nil {
   131  		t.Fail()
   132  		t.Logf("Error creating peer: %s", err)
   133  	}
   134  
   135  	defer finitPeer(lis, chainID)
   136  
   137  	var ctxt = context.Background()
   138  
   139  	ccName := "mycc"
   140  	url := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01"
   141  	chaincodeID := &pb.ChaincodeID{Name: ccName, Path: url, Version: "0"}
   142  
   143  	f := "init"
   144  	args := util.ToChaincodeArgs(f, "a", "100", "b", "200")
   145  
   146  	spec := &pb.ChaincodeSpec{Type: 1, ChaincodeId: chaincodeID, Input: &pb.ChaincodeInput{Args: args}}
   147  
   148  	cccid := ccprovider.NewCCContext(chainID, ccName, "0", "", false, nil, nil)
   149  	var nextBlockNumber uint64 = 1
   150  	_, err = deploy(ctxt, cccid, spec, nextBlockNumber)
   151  
   152  	if err != nil {
   153  		theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
   154  		t.Fail()
   155  		t.Logf("Error deploying chaincode %s(%s)", chaincodeID, err)
   156  		theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: chaincodeID}})
   157  		return
   158  	}
   159  
   160  	// Query example01, which should fail
   161  	qArgs := util.ToChaincodeArgs("query", "a")
   162  
   163  	spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: chaincodeID, Input: &pb.ChaincodeInput{Args: qArgs}}
   164  
   165  	//Do not increment block number here because, the block will not be committted because of error
   166  	_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber, nil)
   167  	if err == nil {
   168  		t.Fail()
   169  		t.Logf("querying chaincode exampl01 should fail transaction: %s", err)
   170  		theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: chaincodeID}})
   171  		return
   172  	} else if !strings.Contains(err.Error(), "Invalid invoke function name. Expecting \"invoke\"") {
   173  		t.Fail()
   174  		t.Logf("expected <Invalid invoke function name. Expecting \"invoke\"> found <%s>", err)
   175  		theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: chaincodeID}})
   176  		return
   177  	}
   178  
   179  	//now upgrade to example02 which takes the same args as example01 but inits state vars
   180  	//and also allows query.
   181  	url = "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02"
   182  
   183  	//Note ccName hasn't changed...
   184  	chaincodeID = &pb.ChaincodeID{Name: ccName, Path: url, Version: "1"}
   185  	spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: chaincodeID, Input: &pb.ChaincodeInput{Args: args}}
   186  
   187  	//...and get back the ccid with the new version
   188  	nextBlockNumber++
   189  	cccid2, err := upgrade(ctxt, cccid, spec, nextBlockNumber)
   190  	if err != nil {
   191  		t.Fail()
   192  		t.Logf("Error upgrading chaincode %s(%s)", chaincodeID, err)
   193  		theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
   194  		if cccid2 != nil {
   195  			theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
   196  		}
   197  		return
   198  	}
   199  
   200  	//go back and do the same query now
   201  	spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: chaincodeID, Input: &pb.ChaincodeInput{Args: qArgs}}
   202  	nextBlockNumber++
   203  	_, _, _, err = invokeWithVersion(ctxt, chainID, cccid2.Version, spec, nextBlockNumber, nil)
   204  
   205  	if err != nil {
   206  		t.Fail()
   207  		t.Logf("querying chaincode exampl02 did not succeed: %s", err)
   208  		theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
   209  		theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
   210  		return
   211  	}
   212  	theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
   213  	theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
   214  }
   215  
   216  //TestInvalUpgradeCC - test basic upgrade
   217  //     upgrade to exampl02 when "mycc" is not deployed
   218  //     look for "not found" failure
   219  func TestInvalUpgradeCC(t *testing.T) {
   220  	chainID := util.GetTestChainID()
   221  
   222  	lis, err := initPeer(chainID)
   223  	if err != nil {
   224  		t.Fail()
   225  		t.Logf("Error creating peer: %s", err)
   226  	}
   227  
   228  	defer finitPeer(lis, chainID)
   229  
   230  	var ctxt = context.Background()
   231  
   232  	ccName := "mycc"
   233  	url := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02"
   234  
   235  	f := "init"
   236  	args := util.ToChaincodeArgs(f, "a", "100", "b", "200")
   237  
   238  	cccid := ccprovider.NewCCContext(chainID, ccName, "0", "", false, nil, nil)
   239  
   240  	//Note ccName hasn't changed...
   241  	chaincodeID := &pb.ChaincodeID{Name: ccName, Path: url, Version: "1"}
   242  	spec := &pb.ChaincodeSpec{Type: 1, ChaincodeId: chaincodeID, Input: &pb.ChaincodeInput{Args: args}}
   243  
   244  	//...and get back the ccid with the new version
   245  	var nextBlockNumber uint64
   246  	cccid2, err := upgrade(ctxt, cccid, spec, nextBlockNumber)
   247  	if err == nil {
   248  		t.Fail()
   249  		t.Logf("Error expected upgrading to fail but it succeeded%s(%s)", chaincodeID, err)
   250  		theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
   251  		if cccid2 != nil {
   252  			theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
   253  		}
   254  		return
   255  	}
   256  
   257  	theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
   258  }