BitShares 2.0 多节点私链部署

基于便于管理和测试石墨烯(graphene)区块链的目的,本文讲解如何在局域网中部署一个包含多个证人节点的BitShares 2.0 私有测试链。

1. 总体概述

一般来说,在BitShares2.0中,每个证人是不同的账号在不同的节点上运行,但为了方便私有测试网的部署和讲解,在整个区块链创建初始时使用1个节点1个账号控制11个证人生产区块,后面再逐渐创建新的证人逐步替代。

基于上面的目标,实现步骤如下:

  1. 首先在一个节点上初始化和启动整个区块链测试链。
  2. 创建其他账号,将其选举投票为证人。
  3. 开启其他证人节点,加入网络开始生产区块。

2. 软件版本和硬件环境要求

  • 本文基于的BitShares的版本为:BitShares Core Release 2.0.180425

  • 本文基于的操作系统为: Ubuntu 16.04.4 LTS

  • 官方建议使用32GB(至少16GB) RAM、SSD硬盘的专用服务器。本人使用普通非SSD硬盘,经测试,编译BitShares全节点程序至少需要8G RAM。

3. 编译和安装 BitShares Core

更新和安装依赖库:

sudo apt-get update
sudo apt-get install autoconf cmake git libboost-all-dev libssl-dev g++ libcurl4-openssl-dev

克隆代码仓库,切换到指定tag,更新子模块代码:

git clone https://github.com/bitshares/bitshares-core.git
cd bitshares-core && git checkout 2.0.180425
git submodule update --init --recursive

生成Makefile,编译,安装

cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .
make
sudo make install

检查是否编译安装成功:

cd .. && which witness_node cli_wallet

4. 搭建私链并启动

本节主要讲解如何从genesis.json开始,创建一个区块链,并让证人节点开始生产区块。

4.1 创建目录

创建测试目录,并切换到该目录

mkdir -p testnet_home && cd testnet_home/

4.2 创建和编辑genesis文件

witness_node --create-genesis-json=my-genesis.json
vim my-genesis.json

石墨烯(graphene)代码中默认的geneis块包含唯一一个账户,geneis块中所有的初始证人,初始委员会成员和初始证人候选人都是该账户,该账户的名称是:nathan,私钥为:5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3

genesis文件是用来定义区块链网络初始状态,可以诸如以下的状态:

  • 存在于genesis区块的账户,包括账户的名字和公钥
  • 资产及其初始分配状况(包括核心资产)
  • 区块链参数的初始值
  • 初始证人的帐户/签名密钥(或者其他任何帐户,注:默认的 witness 是 init0~inti10)。

在本人的设置中,为了加快活跃证人的更新时间,修改如下:

"maintenance_interval": 600,

保存并退出。

4.3 初始化证人节点

运行如下命令来初始化证人节点:

witness_node --data-dir data/ --genesis-json my-genesis.json --seed-nodes "[]"

当类似以下信息出现时,意味着初始化过程完成,按ctrl+c关闭见证节点:

1620535ms th_a main.cpp:266 main] Started BitShares node on a chain with 0 blocks.
1620535ms th_a main.cpp:267 main] Chain ID is 20e5aa92b1e3b7f971796a3f3fba59ea0a34b1eeb6fb5e585b2e4c9e441015f4

此时,完成了两件事:

  1. 创建了区块链data目录和在其之下的config.ini文件。
  2. 获得了区块链ID。将Chain ID另外保存起来,后面会用到。

4.4 配置证人节点

打开[Testnet-Home]/data/config.ini文件:

vim data/config.ini

在合适的位置进行以下设置,注意将重复的设置注释掉:

p2p-endpoint = 0.0.0.0:31010
seed-nodes = []
rpc-endpoint = 0.0.0.0:38090
genesis-json = my-genesis.json
enable-stale-production = true

# ID of witness controlled by this node (e.g. "1.6.5", quotes are required, may specify multiple times)
witness-id = "1.6.1"
witness-id = "1.6.2"
witness-id = "1.6.3"
witness-id = "1.6.4"
witness-id = "1.6.5"
witness-id = "1.6.6"
witness-id = "1.6.7"
witness-id = "1.6.8"
witness-id = "1.6.9"
witness-id = "1.6.10"
witness-id = "1.6.11"

解释一下上面的设置:

  • p2p-endpoint,指定开启的p2p监听端口,以方便其他节点连接,可以作为其他节点的seed-node
  • rpc-endpoint,指定开启的rpc监听端口,以方便cli-walletweb钱包与证人节点连接。
  • genesis-json,设置genesis.json的路径,通常只在创建新链生产创世区块时设置。
  • enable-stale-production,让本节点无视区块链数据的时间,无论如何都生成区块数据。该字段通常只在创建新链生产创世区块时设为true。当已存在区块链时,一定要将本参数设为false或者不管,否则会因数据不完整导致分叉。
  • seed-nodes,设置种子节点集合,以方便快速连接到区块链网络和同步区块链数据。在创建新链生产创世区块时设为空,以防止连接到正式网络(代码)中的默认种子节点。当连接已有区块链网络时,尽可能多的设置种子节点以加快同步速度。
  • witness-id,用于授权本证人节点所代表的证人id产生区块,可指定多个。一般来说一个证人节点授权一个证人id,私链第一个节点指定了11个。

4.5 开始区块生产

关键时刻到来了,生产私有测试链的第一个区块。 运行如下命令:

witness_node --data-dir=data

将看到如下重要信息:

********************************
*                              *
*   ------- NEW CHAIN ------   *
*   - Welcome to Graphene! -   *
*   ------------------------   *
*                              *
********************************

接着连续将看到类以下消息,表明成功开始生产区块:

453735ms th_a main.cpp:266 main] Started BitShares node on a chain with 0 blocks.
453735ms th_a main.cpp:267 main] Chain ID is 20e5aa92b1e3b7f971796a3f3fba59ea0a34b1eeb6fb5e585b2e4c9e441015f4
455003ms th_a witness.cpp:181 block_production_loo] Generated block #1 with timestamp 2018-05-11T15:07:35 at time 2018-05-11T15:07:35
460000ms th_a witness.cpp:181 block_production_loo] Generated block #2 with timestamp 2018-05-11T15:07:40 at time 2018-05-11T15:07:40
462000ms th_a witness.cpp:181 block_production_loo] Generated block #3 with timestamp 2018-05-11T15:07:42 at time 2018-05-11T15:07:42

5. 命令行钱包的使用

本节主要讲解如何使用命令行钱包导入账户、创建一个新账户,并转账,查看属性等等。

5.1 创建钱包

现在使用命令行钱包(cli_wallet)连接到上一节创建的证人节点(witness_node)。 保持证人节点处于运行状态,打开另一个命令提示符窗口,运行如下命令:

mkdir -p wallet && cd wallet
cli_wallet --wallet-file my-wallet.json --server-rpc-endpoint ws://127.0.0.1:38090 --rpc-endpoint 0.0.0.0:38099 --rpc-http-endpoint 0.0.0.0:38092 --chain-id 20e5aa92b1e3b7f971796a3f3fba59ea0a34b1eeb6fb5e585b2e4c9e441015f4

下面对指令参数进行解释:
--wallet-file:指定钱包json文件,该文件用于保存本钱包中所有加密后的私钥;
--server-rpc-endpoint:rpc服务器终端地址,指定钱包要连接的区块链全节点所需要的协议、地址和端口;
--rpc-endpoint:指定命令行钱包要监听的websocket RPC的ip地址和端口号;
--rpc-http-endpoint:指定命令行钱包要监听的HTTP RPC的ip地址和端口号;
--chain-id:指定要连接的区块链ID。不指定的话,钱包连接到bitshares的正式链上。

收到set_password提示,表示cli_wallet钱包已成功连接到witness_node节点。为钱包设置一个密码。 该密码用于加密钱包中所有的私钥。 在本例中,我使用supersecret作为密码。

使用如下命令给钱包创建密码并解锁新创建的钱包:

set_password supersecret
unlock supersecret

5.2 导入账号和资金

要将帐户导入钱包,必须知道账户名称和私钥才行。使用import_key命令将代码中默认的账户nathan导入钱包(其私钥为5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3):

import_key nathan 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3

注意:帐户名称是前面在genesies.json文件中设置的, 私钥也是前面在config.ini文件中设置的。

现在已经将私钥导入到钱包中,但初始账户仍然没有资金(后面升级账户需要资金)。 资金在genesies.json文件中的initial_balances字段已经设置好了。 使用import_balance命令可以将这些资金免费(相对于转账需要付手续费来说的)导入钱包:

import_balance nathan ["5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"] true

5.3 创建新帐户

现在来创建另一个帐户(名为alpha ),以便在nathan和alpha之间来回转移资金,产生交易。

只能使用现有的账户去创建新账户,且该账户(称为注册商: registrar)必须支付注册费用。 此外,要求注册商账户必须是终身会员(LTM: lifetime member)。使用upgrade_account命令将nathan升级到LTM:

upgrade_account nathan true

注意:由于缓存问题 ,需要重启cli_wallet钱包,否则升级nathan没有更新。 按下ctrl+c停止CLI钱包,运行与以下命令重新钱包并解锁:

cli_wallet --wallet-file my-wallet.json --server-rpc-endpoint ws://127.0.0.1:38090 --chain-id 20e5aa92b1e3b7f971796a3f3fba59ea0a34b1eeb6fb5e585b2e4c9e441015f4
unlock supersecret

确认nathan现在处于LTM状态:

get_account nathan

在打印的账户属性中,membership_expiration_date的属性值应该是1969-12-31T23:59:59 。 如果仍然是1970-01-01T00:00:00,那么账户就没有成功升级。

现在用原账户作为注册商注册一个新帐户了。 首先需要新帐户的公钥。可以通过suggest_brain_key命令来获得:

suggest_brain_key

显示的信息大致如下:

{
  "brain_priv_key": "FACADAL POOF FOXER MOUSE BANDOG CUBEB BRIERED NE JUTKA QUILLED TORIC GRYLLOS FLEAM LEEWILL PLENIST HUSBAND",
  "wif_priv_key": "5JtnWHxYMUD13sBPNMzprH1Z6r1nH3EWV1WeQXpAbsMLgxVt7tp",
  "pub_key": "BTS5s2iBiMeaCt96hoiAhQz94AqiPChTNF2YpYt6zJNNFJonhXPTF"
}

其中brain_priv_key是私钥助记符,wif_priv_key是钱包导入格式的私钥,pub_key。一定要记下以上重要的信息找个黑客攻击不到的地方保存起来,非常关键。

现在可以注册一个新帐户。可以只使用前面推荐的公钥来注册一个名为alpha的新帐户:

register_account alpha BTS5s2iBiMeaCt96hoiAhQz94AqiPChTNF2YpYt6zJNNFJonhXPTF BTS5s2iBiMeaCt96hoiAhQz94AqiPChTNF2YpYt6zJNNFJonhXPTF nathan nathan 0 true

5.4 转移资金给新账户

使用transfer命令从nathan账户里转一些资金alpha给:

transfer nathan alpha 100000 BTS "here is some cash" true

可以验证alpha是否收到了钱,运行一下命令查看账户余额:

list_account_balances alpha

6. 部署其他证人节点生产区块

前面已经创建一个新的区块链,并已经开启生产区块。本节主要讲解如何在新节点上创建新账户并让新账户成为证人,并投票成活跃证人生产区块。

6.1 新证人节点连接到区块链网

在新节点上按同样的方式编译安装BitShares Core 测试版

为了保证新节点连接的是前面创建的私链,必须保证chain id相同,而witness_node无法指定chain id,只能通过genesis.json文件初始化。

将原来的genesis.json复制到新节点指定目录中。可用如下指令完成该功能:

mkdir -p testnet-home && cd testnet-home/
scp bitshares@192.168.110.103:~/testnet_home/my-genesis.json .

然后运行以下指令初始化该证人节点,并按ctrl+c完成:

witness_node --data-dir data/ --genesis-json my-genesis.json --seed-nodes "[]"

然后编辑 data 目录下的config.ini,修改设置如下:

seed-nodes = ["192.168.110.103:31010"]
rpc-endpoint = 127.0.0.1:38090

基本和前面的config.ini设置差不多,有两点要注意,seed-nodes种子节点集合一定填写之前启动的全节点(即证人节点)的 ipp2p-endpoint 端口。rpc-endpoint指定开启的rpc监听端口,以方便cli-walletweb钱包与证人节点连接。

使用如下命令重新运行证人节点:

witness_node --data-dir data

由于本节点没有证人权限,所以不会生产区块,只会同步区块信息。打印的信息类似如下:

2188254ms th_a       main.cpp:266                  main                 ] Started BitShares node on a chain with 0 blocks.
2188254ms th_a       main.cpp:267                  main                 ] Chain ID is 20e5aa92b1e3b7f971796a3f3fba59ea0a34b1eeb6fb5e585b2e4c9e441015f4
2190025ms th_a       application.cpp:512           handle_block         ] Got block: #460 time: 2018-05-16T09:36:30 latency: 25 ms from: init6  irreversible: 452 (-8)
2195020ms th_a       application.cpp:512           handle_block         ] Got block: #461 time: 2018-05-16T09:36:35 latency: 20 ms from: init1  irreversible: 453 (-8)
2200021ms th_a       application.cpp:512           handle_block         ] Got block: #462 time: 2018-05-16T09:36:40 latency: 21 ms from: init10  irreversible: 454 (-8)

当该节点和网络中的区块数据同步完成后,并定期从其他节点接收新区块后,才能继续下一步。

6.2 打开新钱包创建新账户

基本和前面的操作一样(详细讲解请参考:命令行钱包的使用)。保持证人节点处于运行状态,打开另一个命令提示符窗口:

mkdir -p wallet && cd wallet
cli_wallet --wallet-file my-wallet.json --server-rpc-endpoint ws://127.0.0.1:38090 --chain-id 20e5aa92b1e3b7f971796a3f3fba59ea0a34b1eeb6fb5e585b2e4c9e441015f4

设置新钱包密码,并解锁:

set_password my_password
unlock my_password

只能使用现有的账户去创建新账户,因此先导入 nathan 用户:

import_key nathan 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3

使用指令suggest_brain_key获得新账户信息:

suggest_brain_key

得到新账户信息如下:

{
  "brain_priv_key": "FACADAL POOF FOXER MOUSE BANDOG CUBEB BRIERED NE JUTKA QUILLED TORIC GRYLLOS FLEAM LEEWILL PLENIST HUSBAND",
  "wif_priv_key": "5JtnWHxYMUD13sBPNMzprH1Z6r1nH3EWV1WeQXpAbsMLgxVt7tp",
  "pub_key": "BTS5s2iBiMeaCt96hoiAhQz94AqiPChTNF2YpYt6zJNNFJonhXPTF"
}

创建新账户alpha

register_account alpha BTS5s2iBiMeaCt96hoiAhQz94AqiPChTNF2YpYt6zJNNFJonhXPTF BTS5s2iBiMeaCt96hoiAhQz94AqiPChTNF2YpYt6zJNNFJonhXPTF nathan nathan 0 true

6.3 升级新账户为终生会员

新账户必须升级为终身会员(LTM: lifetime member)后才能称为证人账户。而新账户升级为LTM需要的资金只能通过其他账户转账。

给账户alpha转入一些资金:

transfer nathan alpha 100000 BTS "here is some cash!" true

导入新账户alpha

import_key alpha 5JtnWHxYMUD13sBPNMzprH1Z6r1nH3EWV1WeQXpAbsMLgxVt7tp

可以查看当前钱包的账户及其余额:

list_my_accounts
list_account_balances alpha

现在可以升级为终身会员了:

upgrade_account alpha true

6.4 注册为证人并投票为活跃证人

要成为证人,首先需要创建一个可以投票的证人对象。

create_witness alpha "http://www.alpha" true

可运行get_witness 来获得新证人的idsigning_key

get_witness alpha

得到结果:

{
  "id": "1.6.12",
  "witness_account": "1.2.18",
  "last_aslot": 0,
  "signing_key": "BTS6CNPh8b1xkDPxmn9mNt968QGGM8W7vcbfKthXC12p1HdWSKyNT",
  "vote_id": "1:22",
  "total_votes": 0,
  "url": "http://www.alpha",
  "total_missed": 0,
  "last_confirmed_block_num": 0
}

witness_node需要signing_key和对应的私钥,并联合id才能生成区块。通过get_private_key获取该签名密钥的私钥:

get_private_key "BTS6CNPh8b1xkDPxmn9mNt968QGGM8W7vcbfKthXC12p1HdWSKyNT"

得到私钥如下:

"5KNTT2tYjZ2eDDZUkPjHHnr1PJGCpBaMMWtxt9JD7fXxsTwanuF"

要想让证人生产区块就必须给新建的证人对象投票让其成为活跃证人:

vote_for_witness alpha alpha true true

只有等到下一个维护间隔后,投票才会统计,证人 才有可能成为新的活跃证人。可以使用命令get_dynamic_global_properties查看next_maintenance_time字段的值。在前面我们将维护间隔重新设置为了10分钟。

6.5 让新证人节点生产区块

一旦下一次维护时间间隔过去后,可以使用get_global_properties命令来查看到当前的活动证人列表,根据证人id查看新证人已被投票成为活跃证人。一旦成为活跃证人后, 就可以让新证人节点生产区块。

现在需要重新启动证人,关闭钱包(ctrl+d),然后关闭证人节点(ctrl+c)。 用上面获得新证人id及其公私钥对编辑 data 目录下的config.ini,添加设置如下:

# ID of witness controlled by this node (e.g. "1.6.5", quotes are required, may specify multiple times)
witness-id = "1.6.12"

# Tuple of [PublicKey, WIF private key] (may specify multiple times)
private-key = ["BTS6CNPh8b1xkDPxmn9mNt968QGGM8W7vcbfKthXC12p1HdWSKyNT","5KNTT2tYjZ2eDDZUkPjHHnr1PJGCpBaMMWtxt9JD7fXxsTwanuF"]
  • private-key, 指定本证人节点生产和签署区块所需的公私钥对。

重新运行证人节点:

witness_node --data-dir data

如果witness_node的输出结果如下,那么就轮到本证人节点生产区块,并成功出块:

909991ms th_a       application.cpp:512           handle_block         ] Got block: #907 time: 2018-05-16T10:15:10 latency: -8 ms from: init9  irreversible: 898 (-9)
915000ms th_a       witness.cpp:181               block_production_loo ] Generated block #908 with timestamp 2018-05-16T10:15:15 at time 2018-05-16T10:15:15
919991ms th_a       application.cpp:512           handle_block         ] Got block: #909 time: 2018-05-16T10:15:20 latency: -8 ms from: init6  irreversible: 901 (-8)

参考文章:

bitshares-core/README.md: https://github.com/bitshares/bitshares-core/blob/master/README.md
Private Testnet Howto: http://docs.bitshares.eu/testnet/private-testnet.html
private testnet: https://github.com/bitshares/bitshares-core/wiki/private-testnet
CLI Wallet: http://docs.bitshares.org/integration/apps/cliwallet.html
Howto Run a Block-producing Witness: http://docs.bitshares.eu/bitshares/tutorials/witness-howto.html
Bitshares 私链部署witness节点: https://www.jianshu.com/p/2cb1cdb98529