hyperledger fabric 源码解析-peer(1) cli的套路

论坛 期权论坛 区块链     
飞起一砣子   2018-11-20 23:31   3832   0
            0x00在fabric中,peer是一个重要的二进制程序,其功能主要是提供
  1. peer
复制代码
相关的操作,关于peer的概念,可以参考官方文档1官方文档2
  1. peer
复制代码
这个cli工具,作为一个客户端,可以向区块链网络(
  1. channel
复制代码
)发起
  1. peer
复制代码
相关从操作,这个命令包含很多的子命令,本文不会逐一介绍,这也不是本文的目的,本文主要是通过对
  1. peer
复制代码
源码的分析,介绍一下
  1. fabric
复制代码
这个项目中,
  1. cli
复制代码
工具与服务端通信的”套路“
0x01 准备工作
  • 一些
    1. golang
    复制代码
    的基本知识
    1. rpc
    复制代码
    通信的基本原理或者概念有所了解(了解
    1. grpc
    复制代码
    更好)
    1. git
    复制代码
    有基本的认识
[h1]下载代码[/h1]
  1. fabric
复制代码
的代码目前在
  1. github
复制代码
上有镜像,通过:
  1. git clone https://github.com/hyperledger/fabric.git
复制代码
就可以将代码下到本地
代码结构如下:
  1. $ tree -L 1.├── bccsp├── build├── CHANGELOG.md├── ci.properties├── cmd├── CODE_OF_CONDUCT.md├── common                #公共工具源码,例如configtxgen,cryptogen等├── CONTRIBUTING.md├── core                  # 主要代码├── devenv├── discovery├── docker-env.mk├── docs├── events├── examples├── Gopkg.lock├── Gopkg.toml├── gossip├── gotools├── gotools.mk├── idemix            # ibm idemix密码├── images├── integration├── LICENSE├── Makefile├── msp├── orderer             #orderer源码├── peer                # peer命令源码├── protos              # rpc源码├── README.md├── release├── release_notes├── sampleconfig├── scripts├── settings.gradle├── si├── tox.ini├── unit-test└── vendor26 directories, 13 files
复制代码
[h1]peer命令概览[/h1]peer命令参数如下:
  1. $ peer2018-06-18 09:39:18.382 UTC [msp] getMspConfig -> INFO 001 Loading NodeOUsUsage:  peer [flags]  peer [command]Available Commands:  chaincode   Operate a chaincode: install|instantiate|invoke|package|query|signpackage|upgrade|list.  channel     Operate a channel: create|fetch|join|list|update|signconfigtx|getinfo.  logging     Log levels: getlevel|setlevel|revertlevels.  node        Operate a peer node: start|status.  version     Print fabric peer version.Flags:  -h, --help                   help for peer      --logging-level string   Default logging level and overrides, see core.yaml for full syntax  -v, --version                Display current version of fabric peer serverUse "peer [command] --help" for more information about a command.2018-06-18 09:39:18.415 UTC [main] main -> INFO 002 Exiting.....
复制代码
想要使用
  1. peer
复制代码
命令,可以直接通过 first network
  1. ./byfn.sh -m up
复制代码
命令启动一个示例网络,然后通过
  1. docker exec -it peer0.org1.example.com bash
复制代码
进入peer0的容器,然后执行
  1. peer help
复制代码
就能看到上面的打印了
可以看到,
  1. peer
复制代码
一共有如下几个子命令:
  • chaincode
  • channel
  • node
  • version
而当我们浏览
  1. peer
复制代码
目录下的代码结构是,发现恰好存在这几个目录:
  1. $ tree -L 1          .├── chaincode├── channel├── clilogging├── common├── gossip├── main.go├── main_test.go├── mocks├── node├── testdata└── version9 directories, 2 files
复制代码
很明显,每一个子命令对应了要给目录,而总的入口,则是
  1. main.go
复制代码
[h2]main.go[/h2]现在,我们来看 main.go
首先,来个call graph:


output.png
可以看到,整个main.go中,主要是对vipercobra的一些API的调用,其中,下面的代码通过AddCommand将各个子目录的代码,以子命令形式添加了进来:
  1. // 首先import 各个子目录的包import (        // other imports    "github.com/hyperledger/fabric/peer/chaincode"    "github.com/hyperledger/fabric/peer/channel"    "github.com/hyperledger/fabric/peer/clilogging"    "github.com/hyperledger/fabric/peer/common"    "github.com/hyperledger/fabric/peer/node"    "github.com/hyperledger/fabric/peer/version")// 一些准备工作func main() {        // 环境变量初始化    mainCmd.AddCommand(version.Cmd())    mainCmd.AddCommand(node.Cmd())    mainCmd.AddCommand(chaincode.Cmd(nil))    mainCmd.AddCommand(clilogging.Cmd(nil))    mainCmd.AddCommand(channel.Cmd(nil))        // 其他的参数和配置解析        // 真正的命令执行    if mainCmd.Execute() != nil {        os.Exit(1)    }}
复制代码
因此,要了解
  1. peer
复制代码
命令,其实就是需要搞懂底下各个子命令的实现
[h2]chaincode包[/h2]在了解了
  1. fabric
复制代码
命令的构造方式后(
  1. cobra+viper
复制代码
初始化命令,然后挂载子命令),我们再来以
  1. chaincode
复制代码
这个包作为一个例子,看看
  1. peer
复制代码
是怎么与服务端通信的,首先,再次看到,
  1. chaincode
复制代码
仍然有一系列的子命令:
  • install
  • instantiate
  • invoke
  • package
  • query
  • signpackage
  • upgrade
  • list
    不过,这些命令是直接在chaincode包中实现的,例如
    1. install
    复制代码
    命令,就对应了install.go
    我们打开这个源码文件,看到入口就是:
  1. func installCmd(cf *ChaincodeCmdFactory) *cobra.Command {    chaincodeInstallCmd = &cobra.Command{        Use:       "install",        Short:     fmt.Sprint(installDesc),        Long:      fmt.Sprint(installDesc),        ValidArgs: []string{"1"},        RunE: func(cmd *cobra.Command, args []string) error {            var ccpackfile string            if len(args) > 0 {                ccpackfile = args[0]            }            return chaincodeInstall(cmd, ccpackfile, cf)        },    }//...}
复制代码
这里,核心就是这个叫做
  1. RunE
复制代码
的入口,这是
  1. cobra
复制代码
的命令对象的入口函数,可以看到,这个属性类型是一个函数,接受一个cobra.Command对象和参数字符串,返回一个error对象,这个入口最终则调用了
  1. chaincodeInstall
复制代码
这个函数。
于是,我们来看看
  1. chaincodeInstall
复制代码
做了什么:
  1. func chaincodeInstall(cmd *cobra.Command, ccpackfile string, cf *ChaincodeCmdFactory) error {        // 准备工作    var err error    if cf == nil { //因此peer各个命令的cf参数都是nil,因此这里是真正实例化cf的地方,                //InitCmdFactory函数里会根据cmd.Name()来填充一个grpc客户端,每个命令的grpc客户端都是不一样的        cf, err = InitCmdFactory(cmd.Name(), true, false)        if err != nil {            return err        }    }        // 准备工作    err = install(ccpackmsg, cf)    return err}//install the depspec to "peer.address"func install(msg proto.Message, cf *ChaincodeCmdFactory) error {    // 准备工作    proposalResponse, err := cf.EndorserClients[0].ProcessProposal(context.Background(), signedProp)    if err != nil {        return fmt.Errorf("Error endorsing %s: %s", chainFuncName, err)    }    if proposalResponse != nil {        logger.Infof("Installed remotely %v", proposalResponse)    }    return nil}
复制代码
可以看到,
  1. chaincodeInstall
复制代码
最后调用了
  1. install
复制代码
函数,而该函数最后则是调用了
  1. cf.EndorserClients[0].ProcessProposal
复制代码
,这个函数的定义位于
  1. core/endorser/endorser.go
复制代码
,这是一个grpc的服务端接口,服务定义在
  1. protos/peer/peer.proto
复制代码
:
  1. service Endorser {    rpc ProcessProposal(SignedProposal) returns (ProposalResponse) {}}
复制代码
入参消息定义在
  1. protos/peer/proposal.proto
复制代码
  1. message SignedProposal {    // The bytes of Proposal    bytes proposal_bytes = 1;  // Signaure over proposalBytes; this signature is to be verified against  // the creator identity contained in the header of the Proposal message  // marshaled as proposalBytes    bytes signature = 2;}
复制代码
返回消息定义在
  1. protos/peer/proposal_response.proto
复制代码
  1. message ProposalResponse {    // Version indicates message protocol version    int32 version = 1;    // Timestamp is the time that the message    // was created as  defined by the sender    google.protobuf.Timestamp timestamp = 2;    // A response message indicating whether the    // endorsement of the action was successful    Response response = 4;    // The payload of response. It is the bytes of ProposalResponsePayload    bytes payload = 5;    // The endorsement of the proposal, basically    // the endorser's signature over the payload    Endorsement endorsement = 6;}
复制代码
这样一来,我们就大致理清了
  1. peer
复制代码
命令的工作流程:
  • 根据vip和cobra获取配置信息和命令行信息
  • 根据传入的参数和配置,实例化各个命令的grpc客户端
  • 构造grpc消息,并调用rpc方法,发送请求,并获取消息响应
  • 根据响应,构造命令的输出值
0x02 小结本文简单介绍了
  1. fabric
复制代码
的客户端工具的代码流程,归纳总结了一下
  1. fabric
复制代码
中cli工具的大致工作流程,可以看到,通过rpc,服务端和客户端建立了一种非常松散的耦合关系,值得学习。
         
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:
帖子:
精华:
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP