Skip to content

Commit

Permalink
[FAB-2927] install chaincode package
Browse files Browse the repository at this point in the history
The CLI install command is enhanced to install a package
previously generated with the "package" or "signpackage" commands.

The cc-packaging-and-signing.rst describes the command.

With this change chaincode can deployed in one of these methods
  . using SDK or older "install" commands
  . create a (signed) package first as described in
    cc-packaging-and-signing.rst

fabric will first test for the older "ChaincodeDeploymentSpec" based
install before attempting to use the package.

Security fixes and linking the install to the instantiation record will
be submitted in future CRs.

Change-Id: I90c736d5f933dcd619b4de5944e7b69b50b45893
Signed-off-by: Srinivasan Muralidharan <muralisr@us.ibm.com>
  • Loading branch information
Srinivasan Muralidharan committed Apr 9, 2017
1 parent c810332 commit d91c5c3
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 33 deletions.
17 changes: 16 additions & 1 deletion core/common/ccprovider/ccprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ type CCPackage interface {
// ChaincodeData. The validation is based on the metadata from ChaincodeData
// One use of this method is to validate the chaincode before launching
ValidateCC(ccdata *ChaincodeData) (*pb.ChaincodeDeploymentSpec, error)

// GetPackageObject gets the object as a proto.Message
GetPackageObject() proto.Message
}

//SetChaincodesPath sets the chaincode path for this peer
Expand Down Expand Up @@ -89,8 +92,18 @@ func GetChaincodePackage(ccname string, ccversion string) ([]byte, error) {

// GetChaincodeFromFS this is a wrapper for hiding package implementation.
func GetChaincodeFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
//try raw CDS
cccdspack := &CDSPackage{}
return cccdspack.InitFromFS(ccname, ccversion)
b, depSpec, err := cccdspack.InitFromFS(ccname, ccversion)
if err != nil {
//try signed CDS
ccscdspack := &SignedCDSPackage{}
b, depSpec, err = ccscdspack.InitFromFS(ccname, ccversion)
if err != nil {
return nil, nil, err
}
}
return b, depSpec, nil
}

// PutChaincodeIntoFS is a wrapper for putting raw ChaincodeDeploymentSpec
Expand All @@ -110,8 +123,10 @@ func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error {
// GetCCPackage tries each known package implementation one by one
// till the right package is found
func GetCCPackage(buf []byte) (CCPackage, error) {
//try raw CDS
cccdspack := &CDSPackage{}
if _, err := cccdspack.InitFromBuffer(buf); err != nil {
//try signed CDS
ccscdspack := &SignedCDSPackage{}
if _, err := ccscdspack.InitFromBuffer(buf); err != nil {
return nil, err
Expand Down
7 changes: 6 additions & 1 deletion core/common/ccprovider/cdspackage.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ func (ccpack *CDSPackage) GetDepSpec() *pb.ChaincodeDeploymentSpec {
return ccpack.depSpec
}

// GetPackageObject gets the ChaincodeDeploymentSpec as proto.Message
func (ccpack *CDSPackage) GetPackageObject() proto.Message {
return ccpack.depSpec
}

// ValidateCC returns error if the chaincode is not found or if its not a
// ChaincodeDeploymentSpec
func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) (*pb.ChaincodeDeploymentSpec, error) {
Expand All @@ -59,7 +64,7 @@ func (ccpack *CDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) {
depSpec := &pb.ChaincodeDeploymentSpec{}
err := proto.Unmarshal(buf, depSpec)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal fs deployment spec from bytes")
return nil, fmt.Errorf("failed to unmarshal deployment spec from bytes")
}
ccpack.buf = buf
ccpack.depSpec = depSpec
Expand Down
13 changes: 13 additions & 0 deletions core/common/ccprovider/sigcdspackage.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,19 @@ type SignedCDSPackage struct {
buf []byte
depSpec *pb.ChaincodeDeploymentSpec
sDepSpec *pb.SignedChaincodeDeploymentSpec
env *common.Envelope
}

// GetDepSpec gets the ChaincodeDeploymentSpec from the package
func (ccpack *SignedCDSPackage) GetDepSpec() *pb.ChaincodeDeploymentSpec {
return ccpack.depSpec
}

// GetPackageObject gets the ChaincodeDeploymentSpec as proto.Message
func (ccpack *SignedCDSPackage) GetPackageObject() proto.Message {
return ccpack.env
}

// ValidateCC returns error if the chaincode is not found or if its not a
// ChaincodeDeploymentSpec
func (ccpack *SignedCDSPackage) ValidateCC(ccdata *ChaincodeData) (*pb.ChaincodeDeploymentSpec, error) {
Expand Down Expand Up @@ -69,6 +75,7 @@ func (ccpack *SignedCDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, erro
ccpack.buf = nil
ccpack.sDepSpec = nil
ccpack.depSpec = nil
ccpack.env = nil

env := &common.Envelope{}
err := proto.Unmarshal(buf, env)
Expand All @@ -92,6 +99,7 @@ func (ccpack *SignedCDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, erro
ccpack.buf = buf
ccpack.sDepSpec = sDepSpec
ccpack.depSpec = depSpec
ccpack.env = env

return &ChaincodeData{Name: depSpec.ChaincodeSpec.ChaincodeId.Name, Version: depSpec.ChaincodeSpec.ChaincodeId.Version}, nil
}
Expand All @@ -102,6 +110,7 @@ func (ccpack *SignedCDSPackage) InitFromFS(ccname string, ccversion string) ([]b
ccpack.buf = nil
ccpack.sDepSpec = nil
ccpack.depSpec = nil
ccpack.env = nil

buf, err := GetChaincodePackage(ccname, ccversion)
if err != nil {
Expand All @@ -125,6 +134,10 @@ func (ccpack *SignedCDSPackage) PutChaincodeToFS() error {
return fmt.Errorf("depspec cannot be nil if buf is not nil")
}

if ccpack.env == nil {
return fmt.Errorf("env cannot be nil if buf and depspec are not nil")
}

ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name
ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version

Expand Down
15 changes: 13 additions & 2 deletions docs/source/cc-packaging-and-signing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ installation simply as a file with name
called a chaincode package.

This document describes how a chaincode package can be created and
signed from CLI. The package is used for install using the usual install
procedures and not covered in this document.
signed from CLI. It also describes how the ``install`` command can
be used to install the chaincode package.

What’s in the package ?
-----------------------
Expand Down Expand Up @@ -64,6 +64,17 @@ where ``ccpack.out`` and ``signedccpack.out`` are input and output
packages respectively. ``signedccpack.out`` contains an additional
signature over the package signed using the Local MSP.

Installing the package
----------------------
The package can be installed using the ``install`` command as follows

::

peer chaincode install ccpack.out

where ``ccpack.out`` is a package filecreated using the ``package``
or ``signedpackage`` commands.

Conclusion
----------

Expand Down
126 changes: 107 additions & 19 deletions peer/chaincode/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ package chaincode

import (
"fmt"
"io/ioutil"

"github.com/golang/protobuf/proto"
"golang.org/x/net/context"

"github.com/hyperledger/fabric/core/common/ccpackage"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/peer/common"
pcommon "github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"

Expand All @@ -43,21 +47,25 @@ func installCmd(cf *ChaincodeCmdFactory) *cobra.Command {
Long: fmt.Sprintf(install_desc),
ValidArgs: []string{"1"},
RunE: func(cmd *cobra.Command, args []string) error {
return chaincodeInstall(cmd, args, cf)
var ccpackfile string
if len(args) > 0 {
ccpackfile = args[0]
}
return chaincodeInstall(cmd, ccpackfile, cf)
},
}

return chaincodeInstallCmd
}

//install the depspec to "peer.address"
func install(chaincodeName string, chaincodeVersion string, cds *pb.ChaincodeDeploymentSpec, cf *ChaincodeCmdFactory) error {
func install(msg proto.Message, cf *ChaincodeCmdFactory) error {
creator, err := cf.Signer.Serialize()
if err != nil {
return fmt.Errorf("Error serializing identity for %s: %s", cf.Signer.GetIdentifier(), err)
}

prop, _, err := utils.CreateInstallProposalFromCDS(cds, creator)
prop, _, err := utils.CreateInstallProposalFromCDS(msg, creator)
if err != nil {
return fmt.Errorf("Error creating proposal %s: %s", chainFuncName, err)
}
Expand All @@ -80,12 +88,72 @@ func install(chaincodeName string, chaincodeVersion string, cds *pb.ChaincodeDep
return nil
}

// chaincodeInstall installs the chaincode. If remoteinstall, does it via a lccc call
func chaincodeInstall(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error {
if chaincodePath == common.UndefinedParamValue || chaincodeVersion == common.UndefinedParamValue {
return fmt.Errorf("Must supply value for %s path and version parameters.", chainFuncName)
//generateChaincode creates ChaincodeDeploymentSpec as the package to install
func generateChaincode(cmd *cobra.Command, chaincodeName, chaincodeVersion string) (*pb.ChaincodeDeploymentSpec, error) {
tmppkg, _ := ccprovider.GetChaincodePackage(chaincodeName, chaincodeVersion)
if tmppkg != nil {
return nil, fmt.Errorf("chaincode %s:%s exists", chaincodeName, chaincodeVersion)
}

spec, err := getChaincodeSpecification(cmd)
if err != nil {
return nil, err
}

cds, err := getChaincodeBytes(spec, true)
if err != nil {
return nil, fmt.Errorf("Error getting chaincode code %s: %s", chainFuncName, err)
}

return cds, nil
}

//getPackageFromFile get the chaincode package from file and the extracted ChaincodeDeploymentSpec
func getPackageFromFile(ccpackfile string) (proto.Message, *pb.ChaincodeDeploymentSpec, error) {
b, err := ioutil.ReadFile(ccpackfile)
if err != nil {
return nil, nil, err
}

//the bytes should be a valid package (CDS or SigedCDS)
ccpack, err := ccprovider.GetCCPackage(b)
if err != nil {
return nil, nil, err
}

//either CDS or Envelope
o, err := ccpack.GetPackageObject(), nil
if err != nil {
return nil, nil, err
}

//try CDS first
cds, ok := o.(*pb.ChaincodeDeploymentSpec)
if !ok || cds == nil {
//try Envelope next
env, ok := o.(*pcommon.Envelope)
if !ok || env == nil {
return nil, nil, fmt.Errorf("error extracting valid chaincode package")
}

//this will check for a valid package Envelope
_, sCDS, err := ccpackage.ExtractSignedCCDepSpec(env)
if err != nil {
return nil, nil, fmt.Errorf("error extracting valid signed chaincode package(%s)", err)
}

//...and get the CDS at last
cds, err = utils.GetChaincodeDeploymentSpec(sCDS.ChaincodeDeploymentSpec)
if err != nil {
return nil, nil, fmt.Errorf("error extracting chaincode deployment spec(%s)", err)
}
}

return o, cds, nil
}

// chaincodeInstall installs the chaincode. If remoteinstall, does it via a lccc call
func chaincodeInstall(cmd *cobra.Command, ccpackfile string, cf *ChaincodeCmdFactory) error {
var err error
if cf == nil {
cf, err = InitCmdFactory(true, false)
Expand All @@ -94,22 +162,42 @@ func chaincodeInstall(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory
}
}

tmppkg, _ := ccprovider.GetChaincodePackage(chaincodeName, chaincodeVersion)
if tmppkg != nil {
return fmt.Errorf("chaincode %s:%s exists", chaincodeName, chaincodeVersion)
}
var ccpackmsg proto.Message
if ccpackfile == "" {
if chaincodePath == common.UndefinedParamValue || chaincodeVersion == common.UndefinedParamValue {
return fmt.Errorf("Must supply value for %s path and version parameters.", chainFuncName)
}
//generate a raw ChaincodeDeploymentSpec
ccpackmsg, err = generateChaincode(cmd, chaincodeName, chaincodeVersion)
if err != nil {
return err
}
} else {
//read in a package generated by the "package" sub-command (and perhaps signed
//by multiple owners with the "signpackage" sub-command)
var cds *pb.ChaincodeDeploymentSpec
ccpackmsg, cds, err = getPackageFromFile(ccpackfile)

spec, err := getChaincodeSpecification(cmd)
if err != nil {
return err
}
if err != nil {
return err
}

cds, err := getChaincodeBytes(spec, true)
if err != nil {
return fmt.Errorf("Error getting chaincode code %s: %s", chainFuncName, err)
//get the chaincode details from cds
cName := cds.ChaincodeSpec.ChaincodeId.Name
cVersion := cds.ChaincodeSpec.ChaincodeId.Version

//if user provided chaincodeName, use it for validation
if chaincodeName != "" && chaincodeName != cName {
return fmt.Errorf("chaincode name %s does not match name %s in package", chaincodeName, cName)
}

//if user provided chaincodeVersion, use it for validation
if chaincodeVersion != "" && chaincodeVersion != cVersion {
return fmt.Errorf("chaincode version %s does not match version %s in packages", chaincodeVersion, cVersion)
}
}

err = install(chaincodeName, chaincodeVersion, cds, cf)
err = install(ccpackmsg, cf)

return err
}
Loading

0 comments on commit d91c5c3

Please sign in to comment.