通道创建源码解析

1. 与通道创建相关配置及操作命令

主要是configtx.yaml。通过应用通道的profile生成创建通道的配置文件。

    TwoOrgsChannel:
        Consortium: SampleConsortium
        <<: *ChannelDefaults
        Application:
            <<: *ApplicationDefaults
            Organizations:
                - *Org1
                - *Org2
            Capabilities:
                <<: *ApplicationCapabilities

接着在客户端执行下列命令创建通道。

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

peer channel create命令:

Create a channel and write the genesis block to a file.

Usage:
  peer channel create [flags]

Flags:
  -c, --channelID string     In case of a newChain command, the channel ID to create. It must be all lower case, less than 250 characters long and match the regular expression: [a-z][a-z0-9.-]*
  -f, --file string          Configuration transaction file generated by a tool such as configtxgen for submitting to orderer
  -h, --help                 help for create
      --outputBlock string   The path to write the genesis block for the channel. (default ./<channelID>.block)
  -t, --timeout duration     Channel creation timeout (default 10s)

Global Flags:
      --cafile string                       Path to file containing PEM-encoded trusted certificate(s) for the ordering endpoint
      --certfile string                     Path to file containing PEM-encoded X509 public key to use for mutual TLS communication with the orderer endpoint
      --clientauth                          Use mutual TLS when communicating with the orderer endpoint
      --connTimeout duration                Timeout for client to connect (default 3s)
      --keyfile string                      Path to file containing PEM-encoded private key to use for mutual TLS communication with the orderer endpoint
  -o, --orderer string                      Ordering service endpoint
      --ordererTLSHostnameOverride string   The hostname override to use when validating the TLS connection to the orderer.
      --tls                                 Use TLS when communicating with the orderer endpoint

2.创建通道命令执行流程

首先,在peer main.go文件的main()方法中,添加peer相关操作命令。

    mainCmd.AddCommand(version.Cmd())
    mainCmd.AddCommand(node.Cmd())
    mainCmd.AddCommand(chaincode.Cmd(nil))
    mainCmd.AddCommand(clilogging.Cmd(nil))
    mainCmd.AddCommand(channel.Cmd(nil))

而channel相关命令代码为:

// Cmd returns the cobra command for Node
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
}

其中,创建通道调用的接口为create(cmd, args, cf)

func createCmd(cf *ChannelCmdFactory) *cobra.Command {
    createCmd := &cobra.Command{
        Use:   "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)
        },
    }
    flagList := []string{
        "channelID",
        "file",
        "outputBlock",
        "timeout",
    }
    attachFlags(createCmd, flagList)

    return createCmd
}

上述为peer创建通道的命令构建流程。具体创建通道实现为create()方法。

  1. 如果通道id为"",则报错
  2. 如果不存在CmdFactory,则初始化CmdFactory
  3. 将CmdFactory传入,调用executeCreate()方法,执行创建通道逻辑
func create(cmd *cobra.Command, args []string, cf *ChannelCmdFactory) error {
    // the global chainID filled by the "-c" command
    if channelID == common.UndefinedParamValue {
        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 {
        cf, err = InitCmdFactory(EndorserNotRequired, PeerDeliverNotRequired, OrdererRequired)
        if err != nil {
            return err
        }
    }
    return executeCreate(cf)
}

现在看看InitCmdFactory()方法:

// InitCmdFactory init the ChannelCmdFactory with clients to endorser and orderer according to params
func InitCmdFactory(isEndorserRequired, isPeerDeliverRequired, isOrdererRequired bool) (*ChannelCmdFactory, error) {
    // 只从peer或者orderer其中一个获取区块
    if isPeerDeliverRequired && isOrdererRequired {
        // this is likely a bug during development caused by adding a new cmd
        return nil, errors.New("ERROR - only a single deliver source is currently supported")
    }

    var err error
    cf := &ChannelCmdFactory{}
    // 获取默认签名(默认peer)
    cf.Signer, err = common.GetDefaultSignerFnc()
    if err != nil {
        return nil, errors.WithMessage(err, "error getting default signer")
    }

    cf.BroadcastFactory = func() (common.BroadcastClient, error) {
        // 获取BroadcastClient
        return common.GetBroadcastClientFnc()
    }

    // for join and list, we need the endorser as well
    if isEndorserRequired {
        //创建背书客户端
        // creating an EndorserClient with these empty parameters will create a
        // connection using the values of "peer.address" and
        // "peer.tls.rootcert.file"
        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
    // NewDeliverClientForPeer creates a new DeliverClient from a PeerClient
    if isPeerDeliverRequired {
        cf.DeliverClient, err = common.NewDeliverClientForPeer(channelID)
        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)
        }
        cf.DeliverClient, err = common.NewDeliverClientForOrderer(channelID)
        if err != nil {
            return nil, err
        }
    }
    logger.Infof("Endorser and orderer connections initialized")
    return cf, nil
}

总结:创建通道,获取区块/deliver源只能是orderer,并且不需要创建背书客户端。要求从orderer创建deliver client。

3.通道创建具体实现

executeCreate()方法,其中创建通道也是一个交易,基于ChannelCmdFactory cf发送创建通道交易,然后获取创世块并保存

  1. 发送创建通道交易
  2. 获取区块
  3. proto序列化并保存
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
}

3.1 发送创建通道交易

  • 查看交易数据结构
// Envelope wraps a Payload with a signature so that the message may be authenticated
type Envelope struct {
    // A marshaled Payload
    // 序列化后的相关信息
    Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
    // A signature by the creator specified in the Payload header
    // 签名
    Signature            []byte   `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"`
    XXX_NoUnkeyedLiteral struct{} `json:"-"`
    XXX_unrecognized     []byte   `json:"-"`
    XXX_sizecache        int32    `json:"-"`
}

发送交易方法代码:

func sendCreateChainTransaction(cf *ChannelCmdFactory) error {
    var err error
    var chCrtEnv *cb.Envelope
    // 如果存在tx文件,则从tx文件生成创建通道交易
    if channelTxFile != "" {
        if chCrtEnv, err = createChannelFromConfigTx(channelTxFile); err != nil {
            return err
        }
    } else {
        // 不存在,则利用默认模版生成
        if chCrtEnv, err = createChannelFromDefaults(cf); err != nil {
            return err
        }
    }
    // 对创建通道交易进行验证
    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()
    err = broadcastClient.Send(chCrtEnv)

    return err
}
  1. 定义了个Envelope结构体
  2. 判断channelTxFile文件(启动网络之前创建的channel.tx)是否存在,一般都是存在的。
  3. 如果存在的话从该文件中读取配置信息,不存在的话从默认的模板创建,最后返回Envelope
  4. 对Envelope文件进行验证
  5. 创建用于广播信息的客户端,将创建的Envelope文件广播出去.

    3.2 orderer处理交易

本节会在交易流程及orderer源码详细介绍

4. 附录

channel.tx文件

{
    "payload":{
        "data":{
            "config_update":{
                "channel_id":"mychannel",
                "read_set":{
                    "groups":{
                        "Application":{
                            "groups":{
                                "Org1MSP":{
                                    "mod_policy":"",
                                    "version":"0"
                                },
                                "Org2MSP":{
                                    "mod_policy":"",
                                    "version":"0"
                                }
                            },
                            "mod_policy":"",
                            "version":"0"
                        }
                    },
                    "mod_policy":"",
                    "values":{
                        "Consortium":{
                            "mod_policy":"",
                            "version":"0"
                        }
                    },
                    "version":"0"
                },
                "write_set":{
                    "groups":{
                        "Application":{
                            "groups":{
                                "Org1MSP":{
                                    "mod_policy":"",
                                    "version":"0"
                                },
                                "Org2MSP":{
                                    "mod_policy":"",
                                    "version":"0"
                                }
                            },
                            "mod_policy":"Admins",
                            "policies":{
                                "Admins":{
                                    "mod_policy":"Admins",
                                    "policy":{
                                        "type":3,
                                        "value":{
                                            "rule":"MAJORITY",
                                            "sub_policy":"Admins"
                                        }
                                    },
                                    "version":"0"
                                },
                                "Readers":{
                                    "mod_policy":"Admins",
                                    "policy":{
                                        "type":3,
                                        "value":{
                                            "rule":"ANY",
                                            "sub_policy":"Readers"
                                        }
                                    },
                                    "version":"0"
                                },
                                "Writers":{
                                    "mod_policy":"Admins",
                                    "policy":{
                                        "type":3,
                                        "value":{
                                            "rule":"ANY",
                                            "sub_policy":"Writers"
                                        }
                                    },
                                    "version":"0"
                                }
                            },
                            "values":{
                                "Capabilities":{
                                    "mod_policy":"Admins",
                                    "value":{
                                        "capabilities":{
                                            "V1_2":{

                                            }
                                        }
                                    },
                                    "version":"0"
                                }
                            },
                            "version":"1"
                        }
                    },
                    "mod_policy":"",
                    "values":{
                        "Consortium":{
                            "mod_policy":"",
                            "value":{
                                "name":"SampleConsortium"
                            },
                            "version":"0"
                        }
                    },
                    "version":"0"
                }
            }
        },
        "header":{
            "channel_header":{
                "channel_id":"mychannel",
                "epoch":"0",
                "timestamp":"2019-11-26T02:46:37.000Z",
                "tx_id":"",
                "type":2,
                "version":0
            }
        }
    }
}

菜鸟系列Fabric源码学习—创建通道的更多相关文章

  1. 菜鸟系列Fabric源码学习 — 区块同步

    Fabric 1.4 源码分析 区块同步 本文主要从源码层面介绍fabric peer同步区块过程,peer同步区块主要有2个过程: 1)peer组织的leader与orderer同步区块 2)pee ...

  2. 菜鸟系列Fabric源码学习 — peer节点启动

    Fabric 1.4 源码分析peer节点启动 peer模块采用cobra库来实现cli命令. Cobra提供简单的接口来创建强大的现代化CLI接口,比如git与go工具.Cobra同时也是一个程序, ...

  3. 菜鸟系列Fabric源码学习 — committer记账节点

    Fabric 1.4 源码分析 committer记账节点 本文档主要介绍committer记账节点如何初始化的以及committer记账节点的功能及其实现. 1. 简介 记账节点负责验证交易和提交账 ...

  4. 菜鸟系列Fabric源码学习 — MVCC验证

    Fabric 1.4 源码分析 MVCC验证 读本节文档之前建议先查看[Fabric 1.4 源码分析 committer记账节点]章节. 1. MVCC简介 Multi-Version Concur ...

  5. 菜鸟系列Fabric源码学习—orderer服务启动

    Fabric 1.4 orderer 服务启动流程 1.提要 orderer提供broadcast和deliver两个服务接口.orderer节点与各个peer节点通过grpc连接,orderer将所 ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  7. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  9. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

随机推荐

  1. 3、python--第三天练习题

    #1.简述普通参数.指定参数.默认参数.动态参数的区别 #1.普通参数就是传入的函数,没有默认值 def f(a): a = a + 1 return a print(f(3)) #2.指定参数 de ...

  2. the schema version of 'microsoft.aspnet.mvc' is incompatible with version of nuget

    Nuget versioning issue with package restore http://stackoverflow.com/questions/12035976/nuget-versio ...

  3. 第2组 Alpha冲刺(3/4)

    17-材料-黄智(252342126) 22:10:46 队名:十一个憨批 组长博客 作业博客 组长黄智 过去两天完成的任务:写博客,复习C语言 GitHub签入记录 接下来的计划:构思游戏实现 还剩 ...

  4. Asp.Net跨平台 Jexus 5.8.1 独立版

    在Linux上运行ASP.NET网站或WebApi的传统步骤是,先安装libgdiplus,再安装mono,然后安装Jexus.在这个过程中,虽然安装Jexus是挺简便的一件事,但是安装mono就相对 ...

  5. Python2.7编码问题:UnicodeDecodeError: 'ascii' codec can't decode byte 0xe8 in position...解决方法

    解决方法: 在Python程序最前面加上以下语句: import sys reload(sys) sys.setdefaultencoding('utf-8')

  6. mapreduce 倒序 排序 最简单 易上手

    对于mapreduce倒序只需要建立一个类,然后继承WritableComparator 在重写 Compare函数最后在main里调用一下,就可以实现倒序排序: 代码: public static ...

  7. Echarts案例-折线图

    一:先在官网下载 https://www.echartsjs.com/zh/download.html 然后再建立工程,导入这两个包: 写代码: <!DOCTYPE html> <h ...

  8. tomcat发布web项目

    转:https://www.cnblogs.com/skyblue-li/p/7888951.html Tomcat是一种Web服务器,我们自己做好了一个Web项目,就可以通过Tomcat来发布.服务 ...

  9. install slax record

    #!/usr/bin/bash#apt update#apt upgradeapt install build-essential cmake gdb git vim binutils-doc cpp ...

  10. 5.4.1 sequenceFile读写文件、记录边界、同步点、压缩排序、格式

    5.4.1      sequenceFile读写文件.记录边界.同步点.压缩排序.格式 HDFS和MapReduce是针对大文件优化的存储文本记录,不适合二进制类型的数据.SequenceFile作 ...