经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 区块链 » 查看文章
Fabric1.4源码解析:Peer节点加入通道
来源:cnblogs  作者:触不可及`  时间:2019/7/5 9:01:50  对本文有异议

??????又开始新的阅读了,这次看的是Peer节点加入通道的过程。其实每次看源码都会有好多没有看懂的地方,不过相信只要坚持下去,保持记录,还是有很多收获的。
??????对于Peer节点加入通道这一过程,从用户角度来说也只是简单执行一行命令:

  1. peer channel join -b mychannel.block

??????就完成了某一节点加入通道的过程。而从Fabric网络内部来讲,却是做了很多工作,接下来看一下具体的流程:
整个流程的切入点和客户端创建通道的流程相同在fabric/peer/main.go文件中的main()方法,通过执行以上命令调用到peer/channel/channel.go中的Cmd()方法,然后是peer/channel/join.go文件中的joinCmd()方法,131行的join(),最后就到了88行的executeJoin()方法,接下来就看一下该方法:

  1. spec, err := getJoinCCSpec()
  2. if err != nil {
  3. return err
  4. }

??????首先就是获取需要加入的通道的具体信息,在67行:

  1. func getJoinCCSpec() (*pb.ChaincodeSpec, error) {
  2. #判断指定路径下是否有创世区块,创世区块的创建流程可以看之前那篇解析客户端创建通道的文章
  3. if genesisBlockPath == common.UndefinedParamValue {
  4. return nil, errors.New("Must supply genesis block file")
  5. }
  6. #读取创世区块中的内容,就是通道的一些基本信息
  7. gb, err := ioutil.ReadFile(genesisBlockPath)
  8. if err != nil {
  9. return nil, GBFileNotFoundErr(err.Error())
  10. }
  11. #构造一个ChaincodeSpec结构体,第一个参数为JoinChain,指定操作为加入通道,第二个参数为创世区块的信息
  12. input := &pb.ChaincodeInput{Args: [][]byte{[]byte(cscc.JoinChain), gb}}
  13. spec := &pb.ChaincodeSpec{
  14. Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]),
  15. ChaincodeId: &pb.ChaincodeID{Name: "cscc"},
  16. Input: input,
  17. }
  18. ===================ChaincodeSpec=======================
  19. type ChaincodeSpec struct {
  20. Type ChaincodeSpec_Type `protobuf:"varint,1,opt,name=type,proto3,enum=protos.ChaincodeSpec_Type" json:"type,omitempty"`
  21. ChaincodeId *ChaincodeID `protobuf:"bytes,2,opt,name=chaincode_id,json=chaincodeId,proto3" json:"chaincode_id,omitempty"`
  22. Input *ChaincodeInput `protobuf:"bytes,3,opt,name=input,proto3" json:"input,omitempty"`
  23. Timeout int32 `protobuf:"varint,4,opt,name=timeout,proto3" json:"timeout,omitempty"`
  24. XXX_NoUnkeyedLiteral struct{} `json:"-"`
  25. XXX_unrecognized []byte `json:"-"`
  26. XXX_sizecache int32 `json:"-"`
  27. }
  28. ===================ChaincodeSpec=======================
  29. #最后返回该结构体
  30. return spec, nil
  31. }

executeJoin()方法继续往下看:

  1. invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}

根据之前创建的结构体再封装一个结构体ChaincodeInvocationSpec

  1. type ChaincodeInvocationSpec struct {
  2. ChaincodeSpec *ChaincodeSpec `protobuf:"bytes,1,opt,name=chaincode_spec,json=chaincodeSpec,proto3" json:"chaincode_spec,omitempty"`
  3. XXX_NoUnkeyedLiteral struct{} `json:"-"`
  4. XXX_unrecognized []byte `json:"-"`
  5. XXX_sizecache int32 `json:"-"`
  6. }

??????然后获取一个创建者的身份,用于之后的提案的创建与签名:

  1. creator, err := cf.Signer.Serialize()
  2. if err != nil {
  3. return fmt.Errorf("Error serializing identity for %s: %s", cf.Signer.GetIdentifier(), err)
  4. }

??????接下来就是Proposal的创建了:

  1. var prop *pb.Proposal
  2. prop, _, err = putils.CreateProposalFromCIS(pcommon.HeaderType_CONFIG, "", invocation, creator)
  3. if err != nil {
  4. return fmt.Errorf("Error creating proposal for join %s", err)
  5. }

??????具体还要看一下CreateProposalFromCIS()方法,该方法在core/protos/proputils.go文件第466行,继而调用了237行的CreateChaincodeProposal()方法,看名字应该可以理解个大概信息,创建一个链码提案。然后是243行的CreateChaincodeProposalWithTransient()方法:

  1. func CreateChaincodeProposalWithTransient(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
  2. #首先就是生成一个随机数
  3. nonce, err := crypto.GetRandomNonce()
  4. if err != nil {
  5. return nil, "", err
  6. }
  7. #计算出一个TxID,具体是根据HASH算法生成的
  8. txid, err := ComputeTxID(nonce, creator)
  9. if err != nil {
  10. return nil, "", err
  11. }
  12. #然后调用了这个方法,将之前生成的数据传入进去
  13. return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, chainID, cis, nonce, creator, transientMap)
  14. }

CreateChaincodeProposalWithTxIDNonceAndTransient()方法在第282行:
??????首先看一下该方法传入的值:txid由之前的方法生成,typ最初的方法传入进来,值为HeaderType_CONFIG,chainID为空字符串,cis也是最初的方法传入进来,值为ChaincodeInvocationSpec结构体,nonce由之前的方法生成,creator也是最初的方法传入进来,transientMap为空,在之前的CreateChaincodeProposal()方法中可以看到。然后我们看一下方法中的具体流程:

  1. func CreateChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, nonce, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
  2. #首先是构造一个ChaincodeHeaderExtension结构体
  3. ccHdrExt := &peer.ChaincodeHeaderExtension{ChaincodeId: cis.ChaincodeSpec.ChaincodeId}
  4. =========================ChaincodeHeaderExtension=====================
  5. type ChaincodeHeaderExtension struct {
  6. PayloadVisibility []byte `protobuf:"bytes,1,opt,name=payload_visibility,json=payloadVisibility,proto3" json:"payload_visibility,omitempty"`
  7. // The ID of the chaincode to target.
  8. ChaincodeId *ChaincodeID `protobuf:"bytes,2,opt,name=chaincode_id,json=chaincodeId,proto3" json:"chaincode_id,omitempty"`
  9. XXX_NoUnkeyedLiteral struct{} `json:"-"`
  10. XXX_unrecognized []byte `json:"-"`
  11. XXX_sizecache int32 `json:"-"`
  12. }
  13. =========================ChaincodeHeaderExtension=====================
  14. #将该结构体序列化
  15. ccHdrExtBytes, err := proto.Marshal(ccHdrExt)
  16. if err != nil {
  17. return nil, "", errors.Wrap(err, "error marshaling ChaincodeHeaderExtension")
  18. }
  19. #将ChaincodeInvocationSpec结构体序列化
  20. cisBytes, err := proto.Marshal(cis)
  21. if err != nil {
  22. return nil, "", errors.Wrap(err, "error marshaling ChaincodeInvocationSpec")
  23. }
  24. #又是一个结构体
  25. ccPropPayload := &peer.ChaincodeProposalPayload{Input: cisBytes, TransientMap: transientMap}
  26. ============================ChaincodeProposalPayload=====================
  27. type ChaincodeProposalPayload struct {
  28. Input []byte `protobuf:"bytes,1,opt,name=input,proto3" json:"input,omitempty"`
  29. TransientMap map[string][]byte `protobuf:"bytes,2,rep,name=TransientMap,proto3" json:"TransientMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
  30. XXX_NoUnkeyedLiteral struct{} `json:"-"`
  31. XXX_unrecognized []byte `json:"-"`
  32. XXX_sizecache int32 `json:"-"`
  33. }
  34. ============================ChaincodeProposalPayload=====================
  35. #将该结构体序列化
  36. ccPropPayloadBytes, err := proto.Marshal(ccPropPayload)
  37. if err != nil {
  38. return nil, "", errors.Wrap(err, "error marshaling ChaincodeProposalPayload")
  39. }
  40. var epoch uint64
  41. #创建一个时间戳
  42. timestamp := util.CreateUtcTimestamp()
  43. #构造Header结构体,包含两部分ChannelHeader和SignatureHeader
  44. hdr := &common.Header{
  45. ChannelHeader: MarshalOrPanic(
  46. &common.ChannelHeader{
  47. Type: int32(typ),
  48. TxId: txid,
  49. Timestamp: timestamp,
  50. ChannelId: chainID,
  51. Extension: ccHdrExtBytes,
  52. Epoch: epoch,
  53. },
  54. ),
  55. SignatureHeader: MarshalOrPanic(
  56. &common.SignatureHeader{
  57. Nonce: nonce,
  58. Creator: creator,
  59. },
  60. ),
  61. }
  62. #序列化
  63. hdrBytes, err := proto.Marshal(hdr)
  64. if err != nil {
  65. return nil, "", err
  66. }
  67. #最后构造成一个Proposal
  68. prop := &peer.Proposal{
  69. Header: hdrBytes,
  70. Payload: ccPropPayloadBytes,
  71. }
  72. #返回Proposal,这里一直返回到最外面的方法
  73. return prop, txid, nil
  74. }

??????让我们回到executeJoin()方法,继续往下看:

  1. #刚刚这行代码返回了创建的Proposal
  2. prop, _, err = putils.CreateProposalFromCIS(pcommon.HeaderType_CONFIG, "", invocation, creator)
  3. if err != nil {
  4. return fmt.Errorf("Error creating proposal for join %s", err)
  5. }
  6. #定义一个被签名的Proposal
  7. var signedProp *pb.SignedProposal
  8. #这个方法就是对创建的Proposal进行签名了,具体的就不再看了,继续往下
  9. signedProp, err = putils.GetSignedProposal(prop, cf.Signer)
  10. if err != nil {
  11. return fmt.Errorf("Error creating signed proposal %s", err)
  12. }
  13. #定义了个提案响应
  14. var proposalResp *pb.ProposalResponse
  15. #重要的方法,由Peer节点对刚刚创建的提案进行处理,处理完成后返回提案响应,之前有篇文章对这个方法进行了讲解,在文章最后贴出了地址,这里就不再说明了
  16. proposalResp, err = cf.EndorserClient.ProcessProposal(context.Background(), signedProp)
  17. #后面的比较简单了,就是根据返回的响应消息进行处理,就不再说明了
  18. if err != nil {
  19. return ProposalFailedErr(err.Error())
  20. }
  21. if proposalResp == nil {
  22. return ProposalFailedErr("nil proposal response")
  23. }
  24. if proposalResp.Response.Status != 0 && proposalResp.Response.Status != 200 {
  25. return ProposalFailedErr(fmt.Sprintf("bad proposal response %d: %s", proposalResp.Response.Status, proposalResp.Response.Message))
  26. }
  27. logger.Info("Successfully submitted proposal to join channel")
  28. return nil
  29. }

??????到这里Peer节点加入通道的操作就已经结束了,我们总结一下之前所做的工作:

  1. 首先就是由peer channel join -b mychannel.block这条命令触发,经过多次调用最后到executeJoin()方法。
  2. 首先获取mychannel.block文件中的信息,封闭为ChaincodeSpec结构体。
  3. 然后再封装为ChaincodeInvocationSpec结构体。
  4. 获取一个用于发起提案与对提案进行签名操作的creator
  5. 生成nonce与TxID,进而封装为ChaincodeHeaderExtension,ChaincodeProposalPayload,HeaderProposal结构体。
  6. 对生成的Proposal结构体进行签名操作,由Peer节点进行处理,处理完成后返回响应消息。

??????对于Peer节点进行消息处理的方法ProcessProposal在这篇文章中:Fabric1.4源码解析:Peer节点背书提案过程

??????这里给出一个类图好了,之前有太多的结构体,关系有点复杂:

该图片来源:https://github.com/yeasy/hyperledger_code_fabric/blob/master/peer/_images/signed_proposal.png

最后给出参考文档

原文链接:http://www.cnblogs.com/cbkj-xd/p/11131677.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号