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