github.com/true-sqn/fabric@v2.1.1+incompatible/internal/peer/chaincode/install.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package chaincode
     8  
     9  import (
    10  	"context"
    11  	"io/ioutil"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	cb "github.com/hyperledger/fabric-protos-go/common"
    15  	pb "github.com/hyperledger/fabric-protos-go/peer"
    16  	"github.com/hyperledger/fabric/bccsp"
    17  	"github.com/hyperledger/fabric/core/common/ccpackage"
    18  	"github.com/hyperledger/fabric/core/common/ccprovider"
    19  	"github.com/hyperledger/fabric/internal/peer/common"
    20  	"github.com/hyperledger/fabric/internal/pkg/identity"
    21  	"github.com/hyperledger/fabric/protoutil"
    22  	"github.com/pkg/errors"
    23  	"github.com/spf13/cobra"
    24  )
    25  
    26  var chaincodeInstallCmd *cobra.Command
    27  
    28  const (
    29  	installCmdName = "install"
    30  )
    31  
    32  // Installer holds the dependencies needed to install
    33  // a chaincode
    34  type Installer struct {
    35  	Command         *cobra.Command
    36  	EndorserClients []pb.EndorserClient
    37  	Input           *InstallInput
    38  	Signer          identity.SignerSerializer
    39  	CryptoProvider  bccsp.BCCSP
    40  }
    41  
    42  // InstallInput holds the input parameters for installing
    43  // a chaincode
    44  type InstallInput struct {
    45  	Name        string
    46  	Version     string
    47  	Language    string
    48  	PackageFile string
    49  	Path        string
    50  }
    51  
    52  // installCmd returns the cobra command for chaincode install
    53  func installCmd(cf *ChaincodeCmdFactory, i *Installer, cryptoProvider bccsp.BCCSP) *cobra.Command {
    54  	chaincodeInstallCmd = &cobra.Command{
    55  		Use:       "install",
    56  		Short:     "Install a chaincode.",
    57  		Long:      "Install a chaincode on a peer. This installs a chaincode deployment spec package (if provided) or packages the specified chaincode before subsequently installing it.",
    58  		ValidArgs: []string{"1"},
    59  		RunE: func(cmd *cobra.Command, args []string) error {
    60  			if i == nil {
    61  				var err error
    62  				if cf == nil {
    63  					cf, err = InitCmdFactory(cmd.Name(), true, false, cryptoProvider)
    64  					if err != nil {
    65  						return err
    66  					}
    67  				}
    68  				i = &Installer{
    69  					Command:         cmd,
    70  					EndorserClients: cf.EndorserClients,
    71  					Signer:          cf.Signer,
    72  					CryptoProvider:  cryptoProvider,
    73  				}
    74  			}
    75  			return i.installChaincode(args)
    76  		},
    77  	}
    78  	flagList := []string{
    79  		"lang",
    80  		"ctor",
    81  		"path",
    82  		"name",
    83  		"version",
    84  		"peerAddresses",
    85  		"tlsRootCertFiles",
    86  		"connectionProfile",
    87  	}
    88  	attachFlags(chaincodeInstallCmd, flagList)
    89  
    90  	return chaincodeInstallCmd
    91  }
    92  
    93  // installChaincode installs the chaincode
    94  func (i *Installer) installChaincode(args []string) error {
    95  	if i.Command != nil {
    96  		// Parsing of the command line is done so silence cmd usage
    97  		i.Command.SilenceUsage = true
    98  	}
    99  
   100  	i.setInput(args)
   101  
   102  	// LSCC install
   103  	return i.install()
   104  }
   105  
   106  func (i *Installer) setInput(args []string) {
   107  	i.Input = &InstallInput{
   108  		Name:    chaincodeName,
   109  		Version: chaincodeVersion,
   110  		Path:    chaincodePath,
   111  	}
   112  
   113  	if len(args) > 0 {
   114  		i.Input.PackageFile = args[0]
   115  	}
   116  }
   117  
   118  // install installs a chaincode deployment spec to "peer.address"
   119  // for use with lscc
   120  func (i *Installer) install() error {
   121  	ccPkgMsg, err := i.getChaincodePackageMessage()
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	proposal, err := i.createInstallProposal(ccPkgMsg)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	signedProposal, err := protoutil.GetSignedProposal(proposal, i.Signer)
   132  	if err != nil {
   133  		return errors.WithMessagef(err, "error creating signed proposal for %s", chainFuncName)
   134  	}
   135  
   136  	return i.submitInstallProposal(signedProposal)
   137  }
   138  
   139  func (i *Installer) submitInstallProposal(signedProposal *pb.SignedProposal) error {
   140  	// install is currently only supported for one peer
   141  	proposalResponse, err := i.EndorserClients[0].ProcessProposal(context.Background(), signedProposal)
   142  	if err != nil {
   143  		return errors.WithMessage(err, "error endorsing chaincode install")
   144  	}
   145  
   146  	if proposalResponse == nil {
   147  		return errors.New("error during install: received nil proposal response")
   148  	}
   149  
   150  	if proposalResponse.Response == nil {
   151  		return errors.New("error during install: received proposal response with nil response")
   152  	}
   153  
   154  	if proposalResponse.Response.Status != int32(cb.Status_SUCCESS) {
   155  		return errors.Errorf("install failed with status: %d - %s", proposalResponse.Response.Status, proposalResponse.Response.Message)
   156  	}
   157  	logger.Infof("Installed remotely: %v", proposalResponse)
   158  
   159  	return nil
   160  }
   161  
   162  func (i *Installer) getChaincodePackageMessage() (proto.Message, error) {
   163  	// if no package provided, create one
   164  	if i.Input.PackageFile == "" {
   165  		if i.Input.Path == common.UndefinedParamValue || i.Input.Version == common.UndefinedParamValue || i.Input.Name == common.UndefinedParamValue {
   166  			return nil, errors.Errorf("must supply value for %s name, path and version parameters", chainFuncName)
   167  		}
   168  		// generate a raw ChaincodeDeploymentSpec
   169  		ccPkgMsg, err := genChaincodeDeploymentSpec(i.Command, i.Input.Name, i.Input.Version)
   170  		if err != nil {
   171  			return nil, err
   172  		}
   173  		return ccPkgMsg, nil
   174  	}
   175  
   176  	// read in a package generated by the "package" sub-command (and perhaps signed
   177  	// by multiple owners with the "signpackage" sub-command)
   178  	// var cds *pb.ChaincodeDeploymentSpec
   179  	ccPkgMsg, cds, err := getPackageFromFile(i.Input.PackageFile, i.CryptoProvider)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	// get the chaincode details from cds
   185  	cName := cds.ChaincodeSpec.ChaincodeId.Name
   186  	cVersion := cds.ChaincodeSpec.ChaincodeId.Version
   187  
   188  	// if user provided chaincodeName, use it for validation
   189  	if i.Input.Name != "" && i.Input.Name != cName {
   190  		return nil, errors.Errorf("chaincode name %s does not match name %s in package", i.Input.Name, cName)
   191  	}
   192  
   193  	// if user provided chaincodeVersion, use it for validation
   194  	if i.Input.Version != "" && i.Input.Version != cVersion {
   195  		return nil, errors.Errorf("chaincode version %s does not match version %s in packages", i.Input.Version, cVersion)
   196  	}
   197  
   198  	return ccPkgMsg, nil
   199  }
   200  
   201  func (i *Installer) createInstallProposal(msg proto.Message) (*pb.Proposal, error) {
   202  	creator, err := i.Signer.Serialize()
   203  	if err != nil {
   204  		return nil, errors.WithMessage(err, "error serializing identity")
   205  	}
   206  
   207  	prop, _, err := protoutil.CreateInstallProposalFromCDS(msg, creator)
   208  	if err != nil {
   209  		return nil, errors.WithMessagef(err, "error creating proposal for %s", chainFuncName)
   210  	}
   211  
   212  	return prop, nil
   213  }
   214  
   215  // genChaincodeDeploymentSpec creates ChaincodeDeploymentSpec as the package to install
   216  func genChaincodeDeploymentSpec(cmd *cobra.Command, chaincodeName, chaincodeVersion string) (*pb.ChaincodeDeploymentSpec, error) {
   217  	if existed, _ := ccprovider.ChaincodePackageExists(chaincodeName, chaincodeVersion); existed {
   218  		return nil, errors.Errorf("chaincode %s:%s already exists", chaincodeName, chaincodeVersion)
   219  	}
   220  
   221  	spec, err := getChaincodeSpec(cmd)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	cds, err := getChaincodeDeploymentSpec(spec, true)
   227  	if err != nil {
   228  		return nil, errors.WithMessagef(err, "error getting chaincode deployment spec for %s", chaincodeName)
   229  	}
   230  
   231  	return cds, nil
   232  }
   233  
   234  // getPackageFromFile get the chaincode package from file and the extracted ChaincodeDeploymentSpec
   235  func getPackageFromFile(ccPkgFile string, cryptoProvider bccsp.BCCSP) (proto.Message, *pb.ChaincodeDeploymentSpec, error) {
   236  	ccPkgBytes, err := ioutil.ReadFile(ccPkgFile)
   237  	if err != nil {
   238  		return nil, nil, err
   239  	}
   240  
   241  	// the bytes should be a valid package (CDS or SignedCDS)
   242  	ccpack, err := ccprovider.GetCCPackage(ccPkgBytes, cryptoProvider)
   243  	if err != nil {
   244  		return nil, nil, err
   245  	}
   246  
   247  	// either CDS or Envelope
   248  	o := ccpack.GetPackageObject()
   249  
   250  	// try CDS first
   251  	cds, ok := o.(*pb.ChaincodeDeploymentSpec)
   252  	if !ok || cds == nil {
   253  		// try Envelope next
   254  		env, ok := o.(*cb.Envelope)
   255  		if !ok || env == nil {
   256  			return nil, nil, errors.New("error extracting valid chaincode package")
   257  		}
   258  
   259  		// this will check for a valid package Envelope
   260  		_, sCDS, err := ccpackage.ExtractSignedCCDepSpec(env)
   261  		if err != nil {
   262  			return nil, nil, errors.WithMessage(err, "error extracting valid signed chaincode package")
   263  		}
   264  
   265  		// ...and get the CDS at last
   266  		cds, err = protoutil.UnmarshalChaincodeDeploymentSpec(sCDS.ChaincodeDeploymentSpec)
   267  		if err != nil {
   268  			return nil, nil, errors.WithMessage(err, "error extracting chaincode deployment spec")
   269  		}
   270  
   271  		err = platformRegistry.ValidateDeploymentSpec(cds.ChaincodeSpec.Type.String(), cds.CodePackage)
   272  		if err != nil {
   273  			return nil, nil, errors.WithMessage(err, "chaincode deployment spec validation failed")
   274  		}
   275  	}
   276  
   277  	return o, cds, nil
   278  }