GO语言公链开发实战
上QQ阅读APP看书,第一时间看更新

2.2 bytomcli交互工具

2.2.1 bytomcli命令flag参数

bytomcli的使用方式分为两种:一种是后面接flags参数,bytomcli [flags], bytomcli使用-h参数查看命令的帮助文档;另一种是后面接子命令,bytomcli [command]。所有的命令选项都可以通过执行-h或--help获得指定命令的帮助信息。bytomcli的帮助信息、示例相当详细,简单易懂。建议大家使用帮助信息。

        $ ./bytomcli -h

    Bytomcli is a commond line client for bytom core (a.k.a. bytomd)


    Usage:
      bytomcli [flags]
      bytomcli [command]


    Available Commands:
      build-transaction              创建一笔交易
      check-access-token             校验access token是否合法
      create-access-token           创建accesss token
      create-account                 创建账户
      create-account-receiver       创建账户地址
      create-asset                   创建资产
      create-key                     创建key
      create-transaction-feed       创建交易feed过滤器
      decode-program                 将程序解码为指令和数据
      decode-raw-transaction        解码原始交易
      delete-access-token           删除access token
      delete-account                 删除账户
      delete-key                     删除key
      delete-transaction-feed       删除交易feed过滤器
      estimate-transaction-gas      估算交易费
      gas-rate                       显示当前交易费比例
      get-asset                      通过资产id获得资产详情
      get-block                      根据区块哈希值或高度获取完成区块信息
      get-block-count                获取最新区块的数量
      get-block-hash                 获取最新区块的哈希值
      get-block-header               根据区块哈希值或高度获取完成区块的头部信息
      get-difficulty                  获取最新区块的难度值
      get-hash-rate                  获取最新区块的nonce值
      get-transaction                根据交易hash值获取交易信息
      get-transaction-feed          通过别名获取交易feed
      get-unconfirmed-transaction    根据交易哈希值获取未确认的交易信息
      help                           显示帮助信息
      is-mining                      判断客户端是否开启挖矿功能
      list-access-tokens             显示access token信息
      list-accounts                  显示账户信息
      list-addresses                 显示账户地址信息
      list-assets                    显示资产信息
      list-balances                  显示账户余额
      list-keys                      显示存在的key信息
      list-pubkeys                   显示账户公钥
      list-transaction-feeds        显示所有的交易feed
      list-transactions              显示交易信息
      list-unconfirmed-transactions  显示未确认交易的哈希值
      list-unspent-outputs          显示账户未使用的输出
      net-info                       显示当前网络信息
      rescan-wallet                  重新扫描块信息到钱包
      reset-key-password             重置key密钥
      set-mining                     开始/停止挖矿
      sign-message                   对消息进行签名
      sign-transaction               使用账户密钥对交易进行签名
      submit-transaction             提交签名的交易

          update-asset-alias             更新资产别名
          update-transaction-feed       更新交易feed
          validate-address               校验交易地址
          verify-message                 校验消息签名
          version                        显示客户端版本信息
          wallet-info                    显示钱包信息

以上介绍了bytomcli命令行工具的详细参数。下面,将以一个实际的用例介绍如何使用bytomcli命令行工具。

2.2.2 使用bytomcli查看节点状态信息

使用net-info参数可以查看bytomd节点运行的网络状态。首先,使用bytomcli net-info-h查看net-info的帮助信息。命令执行如下:

        $ ./bytomcli net-info -h
        Print the summary of network
        Usage:
          bytomcli net-info [flags]
        Flags:
          -h, --help   help for net-info

由输出的帮助信息可知,net-info命令仅支持一个可选的flags参数,flags参数列表又仅包含-h参数,可用来查看net-info命令的帮助信息。所以net-info命令的用法是十分简单的,使用bytomcli net-info即可查看节点的网络状态:

        $ ./bytomcli net-info
        {
          "current_block": 36714,
          "highest_block": 36714,
          "listening": true,
          "mining": false,
          "network_id": "mainnet",
          "peer_count": 10,
          "syncing": false,
          "version": "1.0.5+2bc2396a"
        }

net-info参数返回数据如下所示。

❏ current_block:当前节点的当前区块高度为36714。

❏ highest_block:网络中最高区块高度为36714,表明节点已经拥有完整的区块数据。

❏ listening:当前节点处于监听状态。

❏ mining:当前节点是否启用挖矿功能。

❏ network_id:标识节点连接的网络类型(mainnet为正式主网,wisdom为测试网络,solonet为单节点网络)。

❏ peer_count:节点当前连接的其他节点个数。

❏ syncing:当前节点是否正在同步区块数据。

❏ version:节点的版本号。

2.2.3 bytomcli运行案例

本小节我们以net-info命令为例对bytomcli的源码进行剖析,其他命令参数与net-info参数执行的过程大同小异。

bytomcli相关代码文件结构如下所示:

        $ tree cmd/bytomcli/
        cmd/bytomcli/
        ├—— bytomcli
        ├—— commands                    commands目录存放bytomcli所有参数的实现
        |   ├—— accesstoken.go          token参数相关
        |   ├—— account.go              账户参数相关
        |   ├—— asset.go                资产参数相关
        |   ├—— block.go                块参数相关
        |   ├—— bytomcli.go             bytomcli flag解析相关
        |   ├—— key.go                  key参数相关
        |   ├—— mining.go               挖矿参数相关
        |   ├—— net.go                  节点状态参数相关
        |   ├—— program.go              合约参数相关
        |   ├—— template.go             交易模板参数相关
        |   ├—— transaction.go          交易参数相关
        |   ├—— txfeed.go               目前版本该功能未使用
        |   ├—— util.go                 bytomcli相关输出结构体
        |   ├—— version.go              节点版本参数相关
        |   └—— wallet.go               本地钱包参数相关
        └—— main.go                     bytomcli入口函数

1. Cobra库介绍

bytomcli命令行工具是基于Cobra实现的。Cobra既可以用来创建强大的现代CLI应用程序库,也可以用来生成应用和程序文件。很多知名的开源软件都使用Cobra实现其CLI部分,例如kubernetes、docker、etcd等。

Cobra基于三个基本的概念——commands、arguments和flags,实现对命令参数的解析和行为控制。commands是应用程序的中心,应用程序支持的每个交互都包含在命令中,命令可以具有子命令并可选地运行子命令。flags是修改命令行为的方法,Cobra支持POSIX标准和GO的flags包,并可以同时作用于父命令和子命令的参数,也支持只在父命令或者子命令有效的参数。

如何使用Cobra库来构建自己的CLI程序呢?下面我们简单介绍Cobra库的使用。

(1)安装Cobra库

Cobra是非常容易使用的,使用go get来安装最新版本的库。Cobra库相对比较大,安装它可能需要花费一些时间。安装完成后,在GOPATH/bin目录下应该有已经编译好的Cobra程序。

        $ go get -v github.com/spf13/cobra/cobra

(2)使用cobra命令生成应用程序

假设现在我们要开发一个基于CLI的命令程序,名字为demo。首先打开cmd,切换到GOPATH的src目录下,执行如下命令:

        $ cobra init demo

在src目录下会生成一个demo的文件夹,如下:

        $ tree demo/
        demo/
        ├—— LICENSE
        ├—— cmd
        |   └—— root.go
        └—— main.go

如果此时应用程序不需要子命令,那么Cobra生成应用程序的操作就结束了。这里我们先实现一个没有子命令的CLI程序,之后再为程序添加子命令。

接下来继续为demo设计功能。我们打开Cobra自动生成的main.go文件查看,如下:

        package main


        import "demo/cmd"


        func main() {
            cmd.Execute()
        }

main函数执行cmd包的Execute()方法,打开cmd包下的root.go文件查看,发现里面进行了一些初始化操作,并提供了Execute接口。其实,Cobra自动生成的root.go文件中有很多初始化操作是不需要的,其中viper是Cobra集成的配置文件读取的库,这里不需要使用,可以注释掉,如不注释掉则生成的应用程序会大10M左右。

在demo下面新建一个imp包,imp.go内容如下:

        package imp
        import(
            "fmt"
        )
        func Show(name string, age int) {
            fmt.Printf("My Name is %s, My age is %d\n", name, age)
        }

在imp.go文件中,Show函数接收两个参数——name和age,使用fmt打印出来。此时,整个demo项目目录结构如下:

        $ tree demo/
        demo/
        ├—— LICENSE
        ├—— cmd

        |   └—— root.go
        ├—— imp
        |   ├—— imp.go
        └—— main.go

Cobra的所有命令都是通过cobra.Command结构体实现的。为了实现demo功能,我们需要修改RootCmd。修改demo/cmd/root.go文件,如下所示:

        var RootCmd = &cobra.Command{
            Use:   "demo",
            Short: "A test demo",
            Long:  `Demo is a test appcation for print things`,
            // Uncomment the following line if your bare application
            // has an action associated with it:
            Run: func(cmd *cobra.Command, args []string) {
                if len(name) == 0 {
                    cmd.Help()
                    return
                }
                imp.Show(name, age)
            },
        }

虽然我们已经定义了command结构,也能通过Execute接口调用RootCmd定义的回调方法。但是若要实现demo的功能还需从命令行解析传入的参数,这部分应该如何实现?这部分可以在cmd包的init方法中实现。init()函数会在每个包完成初始化后自动执行,并且执行优先级比main函数高。init()函数通常被用来对变量进行初始化操作。这里我们使用init()函数在main函数执行之前解析命令行参数。修改demo/cmd/root.go文件,如下所示:

        var (
            name string
            age  int
        )


        func init() {
            RootCmd.Flags().StringVarP(&name, "name", "n", "", "person's name")
            RootCmd.Flags().IntVarP(&age, "age", "a", 0, "person's age")
        }

至此,demo的功能已经实现了,我们编译运行一下看看实际效果,如下所示:

        $ go run main.go
        Usage:
          demo [flags]


        Flags:
          -a, --age int         person's age
              --config string   config file (default is $HOME/.demo.yaml)
          -h, --help             help for demo
          -n, --name string     person's name
          -t, --toggle          Help message for toggle

如果我们想实现一个带有子命令的CLI程序,只需要再执行cobra add为程序新增子命令。下面我们为demo程序新增一个server的子命令,如下所示:

        $ cd demo
        $ cobra add server

在demo目录下生成了一个cmd/server.go文件,如下所示:

        $ tree demo/
        demo/
        ├—— LICENSE
        ├—— cmd
        |   ├—— root.go
        |   └—— server.go
        ├—— imp
        |   └—— imp.go
        └—— main.go

接下来配置server子命令,此操作与前文中修改root.go文件类似效果如下:

        $ go run main.go
        Usage:
          demo [flags]
          demo [command]


        Available Commands:
          help        Help about any command
          server      A brief description of your command


        Flags:
          -a, --age int         person's age
              --config string   config file (default is $HOME/.demo.yaml)
          -h, --help             help for demo
          -n, --name string     person's name
          -t, --toggle          Help message for toggle


        Use "demo [command] --help" for more information about a command.

2. bytomcli运行流程与原理

对Cobra库有了了解之后,我们再来学习bytomcli的代码时会感觉比较容易,其运行原理如图2-1所示。同上一节中的demo程序一样,bytomcli在main函数中使用cmd. Execute()来启动应用程序,cmd.Execute()是调用commands包的Execute()方法。在mian. go中引入commands包的时候,给它起了一个别名cmd。

图2-1 bytomcli运行原理

当程序调用commands包的Execute()方法时,会先执行commands包中所有的init()方法,这些init()主要用来解析命令行参数。当执行完commands包中所有的init()方法之后,才会执行Execute()。在Execute()方法中,首先执行AddCommands()方法,该方法主要用来给BytomcliCmd命令添加子命令;而AddTemplateFunc()方法则是为子命令添加使用模板的。在解析flags参数、添加子命令和添加使用模板这些初始化操作完成之后,才真正进入命令的执行过程。在BytomcliCmd.ExecuteC()方法中,Cobra库会根据用户输入的命令跳转到相应的子命令,同时执行命令定义的Run方法。

下面我们以create-access-token命令为例,看看这个命令的Run方法都执行了哪些操作。首先会取args参数的第一位作为tokenID,然后调用util包下的ClientCall方法请求/create-access-token路径,并将tokenID传入,创建token。

ClientCall方法封装了一个RPC的client,根据用户传入的路径和参数发送请求,并解析RPC server返回的内容。

至此,create-access-token命令执行完成,其生命周期终止。