zoukankan      html  css  js  c++  java
  • 菜鸟系列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()方法。

    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
                }
            }
        }
    }
    
    如果你觉得写的不错,请移步www.itkezhan.top或者关注公众号IT程序员客栈
  • 相关阅读:
    java 数组声明方法
    python 什么叫迭代
    Golang生成区间随机整数
    Golang字符串格式化
    Golang中map的三种声明方式和简单实现增删改查
    Golang实现二分查找法
    Golang实现冒泡排序法
    Golang切片的三种简单使用方式及区别
    Golang获取int数组里的最大值和下标
    Golang数组注意细节
  • 原文地址:https://www.cnblogs.com/i-dandan/p/11941666.html
Copyright © 2011-2022 走看看