hperledger fabric 1.0 源码分析之peer chaincode install

前言

本文共分三部份介绍,第一部分先了解相关术语词,第二部分进行过程的分析,第三部份进行方法详解。

正文

一、相关术语词

ChaincodeSpec:Chaincode说明,描述说明chaincode的结构体,简称为CS

ChaincodeDeploymentSpec:Chaincode部署说明,描述说明一个chaincode该如何部署,简称为CDS

ccpackfile:一种由chaincode命令的package或signpackage子命令生成的chaincode二进制部署包。前者是由CDS对象生成,后者是由Envelope对象(包含签名过的CDS)生成。

ChaincodeInvocationSpec:chaincode调用说明,简称为CIS,主要存储了一份chaincode说明

Proposal/SignedProposal:Proposal封装了chaincode的数据

CCContext:chaincode context,即链码内容

chaincodeCtorJSON:较复杂的Flag之一,指定chaincode要运行的函数和函数的参数,原型为JSON格式的字符串,如{ "Function":"func", "Args":["param"] }。可以直接调用json.Unmarshal将原型字符串存入一个ChaincodeInput对象中,作为ChaincodeSpec的Input。

ccHdrExt:全称ChaincodeHeaderExtension,是Header类型为Chaincode时要使用的Header的扩展消息。 此扩展用于指定要调用的chaincode以及ledger应显示的内容。

二、执行过程分析

peer注册的chaincode命令

mainCmd.AddCommand(chaincode.Cmd(nil))
//github.com/hyperledger/fabric/peer/chaincode/chaincode.go
//peer chaincode

func Cmd(cf *ChaincodeCmdFactory) *cobra.Command {
	addFlags(chaincodeCmd)

	chaincodeCmd.AddCommand(installCmd(cf))
	chaincodeCmd.AddCommand(instantiateCmd(cf))
	chaincodeCmd.AddCommand(invokeCmd(cf))
	chaincodeCmd.AddCommand(packageCmd(cf, nil))
	chaincodeCmd.AddCommand(queryCmd(cf))
	chaincodeCmd.AddCommand(signpackageCmd(cf))
	chaincodeCmd.AddCommand(upgradeCmd(cf))

	return chaincodeCmd
}
var chaincodeCmd = &cobra.Command{
	Use:   chainFuncName,
	Short: fmt.Sprint(shortDes),
	Long:  fmt.Sprint(longDes),
}

由上可知,peer chaincode命令中新增了六项子命令,以下先介绍peer chinacode install。

在chiancode.go中因含有init方法,所以同时为peer chaincode新增了以下命令标记

func init() {
	resetFlags()
}

// Explicitly define a method to facilitate tests
func resetFlags() {
	flags = &pflag.FlagSet{}

	flags.StringVarP(&chaincodeLang, "lang", "l", "golang",
		fmt.Sprintf("Language the %s is written in", chainFuncName))
	flags.StringVarP(&chaincodeCtorJSON, "ctor", "c", "{}",
		fmt.Sprintf("Constructor message for the %s in JSON format", chainFuncName))
	flags.StringVarP(&chaincodePath, "path", "p", common.UndefinedParamValue,
		fmt.Sprintf("Path to %s", chainFuncName))
	flags.StringVarP(&chaincodeName, "name", "n", common.UndefinedParamValue,
		fmt.Sprint("Name of the chaincode"))
	flags.StringVarP(&chaincodeVersion, "version", "v", common.UndefinedParamValue,
		fmt.Sprint("Version of the chaincode specified in install/instantiate/upgrade commands"))
	flags.StringVarP(&chaincodeUsr, "username", "u", common.UndefinedParamValue,
		fmt.Sprint("Username for chaincode operations when security is enabled"))
	flags.StringVarP(&customIDGenAlg, "tid", "t", common.UndefinedParamValue,
		fmt.Sprint("Name of a custom ID generation algorithm (hashing and decoding) e.g. sha256base64"))
	flags.StringVarP(&chainID, "channelID", "C", util.GetTestChainID(),
		fmt.Sprint("The channel on which this command should be executed"))
	flags.StringVarP(&policy, "policy", "P", common.UndefinedParamValue,
		fmt.Sprint("The endorsement policy associated to this chaincode"))
	flags.StringVarP(&escc, "escc", "E", common.UndefinedParamValue,
		fmt.Sprint("The name of the endorsement system chaincode to be used for this chaincode"))
	flags.StringVarP(&vscc, "vscc", "V", common.UndefinedParamValue,
		fmt.Sprint("The name of the verification system chaincode to be used for this chaincode"))
}
var (
	chaincodeLang     string
	chaincodeCtorJSON string
	chaincodePath     string
	chaincodeName     string
	chaincodeUsr      string // Not used
	chaincodeQueryRaw bool
	chaincodeQueryHex bool
	customIDGenAlg    string
	chainID           string
	chaincodeVersion  string
	policy            string
	escc              string
	vscc              string
	policyMarhsalled  []byte
	orderingEndpoint  string
	tls               bool
	caFile            string
)

示例:

peer chaincode install -n mycc -v v0 -p github.com/chaincode_example02

-n表示chaincode的名字,对应定义的变量chaincodeName

-l表示chaincode的语言,对应定义的变量chaincodeLang(java,go,nodejs)

peer chaincode install命令

peer chaincode install支持以下两种方式:

1、指定代码方式,peer chaincode install -n <链码名称> -v <链码版本> -p <链码路径>
2、基于链码打包文件方式,peer chaincode install <链码打包文件>

//github.com/hyperledger/fabric/peer/chaincode/install.go

//添加install命令
chaincodeCmd.AddCommand(installCmd(cf))
//install实现
func installCmd(cf *ChaincodeCmdFactory) *cobra.Command {
	chaincodeInstallCmd = &cobra.Command{
		Use:       "install",
		Short:     fmt.Sprint(installDesc),
		Long:      fmt.Sprint(installDesc),
		ValidArgs: []string{"1"},
		RunE: func(cmd *cobra.Command, args []string) error {
            //命令运行入口
            //保存第一个参数到ccpackfile,并传入chaincodeInstall()
			var ccpackfile string
			if len(args) > 0 {
				ccpackfile = args[0]
			}
			return chaincodeInstall(cmd, ccpackfile, cf)
		},
	}
	flagList := []string{
		"lang",
		"ctor",
		"path",
		"name",
		"version",
	}
	attachFlags(chaincodeInstallCmd, flagList)

	return chaincodeInstallCmd
}

chaincodeInstall()执行过程

//github.com/hyperledger/fabric/peer/chaincode/install.go

func chaincodeInstall(cmd *cobra.Command, ccpackfile string, cf *ChaincodeCmdFactory) error {
	var err error
	if cf == nil {
        //初始化Endorser客户端,构造ChaincodeCmdFactory
		cf, err = InitCmdFactory(true, false) 
		if err != nil {
			return err
		}
	}

	var ccpackmsg proto.Message
    //链码执行方式判断
	if ccpackfile == "" { 
        //指定代码方式
		if chaincodePath == common.UndefinedParamValue || chaincodeVersion == common.UndefinedParamValue || chaincodeName == common.UndefinedParamValue {
			return fmt.Errorf("Must supply value for %s name, path and version parameters.", chainFuncName)
		}
		//构造ChaincodeDeploymentSpec
		ccpackmsg, err = genChaincodeDeploymentSpec(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)
        //获取ccpackfile包内容并构造ChaincodeDeploymentSpec
		var cds *pb.ChaincodeDeploymentSpec 
		ccpackmsg, cds, err = getPackageFromFile(ccpackfile)

		if err != nil {
			return 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(ccpackmsg, cf)

	return err
}

install()执行过程

//github.com/hyperledger/fabric/peer/chaincode/install.go

//安装到peer.address,即core.yaml配置文件定义中地址,默认为0.0.0.0:7051
func install(msg proto.Message, cf *ChaincodeCmdFactory) error {
    //获取lscc签名身份,并序列化
	creator, err := cf.Signer.Serialize()
	if err != nil {
		return fmt.Errorf("Error serializing identity for %s: %s", cf.Signer.GetIdentifier(), err)
	}
	//由ChaincodeDeploymentSpec生成提案Proposal
	prop, _, err := utils.CreateInstallProposalFromCDS(msg, creator)
	if err != nil {
		return fmt.Errorf("Error creating proposal  %s: %s", chainFuncName, err)
	}
    //对提案进行签名
	var signedProp *pb.SignedProposal
	signedProp, err = utils.GetSignedProposal(prop, cf.Signer)
	if err != nil {
		return fmt.Errorf("Error creating signed proposal  %s: %s", chainFuncName, err)
	}
    //提交提案并等待响应
	proposalResponse, err := cf.EndorserClient.ProcessProposal(context.Background(), signedProp)
	if err != nil {
		return fmt.Errorf("Error endorsing %s: %s", chainFuncName, err)
	}

	if proposalResponse != nil {
		logger.Debugf("Installed remotely %v", proposalResponse)
	}

	return nil
}

三、方法详解

InitCmdFactory(true,false)

初始化客户端并构造ChaincodeCmdFactory

//github.com/hyperledger/fabric/peer/chaincode/common.go

func InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFactory, error) {
	var err error
	var endorserClient pb.EndorserClient
	if isEndorserRequired {
		endorserClient, err = common.GetEndorserClientFnc()
		if err != nil {
			return nil, fmt.Errorf("Error getting endorser client %s: %s", chainFuncName, err)
		}
	}

	signer, err := common.GetDefaultSignerFnc()
	if err != nil {
		return nil, fmt.Errorf("Error getting default signer: %s", err)
	}

	var broadcastClient common.BroadcastClient
    //isOrdererRequired默认为false,暂时未用到,暂时跳过
	if isOrdererRequired {
		if len(orderingEndpoint) == 0 {
			orderingEndpoints, err := common.GetOrdererEndpointOfChainFnc(chainID, signer, endorserClient)
			if err != nil {
				return nil, fmt.Errorf("Error getting (%s) orderer endpoint: %s", chainID, err)
			}
			if len(orderingEndpoints) == 0 {
				return nil, fmt.Errorf("Error no orderer endpoint got for %s", chainID)
			}
			logger.Infof("Get chain(%s) orderer endpoint: %s", chainID, orderingEndpoints[0])
			orderingEndpoint = orderingEndpoints[0]
		}

		broadcastClient, err = common.GetBroadcastClientFnc(orderingEndpoint, tls, caFile)

		if err != nil {
			return nil, fmt.Errorf("Error getting broadcast client: %s", err)
		}
	}
    //构造并返回ChaincodeCmdFactory
	return &ChaincodeCmdFactory{
		EndorserClient:  endorserClient,
		Signer:          signer,
		BroadcastClient: broadcastClient,
	}, nil
}
//github.com/hyperledger/fabric/peer/common/common.go

//初始化进行了赋值
GetEndorserClientFnc = GetEndorserClient
//与peer服务建立连接
func GetEndorserClient() (pb.EndorserClient, error) {
	clientConn, err := peer.NewPeerClientConnection()
	if err != nil {
		err = errors.ErrorWithCallstack("PER", "404", "Error trying to connect to local peer").WrapError(err)
		return nil, err
	}
	endorserClient := pb.NewEndorserClient(clientConn)
	return endorserClient, nil
}
//github.com/hyperledger/fabric/peer/common/common.go

//初始化进行了赋值
GetDefaultSignerFnc = GetDefaultSigner
//获取启动peer服务时建立的默认的msp身份签名
func GetDefaultSigner() (msp.SigningIdentity, error) {
	signer, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
	if err != nil {
		return nil, fmt.Errorf("Error obtaining the default signing identity, err %s", err)
	}

	return signer, err
}

genChaincodeDeploymentSpec(cmd, chaincodeName, chaincodeVersion)

生成chaincode部署说明对象

//github.com/hyperledger/fabric/peer/chaincode/install.go

func genChaincodeDeploymentSpec(cmd *cobra.Command, chaincodeName, chaincodeVersion string) (*pb.ChaincodeDeploymentSpec, error) {
    //通过chaincode提供者对象判断是否存在
	if existed, _ := ccprovider.ChaincodePackageExists(chaincodeName, chaincodeVersion); existed {
		return nil, fmt.Errorf("chaincode %s:%s already exists", chaincodeName, chaincodeVersion)
	}
    
	spec, err := getChaincodeSpec(cmd)
	if err != nil {
		return nil, err
	}

	cds, err := getChaincodeDeploymentSpec(spec, true)
	if err != nil {
		return nil, fmt.Errorf("Error getting chaincode code %s: %s", chainFuncName, err)
	}
    //返回构造好的chaincodeDeploymentSpec
	return cds, nil
}
//github.com/hyperledger/fabric/peer/chaincode/common.go

//构造chaincode说明
func getChaincodeSpec(cmd *cobra.Command) (*pb.ChaincodeSpec, error) {
	spec := &pb.ChaincodeSpec{}
	if err := checkChaincodeCmdParams(cmd); err != nil {
		return spec, err
	}

	// Build the spec
	input := &pb.ChaincodeInput{}
	if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil {
		return spec, fmt.Errorf("Chaincode argument error: %s", err)
	}

	chaincodeLang = strings.ToUpper(chaincodeLang)
	if pb.ChaincodeSpec_Type_value[chaincodeLang] == int32(pb.ChaincodeSpec_JAVA) {
		return nil, fmt.Errorf("Java chaincode is work-in-progress and disabled")
	}
	spec = &pb.ChaincodeSpec{
		Type:        pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]),
		ChaincodeId: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName, Version: chaincodeVersion},
		Input:       input,
	}
	return spec, nil
}
//github.com/hyperledger/fabric/peer/chaincode/common.go

//根据传入的chaincodespec构造chaincodeDeploymentSpec
func getChaincodeDeploymentSpec(spec *pb.ChaincodeSpec, crtPkg bool) (*pb.ChaincodeDeploymentSpec, error) {
	var codePackageBytes []byte
	if chaincode.IsDevMode() == false && crtPkg {
		var err error
		if err = checkSpec(spec); err != nil {
			return nil, err
		}

		codePackageBytes, err = container.GetChaincodePackageBytes(spec)
		if err != nil {
			err = fmt.Errorf("Error getting chaincode package bytes: %s", err)
			return nil, err
		}
	}
	chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes}
	return chaincodeDeploymentSpec, nil
}

getPackageFromFile(ccpackfile)

//获取chaincode包内容,并提取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)
	//GetCCPackage将buffer中的数据存入对象CDSpackage或SignedCDSpackage
	ccpack, err := ccprovider.GetCCPackage(b)
	if err != nil {
		return nil, nil, err
	}

	//either CDS or Envelope
	//将buffer转化为CDS或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
}

CreateInstallProposalFromCDS(msg, creator)

从ChaincodeDeploymentSpec创建chaincode安装提案

func createProposalFromCDS(chainID string, msg proto.Message, creator []byte, policy []byte, escc []byte, vscc []byte, propType string) (*peer.Proposal, string, error) {
	//in the new mode, cds will be nil, "deploy" and "upgrade" are instantiates.
	var ccinp *peer.ChaincodeInput
	var b []byte
	var err error
	if msg != nil {
		b, err = proto.Marshal(msg)
		if err != nil {
			return nil, "", err
		}
	}
	switch propType {
	case "deploy":
		fallthrough
	case "upgrade":
		cds, ok := msg.(*peer.ChaincodeDeploymentSpec)
		if !ok || cds == nil {
			return nil, "", fmt.Errorf("invalid message for creating lifecycle chaincode proposal from")
		}
		ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), []byte(chainID), b, policy, escc, vscc}}
	case "install":
		ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), b}}
	}

	//构造lscc说明
	lsccSpec := &peer.ChaincodeInvocationSpec{
		ChaincodeSpec: &peer.ChaincodeSpec{
			Type:        peer.ChaincodeSpec_GOLANG,
			ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
			Input:       ccinp}}

	//...and get the proposal for it
	return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, chainID, lsccSpec, creator)
}
func CreateProposalFromCIS(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) {
	return CreateChaincodeProposal(typ, chainID, cis, creator)
}
func CreateChaincodeProposal(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) {
	return CreateChaincodeProposalWithTransient(typ, chainID, cis, creator, nil)
}
//创建交易ID(txid)
func CreateChaincodeProposalWithTransient(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
	// generate a random nonce
	nonce, err := crypto.GetRandomNonce()
	if err != nil {
		return nil, "", err
	}

	// compute txid
	txid, err := ComputeProposalTxID(nonce, creator)
	if err != nil {
		return nil, "", err
	}

	return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, chainID, cis, nonce, creator, transientMap)
}
//对数据进行序列化处理,返回提案信息
func CreateChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, nonce, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
	ccHdrExt := &peer.ChaincodeHeaderExtension{ChaincodeId: cis.ChaincodeSpec.ChaincodeId}
	ccHdrExtBytes, err := proto.Marshal(ccHdrExt)
	if err != nil {
		return nil, "", err
	}

	cisBytes, err := proto.Marshal(cis)
	if err != nil {
		return nil, "", err
	}

	ccPropPayload := &peer.ChaincodeProposalPayload{Input: cisBytes, TransientMap: transientMap}
	ccPropPayloadBytes, err := proto.Marshal(ccPropPayload)
	if err != nil {
		return nil, "", err
	}

	// TODO: epoch is now set to zero. This must be changed once we
	// get a more appropriate mechanism to handle it in.
	var epoch uint64 = 0

	timestamp := util.CreateUtcTimestamp()
    //将信息构建到ChannelHeader
	hdr := &common.Header{ChannelHeader: MarshalOrPanic(&common.ChannelHeader{
		Type:      int32(typ),
		TxId:      txid,
		Timestamp: timestamp,
		ChannelId: chainID,
		Extension: ccHdrExtBytes,
		Epoch:     epoch}),
		SignatureHeader: MarshalOrPanic(&common.SignatureHeader{Nonce: nonce, Creator: creator})}

	hdrBytes, err := proto.Marshal(hdr)
	if err != nil {
		return nil, "", err
	}

	return &peer.Proposal{Header: hdrBytes, Payload: ccPropPayloadBytes}, txid, nil
}

utils.GetSignedProposal(prop, cf.Signer)

对提案进行签名

func GetSignedProposal(prop *peer.Proposal, signer msp.SigningIdentity) (*peer.SignedProposal, error) {
	// check for nil argument
	if prop == nil || signer == nil {
		return nil, fmt.Errorf("Nil arguments")
	}

	propBytes, err := GetBytesProposal(prop)
	if err != nil {
		return nil, err
	}

	signature, err := signer.Sign(propBytes)
	if err != nil {
		return nil, err
	}

	return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil
}
func GetBytesProposal(prop *peer.Proposal) ([]byte, error) {
	propBytes, err := proto.Marshal(prop)
	return propBytes, err
}

cf.EndorserClient.ProcessProposal(context.Background(), signedProp)

提交提案并模拟执行,对结果进行背书等详细过程,访问https://blog.csdn.net/cs380637384/article/details/81906712

猜你喜欢

转载自blog.csdn.net/cs380637384/article/details/81772501