我有一壶酒,足以慰平生。

0%

centos7搭建fisco-bcos国密环境

fisco(v2.6.0)国密环境搭建

安装

使用build_chain脚本在本地搭建一条4节点的FISCO BCOS链

1
2
3
4
5
6
7
8
# Ubuntu安装依赖
sudo apt install -y openssl curl
# centos安装环境
sudo yum install -y openssl openssl-devel
# 准备环境
cd ~ && mkdir -p fisco && cd fisco
# 下载build_chain.sh脚本
curl -#LO https://github.com/FISCO-BCOS/FISCO-BCOS/releases/download/v2.6.0/build_chain.sh && chmod u+x build_chain.sh

如果因为网络问题导致长时间无法下载build_chain.sh脚本,请尝试 curl -#LO https://gitee.com/FISCO-BCOS/FISCO-BCOS/raw/master/tools/build_chain.sh && chmod u+x build_chain.sh

搭建4节点FISCO BCOS链

1
2
3
4
5
6
# 生成一条4节点的FISCO链 4个节点都属于group1 下面指令在fisco目录下执行
# -p指定起始端口,分别是p2p_port,channel_port,jsonrpc_port
# 根据下面的指令,需要保证机器的30300~30303,20200~20203,8545~8548端口没有被占用
# -g 搭建国密版本的链
# -G 设置`chain.sm_crypto_channel=true`。确认sdk支持的情况下(web3sdk v2.5.0+),可以指定-G参数,连接也使用国密SSL
$ ./build_chain.sh -l 127.0.0.1:4 -p 30300,20200,8545 -g -G

执行成功会打印如下命令:

1
[INFO] All completed. Files in /home/ubuntu/fisco/nodes

启动FISCO BCOS链

启动命令:

1
bash nodes/127.0.0.1/start_all.sh

检查进程是否启动:

1
ps -ef | grep -v grep | grep fisco-bcos

正常执行结果如下:

image-20201126172404054

检测日志输出:

1
tail -f nodes/127.0.0.1/node0/log/log*  | grep connected

正常情况会不停地输出链接信息,从输出可以看出node0与另外3个节点有链接。

image-20201127142825361

执行下面指令,检查是否在共识:

1
tail -f nodes/127.0.0.1/node0/log/log*  | grep +++

正常情况会不停输出++++Generating seal,表示共识正常。

image-20201127142938274

配置及使用控制台

说明

  • 控制台1.x 系列基于 Web3SDK 实现,控制台2.6之后 基于 Java SDK 实现,最新版本控制台基于 Java SDK 实现
  • 2.6及其以上版本控制台使用文档请 参考这里 ,1.x版本控制台使用文档请 参考这里
  • 可通过命令 ./start.sh --version 查看当前控制台版本
  • 基于 Web3SDK 开发应用时将 solidity 代码转换为 java 代码时,必须使用 1.x 版本控制台,具体请参考 这里

准备依赖

  • 安装java(官方推荐java14 这里用的是jdk1.8)
1
2
3
4
5
# ubuntu系统安装java
sudo apt install -y default-jdk

#centos系统安装java
sudo yum install -y java java-devel
  • 获取控制台并回到fisco目录
1
cd ~/fisco && curl -#LO https://github.com/FISCO-BCOS/console/releases/download/v2.7.0/download_console.sh && bash download_console.sh
  • 拷贝控制台配置文件

    若节点未采用默认端口,请将文件中的20200替换成节点对应的channel端口

    1
    2
    # 最新版本控制台使用如下命令拷贝配置文件
    cp -n console/conf/config-example.toml console/conf/config.toml
  • 配置控制台证书

    搭建国密版时,如果使用国密SSL请执行

    1
    cp nodes/127.0.0.1/sdk/gm/* console/conf/

    搭建国密版时,请修改 applicationContext.xml 中 encryptType 修改为1

启动并使用控制台

  • 启动

    1
    cd ~/fisco/console && bash start.sh

    输出下述信息表明启动成功 否则请检查conf/config.toml中节点端口配置是否正确

    image-20201127143815133

  • 用控制台获取信息

    1
    2
    3
    4
    # 获取客户端版本
    [group:1]> getNodeVersion
    # 获取节点信息
    [group:1]> getPeers

    image-20201127143945877

部署及调用官方用例HelloWorld合约

第一步. 编写HelloWorld合约

==官方已给出不用自己编写了==

HelloWorld合约提供两个接口,分别是get()set(),用于获取/设置合约变量name

合约内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pragma solidity ^0.4.24;

contract HelloWorld {
string name;

function HelloWorld() {
name = "Hello, World!";
}

function get()constant returns(string) {
return name;
}

function set(string n) {
name = n;
}
}

第二步. 部署HelloWorld合约

HelloWorld合约已经内置于控制台中,位于控制台目录下contracts/solidity/HelloWorld.sol,参考下面命令部署即可。

1
2
# 在控制台输入以下指令 部署成功则返回合约地址
[group:1]> deploy HelloWorld

image-20201127144307896

第三步. 调用HelloWorld合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看当前块高
[group:1]> getBlockNumber

# 调用get接口获取name变量 此处的合约地址是deploy指令返回的地址
[group:1]> call HelloWorld 0x10f342ac7104b71b098e6740006e5ec6ac1606e4 get

# 查看当前块高,块高不变,因为get接口不更改账本状态
[group:1]> getBlockNumber

# 调用set设置name
[group:1]> call HelloWorld 0x10f342ac7104b71b098e6740006e5ec6ac1606e4 set "Hello, FISCO BCOS"

# 再次查看当前块高,块高增加表示已出块,账本状态已更改
[group:1]> getBlockNumber

# 调用get接口获取name变量,检查设置是否生效
[group:1]> call HelloWorld 0x10f342ac7104b71b098e6740006e5ec6ac1606e4 get

image-20201127144827615

WeBASE搭建

微众银行开源的自研区块链中间件平台——WeBASE(WeBank Blockchain Application Software Extension) 是区块链应用和FISCO BCOS节点之间搭建的中间件平台。WeBASE屏蔽了区块链底层的复杂度,降低区块链使用的门槛,大幅提高区块链应用的开发效率,包含节点前置、节点管理、交易链路,数据导出,Web管理平台等子系统。用户可以根据业务所需,选择子系统进行部署,可以进一步体验丰富交互的体验、可视化智能合约开发环境IDE。

WeBASE快速入门环境搭建

快速入门,只需要搭建节点和节点前置服务(WeBASE-Front),就可通过WeBASE-Front的合约编辑器进行合约的编辑,编译,部署,调试。

节点搭建

及上述过程所作的事情。

节点前置服务(WeBASE-Front)搭建

前提条件

依赖软件 支持版本
Java JDK8或以上版本
  1. 下载安装包

    1
    wget https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/WeBASE/releases/download/v1.4.1/webase-front.zip
  2. 解压

    1
    2
    unzip webase-front.zip
    cd webase-front
  3. 拷贝sdk证书文件(build_chain的时候生成的)

    将节点所在目录nodes/${ip}/sdk/gm/下的的所有证书拷贝到conf目录下。

    1
    cp -r nodes/127.0.0.1/sdk/gm/* /webase-front/conf
  4. 服务起停

    国密版则通过vi修改application.yml中将sdk-encryptType设置为1(默认为0),也可以直接通过以下命令进行快速修改,修改后即可执行启停命令进行服务启停。

    1
    sed -i "s%encryptType: 0%encryptType: 1%g" ./conf/application.yml

    服务启停命令:

    1
    2
    3
    启动: bash start.sh
    停止: bash stop.sh
    检查: bash status.sh
  5. 访问 http://{deployIP}:{frontPort}/WeBASE-Front

    结果如下:

    image-20201127145940125

构建第一个区块链应用

==官方案例搬砖学习==

通过学习教程,你将会了解到以下内容:

  1. 如何将一个业务场景的逻辑用合约的形式表达
  2. 如何将Solidity合约转化成Java类
  3. 如何配置Web3SDK
  4. 如何构建一个应用,并集成Web3SDK到应用工程
  5. 如何通过Web3SDK调用合约接口,了解Web3SDK调用合约接口的原理

要求用户熟悉Linux操作环境,具备Java开发的基本技能,能够使用Gradle工具,熟悉Solidity语法

具体详情见官方文档,这里只记录具体的操作过程,不牵扯到solidity合约的语法讲解,想了解solidity语法的同学可以看几篇博客

应用需求

区块链天然具有防篡改,可追溯等特性,这些特性决定其更容易受金融领域的青睐,本文将会提供一个简易的资产管理的开发示例,并最终实现以下功能:

  • 能够在区块链上进行资产注册
  • 能够实现不同账户的转账
  • 可以查询账户的资产金额

合约源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
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;
}
}

注: Asset.sol合约的实现需要引入FISCO BCOS提供的一个系统合约接口文件 Table.sol ,该系统合约文件中的接口由FISCO BCOS底层实现。当业务合约需要操作CRUD接口时,均需要引入该接口合约文件。Table.sol 合约详细接口参考这里

合约编译

根据业务需求设计了合约Asset.sol的存储与接口,给出了完整实现,但是Java程序无法直接调用Solidity合约,需要先将Solidity合约文件编译为Java文件。

控制台提供了编译工具,可以将Asset.sol合约文件存放在console/contracts/solidity目录。利用console目录下提供的sol2java.sh脚本进行编译,操作如下:

1
2
3
4
# 切换到fisco/console/目录
$ cd ~/fisco/console/
# 编译合约,后面指定一个Java的包名参数,可以根据实际项目路径指定包名
$ ./sol2java.sh org.fisco.bcos.asset.contract

运行结果如下:

image-20201127151343874

运行成功之后,将会在console/contracts/sdk目录生成java、abi和bin目录,如下所示。

image-20201127151426979

java目录下生成了org/fisco/bcos/asset/contract/包路径目录,该目录下包含Asset.javaTable.java两个文件,其中Asset.java是Java应用调用Asset.sol合约需要的文件。

Asset.java的主要接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package org.fisco.bcos.asset.contract;

public class Asset extends Contract {
// Asset.sol合约 transfer接口生成
public RemoteCall<TransactionReceipt> transfer(String from_account, String to_account, BigInteger amount);
// Asset.sol合约 register接口生成
public RemoteCall<TransactionReceipt> register(String account, BigInteger asset_value);
// Asset.sol合约 select接口生成
public RemoteCall<Tuple2<BigInteger, BigInteger>> select(String account);

// 加载Asset合约地址,生成Asset对象
public static Asset load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider);

// 部署Assert.sol合约,生成Asset对象
public static RemoteCall<Asset> deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider);
}

其中load与deploy函数用于构造Asset对象,其他接口分别用来调用对应的solidity合约的接口,详细使用在下文会有介绍。

SDK配置

官方提供了一个Java工程项目供开发使用,首先获取Java工程项目:

1
2
3
4
5
# 获取Java工程项目压缩包
$ cd ~
$ curl -#LO https://github.com/FISCO-BCOS/LargeFiles/raw/master/tools/asset-app.tar.gz
# 解压得到Java工程项目asset-app目录
$ tar -zxf asset-app.tar.gz

asset-app项目的目录结构如下:

image-20201127151645443

项目引入Web3SDK

项目的build.gradle文件已引入Web3SDK,不需修改。其引入方法介绍如下:

  • Web3SDK引入了以太坊的solidity编译器相关jar包,因此在build.gradle文件需要添加以太坊的远程仓库:
1
2
3
4
5
6
7
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/groups/public/"
}
maven { url "https://dl.bintray.com/ethereum/maven/" }
mavenCentral()
}
  • 引入Web3SDK jar包

    1
    compile ('org.fisco-bcos:web3sdk:2.5.0')

证书与配置文件

  • 区块链节点证书配置

    拷贝区块链节点对应的SDK证书

    1
    2
    3
    # 拷贝节点证书到项目的资源目录
    $ cd ~
    $ cp fisco/nodes/127.0.0.1/sdk/* asset-app/src/test/resources/
  • applicationContext.xml

    如果搭链时设置的jsonrpc_listen_ip为127.0.0.1或者0.0.0.0,channel_port为20200, 则applicationContext.xml配置不用修改。若区块链节点配置有改动,需要同样修改配置applicationContext.xml,具体请参考SDK使用文档

    在这里搭建国密版本需要改动applicationContext.xml的几个部分如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <bean id="encryptType" class="org.fisco.bcos.web3j.crypto.EncryptType">
    <constructor-arg value="1"/>
    <!-- 0:standard 1:guomi -->
    <!-- 这里将value值改为1 -->
    </bean>


    <property name="caCert" value="ca.crt" />
    <property name="sslCert" value="sdk.crt" />
    <property name="sslKey" value="sdk.key" />


    <!-- 修改为如下: -->
    <property name="caCert" value="gmca.crt" />
    <property name="sslCert" value="gmensdk.crt" />
    <property name="sslKey" value="gmensdk.key" />

运行

运行项目,测试功能是否正常

  • 编译

    1
    2
    3
    4
    # 切换到项目目录
    $ cd ~/asset-app
    # 编译项目
    $ ./gradlew build

    image-20201127152650738

  • 部署Asset.sol合约

    1
    2
    3
    # 进入dist目录
    $ cd dist
    $ bash asset_run.sh deploy

    运行成功截图如下:

    image-20201127152743890

    笔者在执行部署命令时遇到如下错误:

    image-20201127152848482

    出现这个问题是上一步证书与配置文件没修改正确。在更换国密时需格外注意。

  • 注册资产

    1
    2
    3
    bash asset_run.sh register LCX 10000

    bash asset_run.sh register LFH 9999

    image-20201127153151951

  • 查询资产

    1
    2
    3
    bash asset_run.sh query LCX

    bash asset_run.sh query LFH

    image-20201127153238596

  • 资产转移

    1
    bash asset_run.sh transfer LCX LFH  1

    image-20201127153352675

    image-20201127153406362

您的支持是我继续创作的动力