菜鸟系列Fabric源码学习—创建通道
通道创建源码解析
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()方法。
- 如果通道id为"",则报错
- 如果不存在CmdFactory,则初始化CmdFactory
- 将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发送创建通道交易,然后获取创世块并保存
- 发送创建通道交易
- 获取区块
- 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
}
- 定义了个Envelope结构体
- 判断channelTxFile文件(启动网络之前创建的channel.tx)是否存在,一般都是存在的。
- 如果存在的话从该文件中读取配置信息,不存在的话从默认的模板创建,最后返回Envelope
- 对Envelope文件进行验证
创建用于广播信息的客户端,将创建的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源码学习—创建通道的更多相关文章
- 菜鸟系列Fabric源码学习 — 区块同步
Fabric 1.4 源码分析 区块同步 本文主要从源码层面介绍fabric peer同步区块过程,peer同步区块主要有2个过程: 1)peer组织的leader与orderer同步区块 2)pee ...
- 菜鸟系列Fabric源码学习 — peer节点启动
Fabric 1.4 源码分析peer节点启动 peer模块采用cobra库来实现cli命令. Cobra提供简单的接口来创建强大的现代化CLI接口,比如git与go工具.Cobra同时也是一个程序, ...
- 菜鸟系列Fabric源码学习 — committer记账节点
Fabric 1.4 源码分析 committer记账节点 本文档主要介绍committer记账节点如何初始化的以及committer记账节点的功能及其实现. 1. 简介 记账节点负责验证交易和提交账 ...
- 菜鸟系列Fabric源码学习 — MVCC验证
Fabric 1.4 源码分析 MVCC验证 读本节文档之前建议先查看[Fabric 1.4 源码分析 committer记账节点]章节. 1. MVCC简介 Multi-Version Concur ...
- 菜鸟系列Fabric源码学习—orderer服务启动
Fabric 1.4 orderer 服务启动流程 1.提要 orderer提供broadcast和deliver两个服务接口.orderer节点与各个peer节点通过grpc连接,orderer将所 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)
前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...
随机推荐
- 基于steam的游戏销量预测 — PART 3 — 基于BP神经网络的机器学习与预测
语言:c++ 环境:windows 训练内容:根据从steam中爬取的数据经过文本分析制作的向量以及标签 使用相关:无 解释: 就是一个BP神经网络,借鉴参考了一些博客的解释和代码,具体哪些忘了,给出 ...
- 017_linux驱动之_信号量
2. 信号量 信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码. 当获取不到信号量时,进程进入休眠等待状态. 定义信号量 struct semap ...
- leetcode解题报告(4):Search in Rotated Sorted ArrayII
描述 Follow up for "Search in Rotated Sorted Array": What if duplicates are allowed? Would t ...
- 《剑指offer》数组中只出现一次的数字
本题来自<剑指offer> 数组中只出现一次的数字 题目: 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 思路: 思路一:在<剑指of ...
- java并发编程--第一章并发编程的挑战
一.java并发编程的挑战 并发编程需要注意的问题: 并发编程的目的是让程序运行的更快,然而并不是启动更多的线程就能让程序最大限度的并发执行.若希望通过多线程并发让程序执行的更快,会受到如下问题的挑战 ...
- 网络文件共享服务—vsftpd服务
文件传输协议(FTP) 文件传输协议:File Transfer Protocol是用于在网络上进行文件传输的一套标准协议,使用客户/服务器模式.它属于网络传输协议的应用层. 服务器端:vsftpd ...
- Nginx内置变量及正则语法
对于很多Nginx初学者来说,配置文件是必须要看懂的.但是当公司的Nginx配置文件放在你面前的时候你总会被一些带着"$"符号和一大推看不懂的的正则给正懵逼.没错带着"$ ...
- vue实现购物清单列表添加删除
vue实现购物清单列表添加删除 一.总结 一句话总结: 基础的v-model操作,以及数组的添加(push)删除(splice)操作 1.checkbox可以绑定数组,也可以直接绑定值? 绑定数组就是 ...
- geth 新建账户
使用geth的account命令管理账户,例如创建新账户.更新账户密码.查询账户等: geth account <command> [options...] [arguments...] ...
- QThread 线程暂停 停止功能的实现
为了实现Qt中线程的暂停运行,和停止运行的控制功能 需要在设置两个static型控制变量. //终止原始数据单元进队出队,并清空数据. static bool stopSign; //原始数据单元队列 ...