关于如何搭建BitShares多节点私链请参看:BitShares 2.0 多节点私链部署
关于如何搭建BitShares的web钱包搭建和接入私链请参考:BitShares之web钱包搭建及接入私链
关于如何对BitShares进行交易量压力测试请参考:BitShares交易量压力测试
目录
基于便于管理和测试石墨烯(graphene)区块链的目的,本文讲解如何在局域网中部署一个包含多个证人节点的BitShares 2.0 私有测试链。
一般来说,在BitShares2.0中,每个证人是不同的账号在不同的节点上运行,但为了方便私有测试网的部署和讲解,在整个区块链创建初始时使用1个节点1个账号控制11个证人生产区块,后面再逐渐创建新的证人逐步替代。
基于上面的目标,实现步骤如下:
本文基于的BitShares的版本为:BitShares Core Release 2.0.180425
本文基于的操作系统为: Ubuntu 16.04.4 LTS
官方建议使用32GB(至少16GB) RAM、SSD硬盘的专用服务器。本人使用普通非SSD硬盘,经测试,编译BitShares全节点程序至少需要8G RAM。
更新和安装依赖库:
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
本节主要讲解如何从genesis.json
开始,创建一个区块链,并让证人节点开始生产区块。
创建测试目录,并切换到该目录
mkdir -p testnet_home && cd testnet_home/
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,
保存并退出。
运行如下命令来初始化证人节点:
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
此时,完成了两件事:
data
目录和在其之下的config.ini
文件。Chain ID
另外保存起来,后面会用到。打开[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-wallet
和web
钱包与证人节点连接。genesis-json
,设置genesis.json
的路径,通常只在创建新链生产创世区块时设置。enable-stale-production
,让本节点无视区块链数据的时间,无论如何都生成区块数据。该字段通常只在创建新链生产创世区块时设为true。当已存在区块链时,一定要将本参数设为false或者不管,否则会因数据不完整导致分叉。seed-nodes
,设置种子节点集合,以方便快速连接到区块链网络和同步区块链数据。在创建新链生产创世区块时设为空,以防止连接到正式网络(代码)中的默认种子节点。当连接已有区块链网络时,尽可能多的设置种子节点以加快同步速度。witness-id
,用于授权本证人节点所代表的证人id产生区块,可指定多个。一般来说一个证人节点授权一个证人id,私链第一个节点指定了11个。关键时刻到来了,生产私有测试链的第一个区块。 运行如下命令:
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
本节主要讲解如何使用命令行钱包导入账户、创建一个新账户,并转账,查看属性等等。
现在使用命令行钱包(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
要将帐户导入钱包,必须知道账户名称和私钥才行。使用import_key
命令将代码中默认的账户nathan
导入钱包(其私钥为5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
):
import_key nathan 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
注意:帐户名称是前面在genesies.json
文件中设置的, 私钥也是前面在config.ini
文件中设置的。
现在已经将私钥导入到钱包中,但初始账户仍然没有资金(后面升级账户需要资金)。 资金在genesies.json
文件中的initial_balances
字段已经设置好了。 使用import_balance
命令可以将这些资金免费(相对于转账需要付手续费来说的)导入钱包:
import_balance nathan ["5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"] true
现在来创建另一个帐户(名为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
使用transfer
命令从nathan
账户里转一些资金alpha
给:
transfer nathan alpha 100000 BTS "here is some cash" true
可以验证alpha
是否收到了钱,运行一下命令查看账户余额:
list_account_balances alpha
前面已经创建一个新的区块链,并已经开启生产区块。本节主要讲解如何在新节点上创建新账户并让新账户成为证人,并投票成活跃证人生产区块。
在新节点上按同样的方式编译安装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
种子节点集合一定填写之前启动的全节点(即证人节点)的 ip
和 p2p-endpoint
端口。rpc-endpoint
指定开启的rpc监听端口,以方便cli-wallet
和web
钱包与证人节点连接。
使用如下命令重新运行证人节点:
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)
当该节点和网络中的区块数据同步完成后,并定期从其他节点接收新区块后,才能继续下一步。
基本和前面的操作一样(详细讲解请参考:命令行钱包的使用)。保持证人节点处于运行状态,打开另一个命令提示符窗口:
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
新账户必须升级为终身会员(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
要成为证人,首先需要创建一个可以投票的证人对象。
create_witness alpha "http://www.alpha" true
可运行get_witness
来获得新证人的id
和signing_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
分钟。
一旦下一次维护时间间隔过去后,可以使用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