经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 区块链 » 查看文章
Fabric1.4源码解析:链码实例化过程
来源:cnblogs  作者:触不可及`  时间:2019/7/8 12:17:51  对本文有异议

之前说完了链码的安装过程,接下来说一下链码的实例化过程好了,再然后是链码的调用过程。其实这几个过程内容已经很相似了,都是涉及到Proposal,不过整体流程还是要说一下的。
同样,切入点仍然是fabric/peer/main.go文件中的main()方法:

  1. #这一句定义了关于通过Peer节点操作链码的命令
  2. mainCmd.AddCommand(chaincode.Cmd(nil))

然后是fabric/peer/chaincode/chaincode.go文件中的Cmd()方法,这里则是具体的操作链码的命令,其中就有对链码进行实例化的命令:

  1. chaincodeCmd.AddCommand(instantiateCmd(cf))

最后调用到了fabric/peer/chaincode/instantiate.go文件中的第57行的instantiate()方法。也就是说,当我们在Peer节点执行以下命令时,最终会到这个方法:

  1. #以官方的实例化链码的方法为例
  2. 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()方法:

  1. #首先获取要实例化的链码的信息
  2. spec, err := getChaincodeSpec(cmd)
  3. if err != nil {
  4. return nil, err
  5. }

getChaincodeSpec()方法在peer/chaincode/common.go文件中第69行:

  1. #将用户实例化链码所执行的命令作为参数传入进去
  2. func getChaincodeSpec(cmd *cobra.Command) (*pb.ChaincodeSpec, error) {
  3. #定义一个ChaincodeSpec结构体
  4. spec := &pb.ChaincodeSpec{}
  5. ====================ChaincodeSpec===========================
  6. type ChaincodeSpec struct {
  7. #Type表示链码的类型 有GOLANG,NODE,CAR,JAVA,UNDEFINED五种类型
  8. Type ChaincodeSpec_Type `protobuf:"varint,1,opt,name=type,proto3,enum=protos.ChaincodeSpec_Type" json:"type,omitempty"`
  9. #ChaincodeId也是一个结构体,定义了链码的路径信息,链码的名称以及版本信息
  10. ChaincodeId *ChaincodeID `protobuf:"bytes,2,opt,name=chaincode_id,json=chaincodeId,proto3" json:"chaincode_id,omitempty"`
  11. #ChaincodeInput结构体中定义链码的功能以及函数参数信息
  12. Input *ChaincodeInput `protobuf:"bytes,3,opt,name=input,proto3" json:"input,omitempty"`
  13. Timeout int32 `protobuf:"varint,4,opt,name=timeout,proto3" json:"timeout,omitempty"`
  14. XXX_NoUnkeyedLiteral struct{} `json:"-"`
  15. XXX_unrecognized []byte `json:"-"`
  16. XXX_sizecache int32 `json:"-"`
  17. }
  18. ====================ChaincodeSpec===========================
  19. #对用户输入的命令进行检查
  20. if err := checkChaincodeCmdParams(cmd); err != nil {
  21. // unset usage silence because it's a command line usage error
  22. cmd.SilenceUsage = false
  23. return spec, err
  24. }
  25. #定义ChaincodeInput结构体,就是上面说过的那个
  26. input := &pb.ChaincodeInput{}
  27. if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil {
  28. return spec, errors.Wrap(err, "chaincode argument error")
  29. }
  30. chaincodeLang = strings.ToUpper(chaincodeLang)
  31. #最后将创建的ChaincodeSpec结构体返回
  32. spec = &pb.ChaincodeSpec{
  33. Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]),
  34. ChaincodeId: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName, Version: chaincodeVersion},
  35. Input: input,
  36. }
  37. return spec, nil
  38. }

看一下checkChaincodeCmdParams()方法做了哪些工作,在219行:

  1. func checkChaincodeCmdParams(cmd *cobra.Command) error {
  2. #检查用户输入的链码名称是否为空字符串
  3. if chaincodeName == common.UndefinedParamValue {
  4. return errors.Errorf("must supply value for %s name parameter", chainFuncName)
  5. }
  6. #调用的方法是否为instantiate,install,upgrade,package其中的一个
  7. if cmd.Name() == instantiateCmdName || cmd.Name() == installCmdName ||
  8. cmd.Name() == upgradeCmdName || cmd.Name() == packageCmdName {
  9. if chaincodeVersion == common.UndefinedParamValue {
  10. return errors.Errorf("chaincode version is not provided for %s", cmd.Name())
  11. }
  12. if escc != common.UndefinedParamValue {
  13. logger.Infof("Using escc %s", escc)
  14. } else {
  15. logger.Info("Using default escc")
  16. escc = "escc"
  17. }
  18. if vscc != common.UndefinedParamValue {
  19. logger.Infof("Using vscc %s", vscc)
  20. } else {
  21. logger.Info("Using default vscc")
  22. vscc = "vscc"
  23. }
  24. if policy != common.UndefinedParamValue {
  25. #获取定义的策略,就比如 OR ('Org1MSP.member','Org2MSP.member')这条信息是否有误
  26. p, err := cauthdsl.FromString(policy)
  27. if err != nil {
  28. return errors.Errorf("invalid policy %s", policy)
  29. }
  30. policyMarshalled = putils.MarshalOrPanic(p)
  31. }
  32. #如果定义了配置文件,则从配置文件中读取配置信息
  33. if collectionsConfigFile != common.UndefinedParamValue {
  34. var err error
  35. collectionConfigBytes, err = getCollectionConfigFromFile(collectionsConfigFile)
  36. if err != nil {
  37. return errors.WithMessage(err, fmt.Sprintf("invalid collection configuration in file %s", collectionsConfigFile))
  38. }
  39. }
  40. }
  41. #对用户传入的实例化参数比如:-c '{"Args":["init","a","100","b","200"]}'
  42. if chaincodeCtorJSON != "{}" {
  43. ...
  44. }
  45. return nil
  46. }

回到instantiate()方法:

  1. cds, err := getChaincodeDeploymentSpec(spec, false)
  2. if err != nil {
  3. return nil, fmt.Errorf("error getting chaincode code %s: %s", chaincodeName, err)
  4. }

获取ChaincodeDeploymentSpec这个结构体:

  1. type ChaincodeDeploymentSpec struct {
  2. #这个是之前获取到的结构体
  3. ChaincodeSpec *ChaincodeSpec `protobuf:"bytes,1,opt,name=chaincode_spec,json=chaincodeSpec,proto3" json:"chaincode_spec,omitempty"`
  4. #链码数据
  5. CodePackage []byte `protobuf:"bytes,3,opt,name=code_package,json=codePackage,proto3" json:"code_package,omitempty"`
  6. #链码的运行环境,有两种,Docker容器或者直接在系统中运行
  7. ExecEnv ChaincodeDeploymentSpec_ExecutionEnvironment `protobuf:"varint,4,opt,name=exec_env,json=execEnv,proto3,enum=protos.ChaincodeDeploymentSpec_ExecutionEnvironment" json:"exec_env,omitempty"`
  8. XXX_NoUnkeyedLiteral struct{} `json:"-"`
  9. XXX_unrecognized []byte `json:"-"`
  10. XXX_sizecache int32 `json:"-"`
  11. }

看一下如何获取ChaincodeDeploymentSpec结构体:

  1. #定义了ChaincodeDeploymentSpec中的CodePackage
  2. var codePackageBytes []byte
  3. #判断是否为开发模式
  4. if chaincode.IsDevMode() == false && crtPkg {
  5. var err error
  6. #如果不是则检查链码是否为空,以及路径是否正确
  7. if err = checkSpec(spec); err != nil {
  8. return nil, err
  9. }
  10. #将链码转换为Byte数据
  11. codePackageBytes, err = container.GetChaincodePackageBytes(platformRegistry, spec)
  12. ...
  13. }
  14. #构造chaincodeDeploymentSpec并返回
  15. chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes}
  16. return chaincodeDeploymentSpec, nil

回到instantiate()方法:

  1. #获取一全个签名者,需要对创建实例化链码的Proposal进行签名
  2. creator, err := cf.Signer.Serialize()
  3. if err != nil {
  4. return nil, fmt.Errorf("error serializing identity for %s: %s", cf.Signer.GetIdentifier(), err)
  5. }
  6. #要创建用于实例化链码的Proposal了
  7. prop, _, err := utils.CreateDeployProposalFromCDS(channelID, cds, creator, policyMarshalled, []byte(escc), []byte(vscc), collectionConfigBytes)
  8. if err != nil {
  9. return nil, fmt.Errorf("error creating proposal %s: %s", chainFuncName, err)
  10. }

看一下CreateDeployProposalFromCDS()方法,看名字了解到是根据chaincodeDeploymentSpec创建用于部署链码的Proposal

  1. func CreateDeployProposalFromCDS(
  2. #通道Id
  3. chainID string,
  4. cds *peer.ChaincodeDeploymentSpec,
  5. #签名者
  6. creator []byte,
  7. #具体的策略
  8. policy []byte,
  9. #endorser system chaincode
  10. escc []byte,
  11. #Verification System ChainCode
  12. vscc []byte,
  13. collectionConfig []byte) (*peer.Proposal, string, error) {
  14. #下面的两个方法调用的是同一个,只是传入的参数不同,点进去
  15. if collectionConfig == nil {
  16. return createProposalFromCDS(chainID, cds, creator, "deploy", policy, escc, vscc)
  17. }
  18. return createProposalFromCDS(chainID, cds, creator, "deploy", policy, escc, vscc, collectionConfig)
  19. }

该方法在538行,接下来的部分与客户端安装链码所执行的流程基本是相同的,只有下面的一部分不同:

  1. #对于实例化链码来说,执行的是deploy与upgrade这两部分,而安装链码则是install这部分,差异就在于ChaincodeInput结构体内的参数不同
  2. case "deploy":
  3. fallthrough
  4. case "upgrade":
  5. cds, ok := msg.(*peer.ChaincodeDeploymentSpec)
  6. if !ok || cds == nil {
  7. return nil, "", errors.New("invalid message for creating lifecycle chaincode proposal")
  8. }
  9. Args := [][]byte{[]byte(propType), []byte(chainID), b}
  10. Args = append(Args, args...)
  11. ccinp = &peer.ChaincodeInput{Args: Args}
  12. case "install":
  13. ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), b}}
  14. }
  15. // wrap the deployment in an invocation spec to lscc...
  16. lsccSpec := &peer.ChaincodeInvocationSpec{
  17. ChaincodeSpec: &peer.ChaincodeSpec{
  18. Type: peer.ChaincodeSpec_GOLANG,
  19. ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
  20. Input: ccinp,
  21. },
  22. }

剩下的部分就不再重复看了,可以参考Fabric1.4源码解析:客户端安装链码这篇文章。
总的来说,整个流程共有以下几部分:

  1. 根据用户执行实例化链码的命令启动全过程
  2. 获取需要实例化链码的基本信息
  3. 创建ChaincodeDeploymentSpec结构体.
  4. 获取用于对Proposal进行签名的Creator
  5. 创建ProposalProposalHeader定义为ENDORSER_TRANSACTION,表示是一个需要背书的交易。
  6. 由之前获取的Creator进行签名操作。
  7. Peer节点调用ProcessProposal()方法进行处理,该方法的解析在这里。这是一个很重要的方法。
  8. 接收到由Peer节点处理完成所返回的Response消息后发送到Orderer节点。
  9. Orderer节点接收到消息后进行排序操作,如果是SOLO模式则由Orderer节点生成区块,最后将区块广播至Peer节点,
  10. Peer节点接收到区块消息后验证有效性,最后更新账本数据。

最后附上参考链接:1.传送门
2.传送门

原文链接:http://www.cnblogs.com/cbkj-xd/p/11149791.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号