Fabric1.4 Source Analysis: The client process to create a channel

When using Fabric to create a channel, usually we execute a command completion, this article will look at the implementation process to resolve after this command Fabric source code execution.

peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/channel.tx --tls true --cafile $ORDERER_CA

The entire process entry point in the fabric/peer/main.gofile main()methods (as used herein, is Fabric1.4 versions, different versions of the content may be different) . This method is also defined in command Peer node may perform, regarding versions: version.Cmd(), a node status: node.Cmd()regarding chain code: chaincode.Cmd(nil), the client logs on: clilogging.Cmd(nil)the last channel is about: channel.Cmd(nil). So we start from here, look at the whole process is to create a channel of what.
After the point, to a peer/channel/channel.gofile on line 49, which defines the commands for operating channel Peer node can perform:

func Cmd(cf *ChannelCmdFactory) *cobra.Command {
    AddFlags(channelCmd)
    
    #创建通道
    channelCmd.AddCommand(createCmd(cf))  
    #从通道获取区块
    channelCmd.AddCommand(fetchCmd(cf))
    #加入通道
    channelCmd.AddCommand(joinCmd(cf))
    #列出当前节点所加入的通道
    channelCmd.AddCommand(listCmd(cf))
    #签名并更新通道配置信息
    channelCmd.AddCommand(updateCmd(cf))
    #只对通道配置信息进行签名
    channelCmd.AddCommand(signconfigtxCmd(cf))
    #获取通道信息
    channelCmd.AddCommand(getinfoCmd(cf))

    return channelCmd
}

Peer node specific command usage can refer to the Fabric official document , not an explanation here.
We look at the createCmd(cf)method, the method proceeds to the peer/channel/create.gofile, line 51, to see the file name to know and create a relevant channel.

func createCmd(cf *ChannelCmdFactory) *cobra.Command {
    createCmd := &cobra.Command{
        Use:   "create",   #使用create关键词创建通道
        Short: "Create a channel",  
        Long:  "Create a channel and write the genesis block to a file.",   #创建通道并将创世区块写入文件
        RunE: func(cmd *cobra.Command, args []string) error {
            #这一行命令就是对通道进行创建了,点进行看一下
            return create(cmd, args, cf)
        },
    }
...
}

create(cmd, args, cf)Method 227 lines in this document:

func create(cmd *cobra.Command, args []string, cf *ChannelCmdFactory) error {
    // the global chainID filled by the "-c" command
    #官方注释用-c来表明通道ID
    if channelID == common.UndefinedParamValue {
        #UndefinedParamValue="",如果通道ID等于空
        return errors.New("must supply channel ID")
    }

    // Parsing of the command line is done so silence cmd usage
    cmd.SilenceUsage = true

    var err error
    if cf == nil {
        #如果ChannelCmdFactory为空,则初始化一个
        cf, err = InitCmdFactory(EndorserNotRequired, PeerDeliverNotRequired, OrdererRequired)
        if err != nil {
            return err
        }
    }
    #最后将ChannelCmdFactory传入该方法,进行通道的创建
    return executeCreate(cf)
}

First look at InitCmdFactory () what had been done in the peer/channel/channel.gofile, line 126:

func InitCmdFactory(isEndorserRequired, isPeerDeliverRequired, isOrdererRequired bool) (*ChannelCmdFactory, error) {
    #这里的意思就是只能有一个交付源,要么是Peer要么是Orderer
    if isPeerDeliverRequired && isOrdererRequired {
        return nil, errors.New("ERROR - only a single deliver source is currently supported")
    }

    var err error
    #初始化ChannelCmdFactory,看一下该结构体的内容
    cf := &ChannelCmdFactory{}

Well, take it directly here:

 type ChannelCmdFactory struct {
    #用于背书的客户端
    EndorserClient   pb.EndorserClient
    #签名者
    Signer           msp.SigningIdentity
    #用于广播的客户端
    BroadcastClient  common.BroadcastClient
    #用于交付的客户端
    DeliverClient    deliverClientIntf
    #创建用于广播的客户端的工厂
    BroadcastFactory BroadcastClientFactory
}

And then look down:

    #获取默认的签名者,通常是Peer节点
    cf.Signer, err = common.GetDefaultSignerFnc()
    if err != nil {
        return nil, errors.WithMessage(err, "error getting default signer")
    }

    cf.BroadcastFactory = func() (common.BroadcastClient, error) {
        #根据ChannelCmdFactory结构体中的BroadcastFactory获取BroadcastClient
        return common.GetBroadcastClientFnc()
    }

    // for join and list, we need the endorser as well
    #我们这里是完成对通道的创建,所以只使用了最后一个isOrdererRequired
    if isEndorserRequired {
        #创建一个用于背书的客户端
        cf.EndorserClient, err = common.GetEndorserClientFnc(common.UndefinedParamValue, common.UndefinedParamValue)
        if err != nil {
            return nil, errors.WithMessage(err, "error getting endorser client for channel")
        }
    }

    // for fetching blocks from a peer
    if isPeerDeliverRequired {
        #从Peer节点创建一个用于交付的客户端
        cf.DeliverClient, err = common.NewDeliverClientForPeer(channelID, bestEffort)
        if err != nil {
            return nil, errors.WithMessage(err, "error getting deliver client for channel")
        }
    }

    // for create and fetch, we need the orderer as well
    if isOrdererRequired {
        if len(strings.Split(common.OrderingEndpoint, ":")) != 2 {
            return nil, errors.Errorf("ordering service endpoint %s is not valid or missing", common.OrderingEndpoint)
        }
        #从Order节点创建一个一个用于交付的客户端
        cf.DeliverClient, err = common.NewDeliverClientForOrderer(channelID, bestEffort)
        if err != nil {
            return nil, err
        }
    }
    logger.Infof("Endorser and orderer connections initialized")
    return cf, nil
}

Return create()method:

#到了最后一行代码,传入之前创建的ChannelCmdFactory,开始进行通道的创建
return executeCreate(cf)

The method in peer/channel/create.goline 174 file:

#方法比较清晰,一共完成了以下几个步骤
func executeCreate(cf *ChannelCmdFactory) error {
    #发送创建通道的Transaction到Order节点
    err := sendCreateChainTransaction(cf)
    if err != nil {
        return err
    }
    #获取该通道内的创世区块(该过程在Order节点共识完成之后)
    block, err := getGenesisBlock(cf)
    if err != nil {
        return err
    }
    #序列化区块信息
    b, err := proto.Marshal(block)
    if err != nil {
        return err
    }
    file := channelID + ".block"
    if outputBlock != common.UndefinedParamValue {
        file = outputBlock
    }
    #将区块信息写入本地文件中
    err = ioutil.WriteFile(file, b, 0644)
    if err != nil {
        return err
    }
    return nil
}

1.Peer node creates Envelope file used to create the channel

First we look at sendCreateChainTransaction()the way back to the peer/channel/create.gofile on line 144:

func sendCreateChainTransaction(cf *ChannelCmdFactory) error {
    var err error
    #定义了一个Envelope结构体
    var chCrtEnv *cb.Envelope

EnvelopeStructure:

type Envelope struct {
    #主要就是保存被序列化的有效载荷
    Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
    #由创建者进行的签名信息
    Signature            []byte   `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"`
    XXX_NoUnkeyedLiteral struct{} `json:"-"`
    XXX_unrecognized     []byte   `json:"-"`
    XXX_sizecache        int32    `json:"-"`
}

Back to sendCreateChainTransaction()this method, continue down:

    if channelTxFile != "" {
        #如果指定了channelTxFile,则使用指定的文件创建通道,这个方法很简单,从文件中读取数据,反序列化后返回chCrtEnv.对于我们启动Fabric网络之前曾创建过一个channel.tx文件,指的就是这个
        if chCrtEnv, err = createChannelFromConfigTx(channelTxFile); err != nil {
            return err
        }
    } else {
        #如果没有指定,则使用默认的配置创建通道,看一下这个方法,在71行
        if chCrtEnv, err = createChannelFromDefaults(cf); err != nil {
            return err
        }
    }
-------------------------------createChannelFromDefaults()-------
func createChannelFromDefaults(cf *ChannelCmdFactory) (*cb.Envelope, error) {
    #主要就这一个方法,点进去
    chCrtEnv, err := encoder.MakeChannelCreationTransaction(channelID, localsigner.NewSigner(), genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile))
    if err != nil {
        return nil, err
    }
    return chCrtEnv, nil
}

MakeChannelCreationTransaction()ID method was introduced to the channel, and create a signer, and the default configuration file, the method in common/tools/configtxgen/encoder/encoder.gothe file Line 502:

func MakeChannelCreationTransaction(channelID string, signer crypto.LocalSigner, conf *genesisconfig.Profile) (*cb.Envelope, error) {
    #从名字可以看到是使用了默认的配置模板,对各种策略进行配置,里面就不再细看了
    template, err := DefaultConfigTemplate(conf)
    if err != nil {
        return nil, errors.WithMessage(err, "could not generate default config template")
    }
    #看一下这个方法,从模板中创建一个用于创建通道的Transaction
    return MakeChannelCreationTransactionFromTemplate(channelID, signer, conf, template)
}

MakeChannelCreationTransactionFromTemplate()Method Line 530:

func MakeChannelCreationTransactionFromTemplate(channelID string, signer crypto.LocalSigner, conf *genesisconfig.Profile, template *cb.ConfigGroup) (*cb.Envelope, error) {
    newChannelConfigUpdate, err := NewChannelCreateConfigUpdate(channelID, conf, template)
    ...
    #创建一个用于配置更新的结构体
    newConfigUpdateEnv := &cb.ConfigUpdateEnvelope{
        ConfigUpdate: utils.MarshalOrPanic(newChannelConfigUpdate),
    }

    if signer != nil {
        #如果签名者不为空,创建签名Header
        sigHeader, err := signer.NewSignatureHeader()
        ...
        newConfigUpdateEnv.Signatures = []*cb.ConfigSignature{{
            SignatureHeader: utils.MarshalOrPanic(sigHeader),
        }}
        ...
        #进行签名
        newConfigUpdateEnv.Signatures[0].Signature, err = signer.Sign(util.ConcatenateBytes(newConfigUpdateEnv.Signatures[0].SignatureHeader, newConfigUpdateEnv.ConfigUpdate))
        ...
    }
    #创建被签名的Envelope,然后一直返回到最外面的方法
    return utils.CreateSignedEnvelope(cb.HeaderType_CONFIG_UPDATE, channelID, signer, newConfigUpdateEnv, msgVersion, epoch)
}

Here, Envelope used to create the channel has been created, sendCreateChainTransaction()read on:

...
    #该方法主要是对刚刚创建的Envelope进行验证
    if chCrtEnv, err = sanityCheckAndSignConfigTx(chCrtEnv); err != nil {
        return err
    }
    var broadcastClient common.BroadcastClient
    #验证完成后,创建一个用于广播信息的客户端
    broadcastClient, err = cf.BroadcastFactory()
    if err != nil {
        return errors.WithMessage(err, "error getting broadcast client")
    }

    defer broadcastClient.Close()
    #将创建通道的Envelope信息广播出去
    err = broadcastClient.Send(chCrtEnv)
    return err
}

Here, sendCreateChainTransaction()the method is over, to sum up the work done by this method:

  1. It defines a structure Envelope
  2. Judgment channelTxFile file (channel.tx created before starting the network) is present, it is generally present.
  3. If there is, then read configuration information from the file does not exist, then create from the default template, and finally return Envelope
  4. Envelope on file to verify
  5. Create a client for broadcasting information, Envelope file will be created out of the broadcast.

    2 Order Envelope document processing node

    As for obtaining creation block and save the file to a local not explain, let's look at the Peer node creates a channel broadcasted after Envelope, Order node does.
    In the method of /order/common/server/server.gothe line 141:
func (s *server) Broadcast(srv ab.AtomicBroadcast_BroadcastServer) error {
    ...
    #主要在这一行代码,Handle方法对接收到的信息进行处理
    return s.bh.Handle(&broadcastMsgTracer{
        AtomicBroadcast_BroadcastServer: srv,
        msgTracer: msgTracer{
            debug:    s.debug,
            function: "Broadcast",
        },
    })
}

For the Handler()method, in /order/common/broadcast/broadcast.goline 66 file:

func (bh *Handler) Handle(srv ab.AtomicBroadcast_BroadcastServer) error {
    #首先获取消息的源地址
    addr := util.ExtractRemoteAddress(srv.Context())
    ...
    for {
        #接收消息
        msg, err := srv.Recv()
        ...
        #处理接收到的消息,我们看一下这个方法
        resp := bh.ProcessMessage(msg, addr)
        #最后将响应信息广播出去
        err = srv.Send(resp)
        ...
    }
}

ProcessMessage(msg, addr)Parameters passed to the method to the source address of the received message and a message, the method is more important is the message Order master node processing method. Line 136:

func (bh *Handler) ProcessMessage(msg *cb.Envelope, addr string) (resp *ab.BroadcastResponse) {
    #这个结构体应该理解为记录器,记录消息的相关信息
    tracker := &MetricsTracker{
        ChannelID: "unknown",
        TxType:    "unknown",
        Metrics:   bh.Metrics,
    }
    defer func() {
        // This looks a little unnecessary, but if done directly as
        // a defer, resp gets the (always nil) current state of resp
        // and not the return value
        tracker.Record(resp)
    }()
    #记录处理消息的开始时间
    tracker.BeginValidate()
    #该方法获取接收到的消息的Header,并判断是否为配置信息
    chdr, isConfig, processor, err := bh.SupportRegistrar.BroadcastChannelSupport(msg)
    ...
    #由于之前Peer节点发送的为创建通道的信息,所以消息类型为配置信息
    if !isConfig {
        ...
        #而对于普通的交易信息的处理方法这里就不再看了,主要关注于创建通道的消息的处理
    } else { // isConfig
        logger.Debugf("[channel: %s] Broadcast is processing config update message from %s", chdr.ChannelId, addr)
        #到了这里,对配置更新消息进行处理,主要方法,点进行看一下
        config, configSeq, err := processor.ProcessConfigUpdateMsg(msg)

ProcessConfigUpdateMsg(msg)Methods in orderer/common/msgprocessor/systemchannel.goline 84 file:

#这个地方有些不懂,为什么会调用systemchannel.ProcessConfigUpdateMsg()而不是standardchannel.ProcessConfigUpdateMsg()方法?是因为这个结构体的原因?
===========================SystemChannel=======================
type SystemChannel struct {
    *StandardChannel
    templator ChannelConfigTemplator
}
===========================SystemChannel=======================
func (s *SystemChannel) ProcessConfigUpdateMsg(envConfigUpdate *cb.Envelope) (config *cb.Envelope, configSeq uint64, err error) {
    #首先从消息体中获取通道ID
    channelID, err := utils.ChannelID(envConfigUpdate)
    ...
    #判断获取到的通道ID是否为已经存在的用户通道ID,如果是的话转到StandardChannel中的ProcessConfigUpdateMsg()方法进行处理
    if channelID == s.support.ChainID() {
        return s.StandardChannel.ProcessConfigUpdateMsg(envConfigUpdate)
    }
    ...
    #由于之前由Peer节点发送的为创建通道的Tx,所以对于通道ID是不存在的,因此到了这个方法,点进行看一下
    bundle, err := s.templator.NewChannelConfig(envConfigUpdate)

NewChannelConfig()The method of the line 215, the most important methods to complete the configuration of the channel:

func (dt *DefaultTemplator) NewChannelConfig(envConfigUpdate *cb.Envelope) (channelconfig.Resources, error) {
    #首先反序列化有效载荷
    configUpdatePayload, err := utils.UnmarshalPayload(envConfigUpdate.Payload)
    ...
    #反序列化配置更新信息Envelope
    configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(configUpdatePayload.Data)s
    ...
    #获取通道头信息
    channelHeader, err := utils.UnmarshalChannelHeader(configUpdatePayload.Header.ChannelHeader)
    ...
    #反序列化配置更新信息
    configUpdate, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)
    ...
    #以下具体的不再说了,就是根据之前定义的各项策略对通道进行配置,具体的策略可以看configtx.yaml文件
    consortiumConfigValue, ok := configUpdate.WriteSet.Values[channelconfig.ConsortiumKey]
    ...
    consortium := &cb.Consortium{}
    err = proto.Unmarshal(consortiumConfigValue.Value, consortium)
    ...
    applicationGroup := cb.NewConfigGroup()
    consortiumsConfig, ok := dt.support.ConsortiumsConfig()
    ...
    consortiumConf, ok := consortiumsConfig.Consortiums()[consortium.Name]
    ...
    applicationGroup.Policies[channelconfig.ChannelCreationPolicyKey] = &cb.ConfigPolicy{
        Policy: consortiumConf.ChannelCreationPolicy(),
    }
    applicationGroup.ModPolicy = channelconfig.ChannelCreationPolicyKey
    #获取当前系统通道配置信息
    systemChannelGroup := dt.support.ConfigtxValidator().ConfigProto().ChannelGroup
    if len(systemChannelGroup.Groups[channelconfig.ConsortiumsGroupKey].Groups[consortium.Name].Groups) > 0 &&
        len(configUpdate.WriteSet.Groups[channelconfig.ApplicationGroupKey].Groups) == 0 {
        return nil, fmt.Errorf("Proposed configuration has no application group members, but consortium contains members")
    }
    if len(systemChannelGroup.Groups[channelconfig.ConsortiumsGroupKey].Groups[consortium.Name].Groups) > 0 {
        for orgName := range configUpdate.WriteSet.Groups[channelconfig.ApplicationGroupKey].Groups {
            consortiumGroup, ok := systemChannelGroup.Groups[channelconfig.ConsortiumsGroupKey].Groups[consortium.Name].Groups[orgName]
            if !ok {
                return nil, fmt.Errorf("Attempted to include a member which is not in the consortium")
            }
            applicationGroup.Groups[orgName] = proto.Clone(consortiumGroup).(*cb.ConfigGroup)
        }
    }
    channelGroup := cb.NewConfigGroup()
    #将系统通道配置信息复制
    for key, value := range systemChannelGroup.Values {
        channelGroup.Values[key] = proto.Clone(value).(*cb.ConfigValue)
        if key == channelconfig.ConsortiumKey {
            // Do not set the consortium name, we do this later
            continue
        }
    }

    for key, policy := range systemChannelGroup.Policies {
        channelGroup.Policies[key] = proto.Clone(policy).(*cb.ConfigPolicy)
    }
    #新的配置信息中Order组配置使用系统通道的配置,同时将定义的application组配置赋值到新的配置信息
    channelGroup.Groups[channelconfig.OrdererGroupKey] = proto.Clone(systemChannelGroup.Groups[channelconfig.OrdererGroupKey]).(*cb.ConfigGroup)
    channelGroup.Groups[channelconfig.ApplicationGroupKey] = applicationGroup
    channelGroup.Values[channelconfig.ConsortiumKey] = &cb.ConfigValue{
        Value:     utils.MarshalOrPanic(channelconfig.ConsortiumValue(consortium.Name).Value()),
        ModPolicy: channelconfig.AdminsPolicyKey,
    }
    if oc, ok := dt.support.OrdererConfig(); ok && oc.Capabilities().PredictableChannelTemplate() {
        channelGroup.ModPolicy = systemChannelGroup.ModPolicy
        zeroVersions(channelGroup)
    }
    #将创建的新的配置打包为Bundle
    bundle, err := channelconfig.NewBundle(channelHeader.ChannelId, &cb.Config{
        ChannelGroup: channelGroup,
    })
    ...
    return bundle, nil
}

Then we go back ProcessConfigUpdateMsg()methods:

    ...
    #创建一个配置验证器对该方法的传入参数进行验证操作
    newChannelConfigEnv, err := bundle.ConfigtxValidator().ProposeConfigUpdate(envConfigUpdate)
    ...
    #创建一个签名的Envelope,此次为Header类型为HeaderType_CONFIG进行签名
    newChannelEnvConfig, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, s.support.Signer(), newChannelConfigEnv, msgVersion, epoch)
    #创建一个签名的Transaction,此次为Header类型为HeaderType_ORDERER_TRANSACTION进行签名
    wrappedOrdererTransaction, err := utils.CreateSignedEnvelope(cb.HeaderType_ORDERER_TRANSACTION, s.support.ChainID(), s.support.Signer(), newChannelEnvConfig, msgVersion, epoch)
    ...
    #过滤器进行过滤,主要检查是否创建的Transaction过大,以及签名检查,确保Order节点使用正确的证书进行签名
    err = s.StandardChannel.filters.Apply(wrappedOrdererTransaction)
    ...
    #将Transaction返回 
    return wrappedOrdererTransaction, s.support.Sequence(), nil
}

Here, the message is processed, it is returned to ProcessMessage()the method:

    config, configSeq, err := processor.ProcessConfigUpdateMsg(msg)
    ...
    #记录消息处理完毕时间
    tracker.EndValidate()
    #开始进行入队操作
    tracker.BeginEnqueue()
    #waitReady()是一个阻塞方法,等待入队完成或出现异常
    if err = processor.WaitReady(); err != nil {
    logger.Warningf("[channel: %s] Rejecting broadcast of message from %s with SERVICE_UNAVAILABLE: rejected by Consenter: %s", chdr.ChannelId, addr, err)
    return &ab.BroadcastResponse{Status: cb.Status_SERVICE_UNAVAILABLE, Info: err.Error()}
    }
    #共识方法,具体看定义的Fabric网络使用了哪种共识
    err = processor.Configure(config, configSeq)
    ...
    #最后返回操作成功的响应
    return &ab.BroadcastResponse{Status: cb.Status_SUCCESS}
}

Here, to create a channel sent by the client Transactionis over. A total divided into two parts, one is the Peernode to create a channel of the Envelopeprocess of creation, one Ordernode receives the Envelopeprocess configuration, sum up the overall process:
PeerNode side:

  1. Create a channel for creatingTransaction
    1. Determine whether there is channel.txa file, if any, have been configured to read information directly from the file, there is usually
    2. If not configured according to the default template
    3. For just created for configuration updates Envelopeand relevant examination include whether the data is empty, whether the channel is created already exists, the configuration information is correct, and is signed for the package HeaderTypeto CONFIG_UPDATEthe Envelope.
    4. Created Envelopebroadcasted.
  2. (Creation block generated after this step successfully consensus Order node )
  3. The creation block write local files

OrderNode side:

  1. Receiving the Envelopeinformation related to verify the configuration information and determines whether the
  2. Id determine whether the channel already exists, if it exists as a configuration update information, or to create information for the channel
  3. From Envelopereading the policy configuration
  4. For policy configuration information for verification
  5. On the Headertype CONFIGof sign Envelope
  6. On the Headertype ORDERER_TRANSACTIONof Envelopesign generationTransaction
  7. To the generated Transactionfilter, mainly Tx size, Ordercertificate information is correct node
  8. Enqueue operation, and then wait for the team to complete consensus
  9. Broadcast successful response

The entire process to create a channel is relatively long, limited capacity, so in some places and not analyze too clear. But we can still grasp the whole.
Finally, attach the reference documentation: Portal
and Fabric Source Address: Portal

Guess you like

Origin www.cnblogs.com/cbkj-xd/p/11113195.html
Recommended