基础底层搭建
-
目标
本章的目标是用两个服务器,实现群组1的搭建,每个服务器各有两个节点。
首先准备两台测试服务器: -
提示
对服务器里的内容进行目录化的管理,可以使用WinSCP软件,并且本章中所有的命令行都是基于Ubuntu系统的。
下载安装
下载和安装在服务器A和服务器B都需要进行相同的操作。
- 下载
cd ~/ && git clone https://github.com/FISCO-BCOS/generator.git
- 安装
此操作要求用户具有sudo权限。
cd ~/generator && bash ./scripts/install.sh
检查是否安装成功,若成功,会输出 usage: generator xxx
./generator -h
- 获取节点二进制
拉取最新fisco-bcos二进制文件到meta中
./generator --download_fisco ./meta
- 检查二进制版本
若成功,输出 FISCO-BCOS Version : x.x.x-x
./meta/fisco-bcos -v
初始化链证书
初始化链证书一个群组里只能在一个服务器上进行,这里采用的是在服务器A进行初始化链证书
cd ~/generator
./generator --generate_chain_certificate ./dir_chain_ca
初始化机构A
在服务器A上进行初始化:
./generator --generate_agency_certificate ./dir_agency_ca ./dir_chain_ca agencyA
执行之后,会在 generator/dir_agency_ca/agencyA/目录中生成机钥证书和私钥:
将 generator/dir_agency_ca/agencyA/目录中的链证书、机构证书、机构私钥复制到 generator/meta/文件中
cp ./dir_agency_ca/agencyA/* ~/generator/meta/
复制后:
复制初始化链证书
因为只有服务器A生成了初始化链证书,这里需要手动复制服务器A上 generator/ 目录中的 dir_chain_ca,将A中的dir_chain_ca 复制到服务器B上的 generato/目录下。
初始化机构B
在服务器A上进行初始化:
./generator --generate_agency_certificate ./dir_agency_ca ./dir_chain_ca agencyB
执行之后,会在 generator/dir_agency_ca/agencyB/目录中生成机钥证书和私钥。
将 generator/dir_agency_ca/agencyB/目录中的链证书、机构证书、机构私钥复制到 generator/meta/文件中
cp ./dir_agency_ca/agencyB/* ~/generator/meta/
复制后:
修改配置文件
手动的方式修改conf文件夹下面node_deployment.ini
p2p_ip地址修改为该服务器对应的外网ip,其中rpc_ip要修改为内网ip。
上图为服务器A中的node_deployment.ini配置。服务器B中的node_deployment.i也根据服务器ip进行相应的修改。
机构A生成并发送节点信息
机构(服务器)A生成并发送节点信息
./generator --generate_all_certificates ./agencyA_node_info
机构B生成并发送节点信息
./generator --generate_all_certificates ./agencyB_node_info
创世机构收集节点证书
生成创世区块的机构需要节点证书,本章中是由A机构生成创世区块,因此B机构需要发送节点证书至机构A。将机构B节点agencyB_node_info文件夹cert_*.crt文件copy到机构A的meta文件夹中。
上图是将机构B中的两个节点证书成功拷贝到机构A中的meta,此时meta中共有四个节点证书,两个为机构A的,两个为机构B的。
各机构节点连接信息相互收集
例如:将机构B中的 peersB.txt 拷贝到机构A的meta文件夹,同时,将机构A中的 peersA.txt 拷贝到机构B的meta文件夹。每个peers*.txt里都是该机构的节点连接信息。
注:在机构B中,一开始txt的名称为peers.txt,需要将其复制重命名为peerB.txt,然后拷贝到机构A中。机构A中也同理。上面两张图中都有peers.txt,这是多余的,大家在实际操作中是没有的。
机构A生成群组1创世区块
-
将机构A中的generator/conf/conf/group_genesis.ini进行手动修改,配置成如图:
其中node0-node3这四个节点,分别为两个服务器上节点的外网ip+端口。 -
执行命令 生成group_genesis.ini 配置的群组创世区块:
./generator --create_group_genesis ./group
- 分发群组1创世区块至机构B
手动复制 机构A中的 ./group/group.1.genesis文件到机构B的meta文件夹下。
机构A生成所属节点
执行:
./generator --build_install_package ./meta/peersB.txt ./nodeA
机构B生成所属节点
执行:
./generator --build_install_package ./meta/peersA.txt ./nodeB
各自启动机构下的节点
- 启动A机构下的节点:
bash ./nodeA/start_all.sh
- 启动B机构下的节点:
bash ./nodeB/start_all.sh
注意:如果端口被占用,可以根据端口查找进程
sudo lsof -i:[port]
然后根据PID杀掉进程
sudo kill [PID]
控制台部署
在服务器A和服务器B都进行以下的配置。
- 添加ppa源
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
如果报错:则
add-apt-repository
sudo apt-get install python-software-properties
然后再:
sudo add-apt-repository ppa:webupd8team/java
- 安装openjdk
sudo apt install openjdk-8-jre-headless
- 配置jdk
下载jkd到电脑上:链接:下载jdk网址
新建software文件夹,将jdk压缩包放置其中
在/usr/lib/ 新建jdk文件夹,在software文件里执行解压命令
sudo tar -zxvf jdk-8u231-linux-x64.tar.gz -C /usr/lib/jdk
- 修改配置
sudo vi /etc/profile
export JAVA_HOME=/usr/lib/jdk/jdk1.8.0_231
export JRE_HOME=${JAVA_HOME}/jre
export PATH=${JAVA_HOME}/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib:${JRE_HOME}/lib
执行命令,立即生效:
source /etc/profile
sudo update-alternatives --install /usr/bin/java java /usr/lib/jdk/jdk1.8.0_231/bin/java 300
sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jdk/jdk1.8.0_231/bin/javac 300
- 下载控制台程序
./generator --download_console ./ --cdn
- 在服务器A中执行
./generator --generate_sdk_certificate ./dir_sdk_ca ./dir_agency_ca/agencyA
- 在服务B中执行
./generator --generate_sdk_certificate ./dir_sdk_ca ./dir_agency_ca/agencyB
以上所有操作执行完,便完成了控制台的部署。
示例
进入generator/console 后,输入:
./start.sh
即可进入控制台。
可以部署简单的Asset.sol合约,合约代码:
pragma solidity ^0.4.24;
import "./Table.sol";
contract Asset {
// event
event RegisterEvent(int256 ret, string account, uint256 asset_value);
event TransferEvent(int256 ret, string from_account, string to_account, uint256 amount);
constructor() public {
// 构造函数中创建t_asset表
createTable();
}
function createTable() private {
TableFactory tf = TableFactory(0x1001);
// 资产管理表, key : account, field : asset_value
// | 资产账户(主键) | 资产金额 |
// |-------------------- |-------------------|
// | account | asset_value |
// |---------------------|-------------------|
//
// 创建表
tf.createTable("t_asset", "account", "asset_value");
}
function openTable() private returns(Table) {
TableFactory tf = TableFactory(0x1001);
Table table = tf.openTable("t_asset");
return table;
}
/*
描述 : 根据资产账户查询资产金额
参数 :
account : 资产账户
返回值:
参数一: 成功返回0, 账户不存在返回-1
参数二: 第一个参数为0时有效,资产金额
*/
function select(string account) public constant returns(int256, uint256) {
// 打开表
Table table = openTable();
// 查询
Entries entries = table.select(account, table.newCondition());
uint256 asset_value = 0;
if (0 == uint256(entries.size())) {
return (-1, asset_value);
} else {
Entry entry = entries.get(0);
return (0, uint256(entry.getInt("asset_value")));
}
}
/*
描述 : 资产注册
参数 :
account : 资产账户
amount : 资产金额
返回值:
0 资产注册成功
-1 资产账户已存在
-2 其他错误
*/
function register(string account, uint256 asset_value) public returns(int256){
int256 ret_code = 0;
int256 ret= 0;
uint256 temp_asset_value = 0;
// 查询账户是否存在
(ret, temp_asset_value) = select(account);
if(ret != 0) {
Table table = openTable();
Entry entry = table.newEntry();
entry.set("account", account);
entry.set("asset_value", int256(asset_value));
// 插入
int count = table.insert(account, entry);
if (count == 1) {
// 成功
ret_code = 0;
} else {
// 失败? 无权限或者其他错误
ret_code = -2;
}
} else {
// 账户已存在
ret_code = -1;
}
emit RegisterEvent(ret_code, account, asset_value);
return ret_code;
}
/*
描述 : 资产转移
参数 :
from_account : 转移资产账户
to_account : 接收资产账户
amount : 转移金额
返回值:
0 资产转移成功
-1 转移资产账户不存在
-2 接收资产账户不存在
-3 金额不足
-4 金额溢出
-5 其他错误
*/
function transfer(string from_account, string to_account, uint256 amount) public returns(int256) {
// 查询转移资产账户信息
int ret_code = 0;
int256 ret = 0;
uint256 from_asset_value = 0;
uint256 to_asset_value = 0;
// 转移账户是否存在?
(ret, from_asset_value) = select(from_account);
if(ret != 0) {
ret_code = -1;
// 转移账户不存在
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
// 接受账户是否存在?
(ret, to_asset_value) = select(to_account);
if(ret != 0) {
ret_code = -2;
// 接收资产的账户不存在
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
if(from_asset_value < amount) {
ret_code = -3;
// 转移资产的账户金额不足
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
if (to_asset_value + amount < to_asset_value) {
ret_code = -4;
// 接收账户金额溢出
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
Table table = openTable();
Entry entry0 = table.newEntry();
entry0.set("account", from_account);
entry0.set("asset_value", int256(from_asset_value - amount));
// 更新转账账户
int count = table.update(from_account, entry0, table.newCondition());
if(count != 1) {
ret_code = -5;
// 失败? 无权限或者其他错误?
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
Entry entry1 = table.newEntry();
entry1.set("account", to_account);
entry1.set("asset_value", int256(to_asset_value + amount));
// 更新接收账户
table.update(to_account, entry1, table.newCondition());
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
}
效果图: