以前说完了链码的安装过程,接下来讲一下链码的实例化过程好了,再而后是链码的调用过程。其实这几个过程内容已经很类似了,都是涉及到Proposal
,不过总体流程仍是要说一下的。 一样,切入点仍然是fabric/peer/main.go
文件中的main()
方法:html
#这一句定义了关于经过Peer节点操做链码的命令 mainCmd.AddCommand(chaincode.Cmd(nil))
而后是fabric/peer/chaincode/chaincode.go
文件中的Cmd()
方法,这里则是具体的操做链码的命令,其中就有对链码进行实例化的命令:git
chaincodeCmd.AddCommand(instantiateCmd(cf))
最后调用到了fabric/peer/chaincode/instantiate.go
文件中的第57行的instantiate()
方法。也就是说,当咱们在Peer
节点执行如下命令时,最终会到这个方法:github
#以官方的实例化链码的方法为例 peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')"
接下来就看一下instantiate()
方法:json
#首先获取要实例化的链码的信息 spec, err := getChaincodeSpec(cmd) if err != nil { return nil, err }
getChaincodeSpec()
方法在peer/chaincode/common.go
文件中第69行:app
#将用户实例化链码所执行的命令做为参数传入进去 func getChaincodeSpec(cmd *cobra.Command) (*pb.ChaincodeSpec, error) { #定义一个ChaincodeSpec结构体 spec := &pb.ChaincodeSpec{} ====================ChaincodeSpec=========================== type ChaincodeSpec struct { #Type表示链码的类型 有GOLANG,NODE,CAR,JAVA,UNDEFINED五种类型 Type ChaincodeSpec_Type `protobuf:"varint,1,opt,name=type,proto3,enum=protos.ChaincodeSpec_Type" json:"type,omitempty"` #ChaincodeId也是一个结构体,定义了链码的路径信息,链码的名称以及版本信息 ChaincodeId *ChaincodeID `protobuf:"bytes,2,opt,name=chaincode_id,json=chaincodeId,proto3" json:"chaincode_id,omitempty"` #ChaincodeInput结构体中定义链码的功能以及函数参数信息 Input *ChaincodeInput `protobuf:"bytes,3,opt,name=input,proto3" json:"input,omitempty"` Timeout int32 `protobuf:"varint,4,opt,name=timeout,proto3" json:"timeout,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } ====================ChaincodeSpec=========================== #对用户输入的命令进行检查 if err := checkChaincodeCmdParams(cmd); err != nil { // unset usage silence because it's a command line usage error cmd.SilenceUsage = false return spec, err } #定义ChaincodeInput结构体,就是上面说过的那个 input := &pb.ChaincodeInput{} if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil { return spec, errors.Wrap(err, "chaincode argument error") } chaincodeLang = strings.ToUpper(chaincodeLang) #最后将建立的ChaincodeSpec结构体返回 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 }
看一下checkChaincodeCmdParams()
方法作了哪些工做,在219行:ide
func checkChaincodeCmdParams(cmd *cobra.Command) error { #检查用户输入的链码名称是否为空字符串 if chaincodeName == common.UndefinedParamValue { return errors.Errorf("must supply value for %s name parameter", chainFuncName) } #调用的方法是否为instantiate,install,upgrade,package其中的一个 if cmd.Name() == instantiateCmdName || cmd.Name() == installCmdName || cmd.Name() == upgradeCmdName || cmd.Name() == packageCmdName { if chaincodeVersion == common.UndefinedParamValue { return errors.Errorf("chaincode version is not provided for %s", cmd.Name()) } if escc != common.UndefinedParamValue { logger.Infof("Using escc %s", escc) } else { logger.Info("Using default escc") escc = "escc" } if vscc != common.UndefinedParamValue { logger.Infof("Using vscc %s", vscc) } else { logger.Info("Using default vscc") vscc = "vscc" } if policy != common.UndefinedParamValue { #获取定义的策略,就好比 OR ('Org1MSP.member','Org2MSP.member')这条信息是否有误 p, err := cauthdsl.FromString(policy) if err != nil { return errors.Errorf("invalid policy %s", policy) } policyMarshalled = putils.MarshalOrPanic(p) } #若是定义了配置文件,则从配置文件中读取配置信息 if collectionsConfigFile != common.UndefinedParamValue { var err error collectionConfigBytes, err = getCollectionConfigFromFile(collectionsConfigFile) if err != nil { return errors.WithMessage(err, fmt.Sprintf("invalid collection configuration in file %s", collectionsConfigFile)) } } } #对用户传入的实例化参数好比:-c '{"Args":["init","a","100","b","200"]}' if chaincodeCtorJSON != "{}" { ... } return nil }
回到instantiate()
方法:函数
cds, err := getChaincodeDeploymentSpec(spec, false) if err != nil { return nil, fmt.Errorf("error getting chaincode code %s: %s", chaincodeName, err) }
获取ChaincodeDeploymentSpec
这个结构体:code
type ChaincodeDeploymentSpec struct { #这个是以前获取到的结构体 ChaincodeSpec *ChaincodeSpec `protobuf:"bytes,1,opt,name=chaincode_spec,json=chaincodeSpec,proto3" json:"chaincode_spec,omitempty"` #链码数据 CodePackage []byte `protobuf:"bytes,3,opt,name=code_package,json=codePackage,proto3" json:"code_package,omitempty"` #链码的运行环境,有两种,Docker容器或者直接在系统中运行 ExecEnv ChaincodeDeploymentSpec_ExecutionEnvironment `protobuf:"varint,4,opt,name=exec_env,json=execEnv,proto3,enum=protos.ChaincodeDeploymentSpec_ExecutionEnvironment" json:"exec_env,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` }
看一下如何获取ChaincodeDeploymentSpec
结构体:orm
#定义了ChaincodeDeploymentSpec中的CodePackage var codePackageBytes []byte #判断是否为开发模式 if chaincode.IsDevMode() == false && crtPkg { var err error #若是不是则检查链码是否为空,以及路径是否正确 if err = checkSpec(spec); err != nil { return nil, err } #将链码转换为Byte数据 codePackageBytes, err = container.GetChaincodePackageBytes(platformRegistry, spec) ... } #构造chaincodeDeploymentSpec并返回 chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes} return chaincodeDeploymentSpec, nil
回到instantiate()
方法:htm
#获取一全个签名者,须要对建立实例化链码的Proposal进行签名 creator, err := cf.Signer.Serialize() if err != nil { return nil, fmt.Errorf("error serializing identity for %s: %s", cf.Signer.GetIdentifier(), err) } #要建立用于实例化链码的Proposal了 prop, _, err := utils.CreateDeployProposalFromCDS(channelID, cds, creator, policyMarshalled, []byte(escc), []byte(vscc), collectionConfigBytes) if err != nil { return nil, fmt.Errorf("error creating proposal %s: %s", chainFuncName, err) }
看一下CreateDeployProposalFromCDS()
方法,看名字了解到是根据chaincodeDeploymentSpec
建立用于部署链码的Proposal
:
func CreateDeployProposalFromCDS( #通道Id chainID string, cds *peer.ChaincodeDeploymentSpec, #签名者 creator []byte, #具体的策略 policy []byte, #endorser system chaincode escc []byte, #Verification System ChainCode vscc []byte, collectionConfig []byte) (*peer.Proposal, string, error) { #下面的两个方法调用的是同一个,只是传入的参数不一样,点进去 if collectionConfig == nil { return createProposalFromCDS(chainID, cds, creator, "deploy", policy, escc, vscc) } return createProposalFromCDS(chainID, cds, creator, "deploy", policy, escc, vscc, collectionConfig) }
该方法在538行,接下来的部分与客户端安装链码所执行的流程基本是相同的,只有下面的一部分不一样:
#对于实例化链码来讲,执行的是deploy与upgrade这两部分,而安装链码则是install这部分,差别就在于ChaincodeInput结构体内的参数不一样 case "deploy": fallthrough case "upgrade": cds, ok := msg.(*peer.ChaincodeDeploymentSpec) if !ok || cds == nil { return nil, "", errors.New("invalid message for creating lifecycle chaincode proposal") } Args := [][]byte{[]byte(propType), []byte(chainID), b} Args = append(Args, args...) ccinp = &peer.ChaincodeInput{Args: Args} case "install": ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), b}} } // wrap the deployment in an invocation spec to lscc... lsccSpec := &peer.ChaincodeInvocationSpec{ ChaincodeSpec: &peer.ChaincodeSpec{ Type: peer.ChaincodeSpec_GOLANG, ChaincodeId: &peer.ChaincodeID{Name: "lscc"}, Input: ccinp, }, }
剩下的部分就再也不重复看了,能够参考Fabric1.4源码解析:客户端安装链码这篇文章。 总的来讲,整个流程共有如下几部分:
ChaincodeDeploymentSpec
结构体.Proposal
进行签名的Creator
。Proposal
,Proposal
的Header
定义为ENDORSER_TRANSACTION
,表示是一个须要背书的交易。Creator
进行签名操做。Peer
节点调用ProcessProposal()
方法进行处理,该方法的解析在这里。这是一个很重要的方法。Peer
节点处理完成所返回的Response
消息后发送到Orderer
节点。Orderer
节点接收到消息后进行排序操做,若是是SOLO
模式则由Orderer
节点生成区块,最后将区块广播至Peer
节点,Peer
节点接收到区块消息后验证有效性,最后更新帐本数据。原文出处:https://www.cnblogs.com/cbkj-xd/p/11149791.html