And start a new reading, this time to see that the process Peer node joins the channel. In fact, each time to see the source code, there will be a lot of places do not understand, but I believe as long as the stick, record-keeping, there are still a lot of harvest.
Peer node added to the channel process, from the user point of view also simply execute the command line:
peer channel join -b mychannel.block
To complete the process of a node is added channels. Fabric from the internal network is concerned, but it is a lot of work to do, let's look at a specific process:
the whole process starting point and client to create a channel processes the same fabric/peer/main.go
file main()
method, by executing the above command to call peer/channel/channel.go
in Cmd()
method, then the peer/channel/join.go
file joinCmd()
method, line 131 join()
, and finally went to the line 88 executeJoin()
method, then look at the method:
spec, err := getJoinCCSpec()
if err != nil {
return err
}
The first is get specific information channels need to be added, in line 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()
The method continues to look down:
invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
The structure of a package prior to re-create the structure 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:"-"`
}
And then obtain the identity of a creator, after the proposal for the creation and signature:
creator, err := cf.Signer.Serialize()
if err != nil {
return fmt.Errorf("Error serializing identity for %s: %s", cf.Signer.GetIdentifier(), err)
}
The next step is the creation of the 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)
}
It depends on the specific CreateProposalFromCIS()
method in core/protos/proputils.go
the first 466 lines of the file, and then call the 237 line CreateChaincodeProposal()
method, look at the name should be understood that a information about creating a chain code proposal. Then the 243 line CreateChaincodeProposalWithTransient()
method:
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()
Method on line 282:
first look at the method of the incoming values: txid
generated by the previous methods, typ
the first method of passing in, value HeaderType_CONFIG
, chainID empty string, cis
are passed to come original method, the value of ChaincodeInvocationSpec
the structure , nonce
generated by the previous method, creator
but also original method passed in, transientMap
it is empty, before the CreateChaincodeProposal()
method can be seen. Then we look at the specific flow method:
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
}
Let's go back executeJoin()
methods, read on:
#刚刚这行代码返回了创建的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 node is added to this channel operation has ended, we summarize the work done before:
- The first is the
peer channel join -b mychannel.block
trigger of this order, and finally after several calls to theexecuteJoin()
method. - First, obtain
mychannel.block
information in the file, close toChaincodeSpec
the structure. - It is then encapsulated
ChaincodeInvocationSpec
structure. - Gets a proposal for initiating a proposal and signing operations
creator
. - The TxID and the nonce, and further packaged as
ChaincodeHeaderExtension
,ChaincodeProposalPayload
,Header
,Proposal
structure. - Of the resulting
Proposal
structure is a signing operation performed by a Peer node processing, the processing is completed after returning a response message.
Message processing method for Peer node ProcessProposal
in this article: Fabric1.4 source code parsing: Peer node endorsement proposal process
Here is a class diagram Well, there are too many structures before, the relationship a bit more complicated:
The Source: https://github.com/yeasy/hyperledger_code_fabric/blob/master/peer/_images/signed_proposal.png
Finally, reference documentation