Fabric1.4源码解析:Peer节点加入通道
又开始新的阅读了,这次看的是Peer节点加入通道的过程。其实每次看源码都会有好多没有看懂的地方,不过相信只要坚持下去,保持记录,还是有很多收获的。
对于Peer节点加入通道这一过程,从用户角度来说也只是简单执行一行命令:
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()方法,接下来就看一下该方法:
spec, err := getJoinCCSpec()
if err != nil {
return err
}
首先就是获取需要加入的通道的具体信息,在67行:
func getJoinCCSpec() (*pb.ChaincodeSpec, error) {
#判断指定路径下是否有创世区块,创世区块的创建流程可以看之前那篇解析客户端创建通道的文章
if genesisBlockPath == common.UndefinedParamValue {
return nil, errors.New("Must supply genesis block file")
}
#读取创世区块中的内容,就是通道的一些基本信息
gb, err := ioutil.ReadFile(genesisBlockPath)
if err != nil {
return nil, GBFileNotFoundErr(err.Error())
}
#构造一个ChaincodeSpec结构体,第一个参数为JoinChain,指定操作为加入通道,第二个参数为创世区块的信息
input := &pb.ChaincodeInput{Args: [][]byte{[]byte(cscc.JoinChain), gb}}
spec := &pb.ChaincodeSpec{
Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]),
ChaincodeId: &pb.ChaincodeID{Name: "cscc"},
Input: input,
}
===================ChaincodeSpec=======================
type ChaincodeSpec struct {
Type ChaincodeSpec_Type `protobuf:"varint,1,opt,name=type,proto3,enum=protos.ChaincodeSpec_Type" json:"type,omitempty"`
ChaincodeId *ChaincodeID `protobuf:"bytes,2,opt,name=chaincode_id,json=chaincodeId,proto3" json:"chaincode_id,omitempty"`
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=======================
#最后返回该结构体
return spec, nil
}
executeJoin()方法继续往下看:
invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
根据之前创建的结构体再封装一个结构体ChaincodeInvocationSpec:
type ChaincodeInvocationSpec struct {
ChaincodeSpec *ChaincodeSpec `protobuf:"bytes,1,opt,name=chaincode_spec,json=chaincodeSpec,proto3" json:"chaincode_spec,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
然后获取一个创建者的身份,用于之后的提案的创建与签名:
creator, err := cf.Signer.Serialize()
if err != nil {
return fmt.Errorf("Error serializing identity for %s: %s", cf.Signer.GetIdentifier(), err)
}
接下来就是Proposal的创建了:
var prop *pb.Proposal
prop, _, err = putils.CreateProposalFromCIS(pcommon.HeaderType_CONFIG, "", invocation, creator)
if err != nil {
return fmt.Errorf("Error creating proposal for join %s", err)
}
具体还要看一下CreateProposalFromCIS()方法,该方法在core/protos/proputils.go文件第466行,继而调用了237行的CreateChaincodeProposal()方法,看名字应该可以理解个大概信息,创建一个链码提案。然后是243行的CreateChaincodeProposalWithTransient()方法:
func CreateChaincodeProposalWithTransient(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
#首先就是生成一个随机数
nonce, err := crypto.GetRandomNonce()
if err != nil {
return nil, "", err
}
#计算出一个TxID,具体是根据HASH算法生成的
txid, err := ComputeTxID(nonce, creator)
if err != nil {
return nil, "", err
}
#然后调用了这个方法,将之前生成的数据传入进去
return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, chainID, cis, nonce, creator, transientMap)
}
CreateChaincodeProposalWithTxIDNonceAndTransient()方法在第282行:
首先看一下该方法传入的值:txid由之前的方法生成,typ最初的方法传入进来,值为HeaderType_CONFIG,chainID为空字符串,cis也是最初的方法传入进来,值为ChaincodeInvocationSpec结构体,nonce由之前的方法生成,creator也是最初的方法传入进来,transientMap为空,在之前的CreateChaincodeProposal()方法中可以看到。然后我们看一下方法中的具体流程:
func CreateChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, nonce, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
#首先是构造一个ChaincodeHeaderExtension结构体
ccHdrExt := &peer.ChaincodeHeaderExtension{ChaincodeId: cis.ChaincodeSpec.ChaincodeId}
=========================ChaincodeHeaderExtension=====================
type ChaincodeHeaderExtension struct {
PayloadVisibility []byte `protobuf:"bytes,1,opt,name=payload_visibility,json=payloadVisibility,proto3" json:"payload_visibility,omitempty"`
// The ID of the chaincode to target.
ChaincodeId *ChaincodeID `protobuf:"bytes,2,opt,name=chaincode_id,json=chaincodeId,proto3" json:"chaincode_id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
=========================ChaincodeHeaderExtension=====================
#将该结构体序列化
ccHdrExtBytes, err := proto.Marshal(ccHdrExt)
if err != nil {
return nil, "", errors.Wrap(err, "error marshaling ChaincodeHeaderExtension")
}
#将ChaincodeInvocationSpec结构体序列化
cisBytes, err := proto.Marshal(cis)
if err != nil {
return nil, "", errors.Wrap(err, "error marshaling ChaincodeInvocationSpec")
}
#又是一个结构体
ccPropPayload := &peer.ChaincodeProposalPayload{Input: cisBytes, TransientMap: transientMap}
============================ChaincodeProposalPayload=====================
type ChaincodeProposalPayload struct {
Input []byte `protobuf:"bytes,1,opt,name=input,proto3" json:"input,omitempty"`
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"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
============================ChaincodeProposalPayload=====================
#将该结构体序列化
ccPropPayloadBytes, err := proto.Marshal(ccPropPayload)
if err != nil {
return nil, "", errors.Wrap(err, "error marshaling ChaincodeProposalPayload")
}
var epoch uint64
#创建一个时间戳
timestamp := util.CreateUtcTimestamp()
#构造Header结构体,包含两部分ChannelHeader和SignatureHeader
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
}
#最后构造成一个Proposal
prop := &peer.Proposal{
Header: hdrBytes,
Payload: ccPropPayloadBytes,
}
#返回Proposal,这里一直返回到最外面的方法
return prop, txid, nil
}
让我们回到executeJoin()方法,继续往下看:
#刚刚这行代码返回了创建的Proposal
prop, _, err = putils.CreateProposalFromCIS(pcommon.HeaderType_CONFIG, "", invocation, creator)
if err != nil {
return fmt.Errorf("Error creating proposal for join %s", err)
}
#定义一个被签名的Proposal
var signedProp *pb.SignedProposal
#这个方法就是对创建的Proposal进行签名了,具体的就不再看了,继续往下
signedProp, err = putils.GetSignedProposal(prop, cf.Signer)
if err != nil {
return fmt.Errorf("Error creating signed proposal %s", err)
}
#定义了个提案响应
var proposalResp *pb.ProposalResponse
#重要的方法,由Peer节点对刚刚创建的提案进行处理,处理完成后返回提案响应,之前有篇文章对这个方法进行了讲解,在文章最后贴出了地址,这里就不再说明了
proposalResp, err = cf.EndorserClient.ProcessProposal(context.Background(), signedProp)
#后面的比较简单了,就是根据返回的响应消息进行处理,就不再说明了
if err != nil {
return ProposalFailedErr(err.Error())
}
if proposalResp == nil {
return ProposalFailedErr("nil proposal response")
}
if proposalResp.Response.Status != 0 && proposalResp.Response.Status != 200 {
return ProposalFailedErr(fmt.Sprintf("bad proposal response %d: %s", proposalResp.Response.Status, proposalResp.Response.Message))
}
logger.Info("Successfully submitted proposal to join channel")
return nil
}
到这里Peer节点加入通道的操作就已经结束了,我们总结一下之前所做的工作:
- 首先就是由
peer channel join -b mychannel.block这条命令触发,经过多次调用最后到executeJoin()方法。 - 首先获取
mychannel.block文件中的信息,封闭为ChaincodeSpec结构体。 - 然后再封装为
ChaincodeInvocationSpec结构体。 - 获取一个用于发起提案与对提案进行签名操作的
creator。 - 生成nonce与TxID,进而封装为
ChaincodeHeaderExtension,ChaincodeProposalPayload,Header,Proposal结构体。 - 对生成的
Proposal结构体进行签名操作,由Peer节点进行处理,处理完成后返回响应消息。
对于Peer节点进行消息处理的方法ProcessProposal在这篇文章中:Fabric1.4源码解析:Peer节点背书提案过程
这里给出一个类图好了,之前有太多的结构体,关系有点复杂:

该图片来源:https://github.com/yeasy/hyperledger_code_fabric/blob/master/peer/_images/signed_proposal.png
最后给出参考文档
Fabric1.4源码解析:Peer节点加入通道的更多相关文章
- Fabric1.4源码解析:客户端创建通道过程
在使用Fabric创建通道的时候,通常我们执行一条命令完成,这篇文章就解析一下执行这条命令后Fabric源码中执行的流程. peer channel create -o orderer.example ...
- Fabric1.4源码解析:Peer节点背书提案过程
以前从来没有写过博客,从这段时间开始才开始写一些自己的博客,之前总觉得写一篇博客要耗费大量的时间,而且写的还是自己已经学会的,觉得没什么必要.但是当开始用博客记录下来的时候,才发现有些学会的地方只是自 ...
- Fabric1.4源码解析:链码实例化过程
之前说完了链码的安装过程,接下来说一下链码的实例化过程好了,再然后是链码的调用过程.其实这几个过程内容已经很相似了,都是涉及到Proposal,不过整体流程还是要说一下的. 同样,切入点仍然是fabr ...
- Fabric1.4源码解析:客户端安装链码
看了看客户端安装链码的部分,感觉还是比较简单的,所以在这里记录一下. 还是先给出安装链码所使用的命令好了,这里就使用官方的安装链码的一个例子: #-n 指定mycc是由用户定义 ...
- Mybatis 系列3-结合源码解析properties节点和environments节点
[Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...
- 菜鸟系列Fabric源码学习 — peer节点启动
Fabric 1.4 源码分析peer节点启动 peer模块采用cobra库来实现cli命令. Cobra提供简单的接口来创建强大的现代化CLI接口,比如git与go工具.Cobra同时也是一个程序, ...
- Fabric1.4源码解析:Peer节点启动过程
看一下Peer节点的启动过程,通常在Fabric网络中,Peer节点的启动方式有两种,通过Docker容器启动,或者是通过执行命令直接启动. 一般情况下,我们都是执行docker-compose -f ...
- Fabric1.4源码解析: 链码容器启动过程
想写点东西记录一下最近看的一些Fabric源码,本文使用的是fabric1.4的版本,所以对于其他版本的fabric,内容可能会有所不同. 本文想针对Fabric中链码容器的启动过程进行源码的解析.这 ...
- Mybatis 系列6-结合源码解析节点配置:objectFactory、databaseIdProvider、plugins、mappers
[Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...
随机推荐
- .net core下,Ocelot网关与Spring Cloud Gateway网关的对比测试
有感于 myzony 发布的 针对 Ocelot 网关的性能测试 ,并且公司下一步也需要对.net和java的应用做一定的整合,于是对Ocelot网关.Spring Cloud Gateway网关做个 ...
- 关于iis里面 .net framework 版本的切换
然而,即便这样还是不够的 在iis上浏览网站会报以下错误: 这是什么原因呢?这是因为应用程序池中没有使用asp.net v4.0 网站程序是用v4.0写的,所以我们要用4.0的程序池版本,当前应用程序 ...
- CSRF 专题
一.CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造. 也被称为:one click attack/session riding(一 ...
- iOS-swift-如何实现崩溃树级别文件夹和扩大
如何实现崩溃树级别文件夹和扩大 1 介绍 最近,它一直在使用swift写项目,现在,他已经完成了不到一半,大概11可在一月中旬完成,什么时候会出一系列视频,源代码放出来.我是iOS 零基础学习 ...
- SequenceType 与 GeneratorType
Swift 语言中提供了一种 for .. in 语法的形式,用于遍历集合,比如对于 Array 类型,就可以用 for .. in 来进行遍历.这个语法在很多其他语言中也有提供,省去了我们定义下标的 ...
- WPF特效-鱼游动动画
原文:WPF特效-鱼游动动画 实现思路: 通过VisualBrush Binding方式获取鱼局部图像,在Viewport3D中创建ModelVisual3D块并把获取到的局部 ...
- RHEL 6和RHEL 7(CentOS 6和CentOS 7)恢复ROOT密码
RedHat 6恢复Root密码: 1.启动RedHat 6的时候在这个界面按任意键 2.出现如下界面,按 e 3.出现如下界面,选择第二个--kernel,然后再按 e 4.出现如下界面,输入 空格 ...
- WPF ListBox的内容属性Items
<Window x:Class="XamlTest.Window3" xmlns="http://schemas.microsoft.com/winf ...
- 在WPF中,如何得到任何Object对象的XAML代码?
原文:在WPF中,如何得到任何Object对象的XAML代码? 在WPF中,可以使用System.Windows.Markup.XamlWriter.Save(objName)得到任何Object对象 ...
- C# 桌面软件开发-深入学习[2]- AY-C#人爱学不学-aaronyang技术分享
原文:C# 桌面软件开发-深入学习[2]- AY-C#人爱学不学-aaronyang技术分享 1 : C# Assembly.GetEntryAssembly().GetName().Version. ...