1、创建 ubuntu

使用 vm vare 16 pro
新建虚拟机、
下一步、
稍后安装操作系统,下一步、
Linux,Ubuntu64位,下一步、
虚拟机名称,位置,下一步、
设置最大磁盘大小,将虚拟磁盘存储为单个文件,下一步、
完成、
编辑虚拟机设置、
将ubuntu22 镜像添加进去、
启动,开始安装。

这时候如果想要 xshell 连接
查看 ip 地址

ip a

设置 ssh

sudo apt-get install openssh-server

设置 root 账户的密码

sudo passwd root

切到 root ~

su root
cd ~

安装 vim

apt-get install vim

设置root用户直接登录

vim /etc/ssh/sshd_config

取消文件中 Port 22 的注释、取消文件里PermitRootLogin yes的注释,并设置为 yes

Port 22
PermitRootLogin yes

保存退出,然后重启sshd

systemctl restart sshd

然后 xshell 连接,直接通过 root 账户登录。

2、搭建单群组FISCO BCOS联盟链

官方文档

环境: Ubuntu 22.04 64bit

1、安装 ubuntu 依赖

sudo apt-get install openssh-server

2、创建操作目录, 下载安装脚本

## 创建操作目录
cd ~ && mkdir -p fisco && cd fisco

## 下载脚本
curl -#LO https://github.com/FISCO-BCOS/FISCO-BCOS/releases/download/v2.9.1/build_chain.sh && chmod u+x build_chain.sh

3、搭建单群组4节点联盟链

bash build_chain.sh -l 127.0.0.1:4 -p 30300,20200,8545

4、启动 FISCO BCOS 链

bash nodes/127.0.0.1/start_all.sh

5、检查进程
(1)检查进程是否启动

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

在这里插入图片描述

(2)检查日志输出
查看节点node0链接的节点数:

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

在这里插入图片描述
(3)检查是否在共识

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

在这里插入图片描述

3、配置及使用控制台

1、准备依赖
(1)安装java

sudo apt install -y default-jdk

(2)获取控制台并回到fisco目录

cd ~/fisco && curl -LO https://github.com/FISCO-BCOS/console/releases/download/v2.9.2/download_console.sh && bash download_console.sh

(3)拷贝控制台配置文件

cp -n console/conf/config-example.toml console/conf/config.toml

(4)配置控制台证书

cp -r nodes/127.0.0.1/sdk/* console/conf/

2、启动并使用控制台

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

在这里插入图片描述
用控制台获取信息:

  • 获取控制台版本:
getNodeVersion
  • 获取节点信息
getPeers

3、部署及调用 HelloWorld 合约

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合约已经内置于控制台中,位于控制台目录下contracts/solidity/HelloWorld.sol 。 自己也可以往此目录中添加合约。

部署合约:

deploy HelloWorld

调用 HelloWorld 合约

// 获取当前块高
getBlockNumber
// 调用get接口获取name变量 此处的合约地址是deploy指令返回的地址
call HelloWorld 合约地址 get
// 查看当前块高,块高不变,因为get接口不更改账本状态
getBlockNumber
// 调用set设置name
call HelloWorld 合约地址 set "Hello, MXY"
// 再次查看当前块高,块高增加表示已出块,账本状态已更改
getBlockNumber
// 退出
quit

4、WeBASE-Front搭建

1、配置 java 环境变量

将 jdk-8u351-linux-x64.tar.gz 上传到 ubuntu
解压到 /usr/local 下
改名 jdk1.8
配置 profile 文件

vim /etc/profile
export JAVA_HOME=/usr/local/jdk1.8
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
source /etc/profile

2、下载安装包

cd ~/fisco/&& wget https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/WeBASE/releases/download/v1.5.2/webase-front.zip

3、解压安装包(使用unzip之前要先安装对应的包)

unzip webase-front.zip

4、拷贝证书文件

cp -r nodes/127.0.0.1/sdk/* webase-front/conf/

5、服务启停

cd webase-front

// 启动:
bash start.sh
// 停止:
bash stop.sh
// 检查:
bash status.sh

6、访问
http://主机ip:5002/WeBASE-Front
例如我的是:http://192.168.182.128:5002/WeBASE-Front

在这里插入图片描述

5、WeBASE一键部署

官方文档

​一键部署可以在 同机 快速搭建WeBASE管理台环境,方便用户快速体验WeBASE管理平台。
一键部署会搭建:节点(FISCO-BCOS 2.0+)、管理平台(WeBASE-Web)、节点管理子系统(WeBASE-Node-Manager)、节点前置子系统(WeBASE-Front)、签名服务(WeBASE-Sign)。其中,节点的搭建是可选的,可以通过配置来选择使用已有链或者搭建新链。

所需环境:

在这里插入图片描述

1、安装 python 3.8

sudo apt update
sudo apt install software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt install python3.8

2、安装 pymysql

sudo apt-get install -y python3-pip
sudo pip3 install PyMySQL

3、安装 mysql

apt install mysql-server

4、配置 mysql 账户

sudo vim /etc/mysql/debian.cnf
// 记住这里的用户名和密码
// 使用此处的用户名和密码登录 Mysql
mysql -u debian-sys-maint -p

我是 mysql 8 版本,修改 root 用户 的密码为 123456

use mysql;
update user set authentication_string='' where user='root';
ALTER user 'root'@'localhost' IDENTIFIED BY '123456';

再创建一个新用户 test ,用于后续的 webase

create user 'test'@'localhost' IDENTIFIED BY '123456';
GRANT ALL PRIVILEGES ON *.* TO 'test'@'localhost';
Flush privileges;

5、拉取部署安装包、解压

wget https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/WeBASE/releases/download/v1.5.4/webase-deploy.zip

unzip webase-deploy.zip

cd webase-deploy

6、修改配置

vi common.properties
# WeBASE子系统的最新版本(v1.1.0或以上版本)
webase.web.version=v1.5.4
webase.mgr.version=v1.5.4
webase.sign.version=v1.5.3
webase.front.version=v1.5.4

#####################################################################
## 使用Docker启用Mysql服务,则需要配置以下值

# 1: enable mysql in docker
# 0: mysql run in host, required fill in the configuration of webase-node-mgr and webase-sign
docker.mysql=1

# if [docker.mysql=1], mysql run in host (only works in [installDockerAll])
# run mysql 5.6 by docker
docker.mysql.port=23306
# default user [root]
docker.mysql.password=123456

#####################################################################
## 不使用Docker启动Mysql,则需要配置以下值

# 节点管理子系统mysql数据库配置
mysql.ip=127.0.0.1
mysql.port=3306
mysql.user=test # 改成自己的
mysql.password=123456 # 改成自己的
mysql.database=webasenodemanager

# 签名服务子系统mysql数据库配置
sign.mysql.ip=localhost
sign.mysql.port=3306
sign.mysql.user=test # 改成自己的
sign.mysql.password=123456 # 改成自己的
sign.mysql.database=webasesign



# 节点前置子系统h2数据库名和所属机构
front.h2.name=webasefront
front.org=fisco

# WeBASE管理平台服务端口
web.port=5000
# 启用移动端管理平台 (0: disable, 1: enable)
web.h5.enable=1

# 节点管理子系统服务端口
mgr.port=5001
# 节点前置子系统端口
front.port=5002
# 签名服务子系统端口
sign.port=5004


# 节点监听Ip
node.listenIp=127.0.0.1
# 节点p2p端口
node.p2pPort=30300
# 节点链上链下端口
node.channelPort=20200
# 节点rpc端口
node.rpcPort=8545

# 加密类型 (0: ECDSA算法, 1: 国密算法)
encrypt.type=0
# SSL连接加密类型 (0: ECDSA SSL, 1: 国密SSL)
# 只有国密链才能使用国密SSL
encrypt.sslType=0

# 是否使用已有的链(yes/no)
if.exist.fisco=no  # 如果使用已有的链的话,则下面配置已有链的路径

# 使用已有链时需配置
# 已有链的路径,start_all.sh脚本所在路径
# 路径下要存在sdk目录(sdk目录中包含了SSL所需的证书,即ca.crt、sdk.crt、sdk.key和gm目录(包含国密SSL证书,gmca.crt、gmsdk.crt、gmsdk.key、gmensdk.crt和gmensdk.key)
fisco.dir=/data/app/nodes/127.0.0.1
# 前置所连接节点,在127.0.0.1目录中的节点中的一个
# 节点路径下要存在conf文件夹,conf里存放节点证书(ca.crt、node.crt和node.key)
node.dir=node0

# 搭建新链时需配置
# FISCO-BCOS版本
fisco.version=2.7.2
# 搭建节点个数(默认两个)
node.counts=nodeCounts # 这里先不改,以后节点扩容时再改

7、部署并启动所有服务

python3 deploy.py installAll

注意(这里没采用之前已经部署的链,且端口号被占用了,通过下面指令查看占用端口号的进程并全删了)

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

出现问题1:找不到 openssl
可能是我的 openssl 是 3.0.2 版本。而拉下来的 build_chain.sh 中默认使用 1.0.2 版本。导致错误
解决办法:

vim build_chain.sh 

在第 245 行,手动将 openssl 的版本更改为我们的 3.0.2 版本。

在这里插入图片描述
然后再重新执行 python3 deploy.py installAll 。它会尝试重新拉取 build_chain.sh,会提示是否覆盖 build_chain.sh 注意输入 no 不覆盖!覆盖了我们的更改就没了

启动之后访问WeBASE管理平台: 192.168.182.139:5000
也可访问节点前置:192.168.182.139::5002/WeBASE-Front

出现问题2:报系统错误!登录验证码显示不出。
查看错误日志

vim webase-node-mgr/log/WeBASE-Node-Manager.log 

发现问题是 数据库连接错误问题

在这里插入图片描述
修改 application.yml 文件

vim webase-node-mgr/conf/application.yml

在url 的最后面添加 &&useSSL=false

在这里插入图片描述
然后停止所有服务,再重新启动。访问页面时多刷几下就好了!

8、服务部署后,需要对各服务进行启停操作

# 一键部署
部署并启动所有服务        python3 deploy.py installAll
停止一键部署的所有服务    python3 deploy.py stopAll
启动一键部署的所有服务    python3 deploy.py startAll
# 各子服务启停
启动FISCO-BCOS节点:      python3 deploy.py startNode
停止FISCO-BCOS节点:      python3 deploy.py stopNode
启动WeBASE-Web:          python3 deploy.py startWeb
停止WeBASE-Web:          python3 deploy.py stopWeb
启动WeBASE-Node-Manager: python3 deploy.py startManager
停止WeBASE-Node-Manager: python3 deploy.py stopManager
启动WeBASE-Sign:        python3 deploy.py startSign
停止WeBASE-Sign:        python3 deploy.py stopSign
启动WeBASE-Front:        python3 deploy.py startFront
停止WeBASE-Front:        python3 deploy.py stopFront
# 可视化部署
部署并启动可视化部署的所有服务  python3 deploy.py installWeBASE
停止可视化部署的所有服务  python3 deploy.py stopWeBASE
启动可视化部署的所有服务  python3 deploy.py startWeBASE

以后再启动所有服务时,只需要指令:

python3 deploy.py startAll

9、登录访问

默认账号:admin
默认密码:Abcd1234
登录过后重置密码:Abcd12345(我重置的)

10、附上界面

在这里插入图片描述

6、单群组节点扩容

1、下载扩容脚本
在fisco目录下输入以下命令:

curl -#LO https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/master-2.0/tools/gen_node_cert.sh

2、生成新节点私钥证书
运行gen_node_cert.sh脚本

bash gen_node_cert.sh -c nodes/cert/agency -o nodes/127.0.0.1/node4

3、准备节点配置文件

cd nodes/127.0.0.1
cp node0/config.ini node0/start.sh node0/stop.sh node4/

4、修改配置文件

vim node4/config.ini

在这里插入图片描述

5、拷贝群组文件

cp node0/conf/group.1.genesis node0/conf/group.1.ini node4/conf/

6、启动节点

bash node4/start.sh

7、使用 FISCO BCOS的 console 进行群组扩容
获取节点的id

cat node4/conf/node.nodeid

进入控制台

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

使用console将 node4 作为观察节点加入群组1
getObserverList 查看观察节点列表
addObserver 加入观察节点
在这里插入图片描述

使用console将 node4 作为共识节点加入群组1
getSealerList 查看共识节点列表
addSealer 加入共识节点

在这里插入图片描述
停止、启动测试。

在这里插入图片描述

7、多群组部署

官方文档
星形组网拓扑和并行多组组网拓扑是区块链应用中使用较广泛的两种组网方式。
星形拓扑: 中心机构节点同时属于多个群组,运行多家机构应用,其他每家机构属于不同群组,运行各自应用;

在这里插入图片描述

并行多组: 区块链中每个节点均属于多个群组,可用于多方不同业务的横向扩展,或者同一业务的纵向扩展。

在这里插入图片描述
本章以构建上图所示的单机、四机构、三群组、八节点的星形组网拓扑为例,介绍多群组使用方法。

星形区块链组网如下:

agencyA:在127.0.0.1上有2个节点,同时属于group1、group2、group3;
agencyB:在127.0.0.1上有2个节点,属于group1;
agencyC:在127.0.0.1上有2个节点,属于group2;
agencyD:在127.0.0.1上有2个节点,属于group3。

注意: 实际应用场景中,不建议将多个节点部署在同一台机器,建议根据 机器负载 选择部署节点数目,请参考 硬件配置 星形网络拓扑 中,核心节点(本例中agencyA节点)属于所有群组,负载较高,建议单独部署于性能较好的机器在不同机器操作时,请将生成的对应IP的文件夹拷贝到对应机器启动,建链操作只需要执行一次

1、新建 fiscogroup 文件夹,创建 ipconf 配置文件

vim ipconf

# 编辑如下内容:
# 空格分隔的参数分别表示如下含义:
# ip:num: 物理机IP以及物理机上的节点数目
# agency_name: 机构名称
# group_list: 节点所属的群组列表,不同群组以逗号分隔
# 意思是 在 127.0.0.1 这个ip 2个节点,agencyA 属于 1 2 3 组 agencyB 属于1组 agencyC 属于2组 agencyD 属于3127.0.0.1:2 agencyA 1,2,3
127.0.0.1:2 agencyB 1
127.0.0.1:2 agencyC 2
127.0.0.1:2 agencyD 3

2、获取 build_chain.sh 脚本(直接从 fisco 中复制过来)

cp fisco/build_chain.sh fiscogroup

3、使用build_chain脚本构建星形区块链节点配置文件夹

bash build_chain.sh -f ipconf -p 30300,20200,8545

在这里插入图片描述
一个机构有2个节点,四个机构所以共8个节点,一共三个组,angencyA供三个组所有,每个组4个节点。
node0和node1属于组 1 2 3
node2和node3属于组1
node4和node5属于组2
ndoe6和node7属于组3
4、启动节点

cd ~/fisco/nodes/127.0.0.1
bash start_all.sh

在这里插入图片描述

5、查看节点进程

ps aux | grep fisco-bcos

在这里插入图片描述
6、查看群组共识状态

tail -f node*/log/* | grep "++"

节点正常共识打印 +++ 日志。
在这里插入图片描述
7、配置控制台
直接从 fisco 中 将 console 复制过来

cp -r ~/fisco/console ~/fiscogroup/

进入控制台,拷贝group节点证书到控制台配置目录

cp ~/fiscogroup/nodes/127.0.0.1/sdk/* conf/

获取 node 的 channel_listen_port

grep "channel_listen_port" ~/fiscogroup/nodes/127.0.0.1/node*/config.ini

在这里插入图片描述
拷贝控制台配置

cp ~/fiscogroup/console/conf/config-example.toml ~/fiscogroup/console/conf/config.toml

8、启动控制台,查看组

bash start.sh

发送交易,切换组,测试。各个组之间互不影响。
在这里插入图片描述
查看group1、group2、group3出块情况:

# 三个组中都有node0
cat node0/log/* |grep "g:1.*Report"
cat node0/log/* |grep "g:2.*Report"
cat node0/log/* |grep "g:3.*Report"

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可看到 1 2 组都有新区块产生(部署了HelloWorld合约),3组没有新区块产生。

8、多机部署

1、设置静态ip

vim /etc/netplan/01-network-manager-all.yaml

进入 01-network-manager-all.yaml 文件 做如下编辑

network:
  ethernets:
        ens33:
              addresses: [192.168.182.139/24]
              routes:
              - to: default
                via: 192.168.182.2
              dhcp4: no
              nameservers:
                addresses: [114.114.114.114]
  version: 2
  renderer: NetworkManager

将本机固定为 192.168.182.139
保存后执行:

netplan apply

注意:如果在设置过静态ip 之后无法ping通外网,可能是网关设置错误!可以在vmvare – 编辑 – 虚拟网络编辑器 – VMnet8 – NAT设置 查看

2、在 fiscoubuntu 基础之上再克隆两台虚拟机 fisco2 和 fisco3,并全按照上述步骤设置静态 ip
三台主机 ip 地址分别为
192.168.182.139
192.168.182.140
192.168.182.141

3、拍摄快照
任何一个虚拟机都不可能保证永远不出毛病,不宕机,为了能在他宕机之后快速恢复到之前的状态,我们可以在关键步骤拍摄快照,方便我们快速恢复。
操作:在vm vare右键虚拟机 -> 拍摄快照 编辑名称即可。

在这里插入图片描述
4、ssh服务配置
解决远程登录和ssh的免密登陆功能

(1)设置三台主机的主机名 hostname

vim /etc/hostname

分别设置为 fisco1 fisco2 fisco3
(2)设置ip与主机名的映射,三台主机都要设置

vim /etc/hosts
192.168.182.139 fisco1
192.168.182.140 fisco2
192.168.182.141 fisco3

然后 reboot 重启三台主机
(3)设置免密登陆

ssh-keygen -t rsa
敲三次回车

将本主机生成的密钥 copy 到其余两台主机中,这样就可以不需要密码就切换到另外两台主机了!
主机 fisco1 执行

ssh-copy-id fisco2
输入yes
输入fisco2的密码
ssh-copy-id fisco3
输入yes
输入fisco3的密码

主机 fisco2 执行

ssh-copy-id fisco1
输入yes
输入fisco1的密码
ssh-copy-id fisco3
输入yes
输入fisco3的密码

主机 fisco3 执行

ssh-copy-id fisco1
输入yes
输入fisco1的密码
ssh-copy-id fisco2
输入yes
输入fisco2的密码

然后就可以通过 ssh 主机名 免密码就切换到其他两台主机了。

ssh fisco1
ssh fisco2
ssh fisco3

5、搭建多机9节点区块链网络
创建文件夹 mulmachinefisco
将 fisco 文件夹中的 build_chain.sh 复制进来。

cp ~/fisco/build_chain.sh ~/mulmachinefisco/

将 fisco 文件夹中的 console 复制进来。

cp -r ~/fisco/console ~/mulmachinefisco/

创建区块链网络配置文件,并编辑

vim ipconf

每个主机3个节点,都属于agencyA,属于同一个组1

192.168.182.139:3 agencyA 1
192.168.182.140:3 agencyA 1
192.168.182.141:3 agencyA 1

基于配置文件生成区块链节点配置

bash build_chain.sh -f ipconf -p 30300,20200,8545

在这里插入图片描述
成功生成多机9节点配置,每台机器的区块链节点配置均位于 nodes 文件夹下,如下:

ls nodes/
192.168.182.139  192.168.182.140  192.168.182.141  cert  cert.cnf

拷贝区块链节点配置
生成区块链节点配置后,需要将每个节点配置拷贝到对应机器上,可通过scp命令执行拷贝。
首先分别在 fisco2 和 fisco2 ~目录下 创建分别文件夹 mulmachinefisco

ssh fisco2 "mkdir -p ~/mulmachinefisco"
ssh fisco3 "mkdir -p ~/mulmachinefisco"

然后将 nodes 下面的 192.168.182.140 复制给 fisco2

scp -r /root/mulmachinefisco/nodes/192.168.182.140/ fisco2:/root/mulmachinefisco/192.168.182.140

将 nodes 下面的 192.168.182.141 复制给 fisco3

scp -r /root/mulmachinefisco/nodes/192.168.182.141/ fisco3:/root/mulmachinefisco/192.168.182.141

然后分别启动每个主机的节点。在fisco1 上远程启动 fisco2 和 fisco3

# 启动 fisco1 上的三个节点
bash ~/mulmachinefisco/nodes/192.168.182.139/start_all.sh
# 启动 fisco2 上的三个节点
ssh fisco2 "bash ~/mulmachinefisco/192.168.182.140/start_all.sh"
# 启动 fisco3 上的三个节点
ssh fisco3 "bash ~/mulmachinefisco/192.168.182.141/start_all.sh"

在这里插入图片描述
检查区块链节点。
每台主机都检查进程是否启动成功

ps aux | grep fisco | grep -v grep

每台主机都检查网络连接是否正常

// 对fisco2 和 fisco3,因为他俩 mulmachinefisco 文件夹下就是 主机号,而fisco1 下面是nodes。
tail -f ~/mulmachinefisco/*/node0/log/* |grep -i connected
// 对fiso1 
tail -f ~/mulmachinefisco/nodes/192.168.182.139/node0/log/* |grep -i connected

在这里插入图片描述
可看到每个节点都与除自己之外的8个点相连。

检查区块链共识是否正常

// 对fisco2 和 fisco3 
tail -f ~/mulmachinefisco/*/node0/log/* |grep -i +++

在这里插入图片描述
共识正常!以上检查均正常,说明多机9节点区块链系统部署成功,可通过控制台对其发起交易!

最后再配置一下控制台。
在这里插入图片描述
将 192.168.182.139、192.168.182.140、192.168.182.141 三台主机中任意一个主机下的 sdk 复制到 console 的 conf 下。

cp ~/mulmachinefisco/nodes/192.168.182.139/sdk/* ~/mulmachinefisco/console/conf/

拷贝控制台配置

cp -n ~/mulmachinefisco/console/conf/config-example.toml ~/mulmachinefisco/console/conf/config.toml

然后切到console 目录下,启动控制台

bash start.sh

查看参与共识的节点列表

getSealerList

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9、Java与WeBASE交互(HTTP方式)

一键部署 WeBASE 后,登录 http://192.168.182.139:5000/
用户名:admin
密码:Abcde1234
1、编译、部署 Helloworld 合约,部署用户为新建的 admin 用户

在这里插入图片描述
用户信息:

在这里插入图片描述
部署过的合约:

在这里插入图片描述
首页可看到交易情况和出块情况:

在这里插入图片描述
2、导出 Java 项目(为gradle项目)

在这里插入图片描述
填写项目名称、包名。节点只有一个选上即可。channelIp 127.0.0.1 不用管。选择已经编译好的 Helloworld合约

在这里插入图片描述
导出文件为压缩包 ,解压后得到文件夹, 通过idea file-> open 打开文件(注意不要直接将文件夹拖入idea)
打开项目之后作如下配置:
(1)Project Structure
配置 jdk 1.8
(2)Settings
在这里插入图片描述
(3)将项目 gradle 文件夹下的 wrapper 文件夹下的 gradle-wrapper.jar 右键 添加到 library
(4)右边的 Toggle Offline Mode 选中
在这里插入图片描述
(5)build.gradle 文件中 添加 hutool 依赖

在这里插入图片描述
(6)application.properties 中 system.peers 修改为 192.168.182.139:20200
(7)期间报了一个错误,HelloworldService 中注释掉一行

在这里插入图片描述
然后就编译成功、启动项目成功了。

3、编写与链上合约交互的类与方法
官方文档中给出了很多供调用的接口。
官方文档:接口说明
下面以几个典型的为例。

(1)对合约中方法的调用

接口URL:http://localhost:5002/WeBASE-Front/trans/handle
调用方法:HTTP POST

在这里插入图片描述
必填项必须设置值。

编写工具类 WBASEUtils,用于调用合约中的方法的封装,代码如下。

public class WBASEUtils {

    // 调用合约中方法
    public static Dict request(
            String userAddress,
            String contractName,
            String contractAddress,
            String funcName,
            String ABI,
            List funcParam) {
        JSONArray abiJson = JSONUtil.parseArray(ABI);
        JSONObject data = JSONUtil.createObj();
        data.set("user", userAddress); // 用户地址
        data.set("contractName", contractName); // 合约名称
        data.set("contractAddress", contractAddress); // 合约地址
        data.set("funcName", funcName); // 方法名
        data.set("contractAbi", abiJson); // 合约abi
        data.set("funcParam", funcParam); // 方法参数
        data.set("groupId", "1"); // 所属组
        data.set("useCns", false); // 是否使用cns调用
        data.set("cnsName", ""); // cns名称
        data.set("version", ""); // cns版本
        String dataString = JSONUtil.toJsonStr(data);
        String responseBody = HttpRequest.post("http://192.168.182.139:5002/WeBASE-Front/trans/handle")
                .header(Header.CONTENT_TYPE, "application/json")
                .body(dataString)
                .execute()
                .body();
        Dict resultDict = new Dict();
        resultDict.set("result", responseBody);
        return  resultDict;
    }
}

编写测试方法来测试,调用 Helloworld合约的 get 方法和 setname 方法。

    /**
     *
     * @author liumenghao
     * @return: void
     * 调用 Helloworld 合约的 setname 方法
     */
    @Test
    public void set() {
    	// 需要注意的是,这里的调用账户需要是 webase-front 中的测试用户。
    	// 如果直接用 webase 中的账户,会报错找不到 privatekey 的错误。
        String userAddress = "0xb37e90d75ce67df4b45a63a5aa1103bf48064ad2";
        String funcName = "setname";
        String ABI = com.liu.helloworld.utils.IOUtil.readResourceAsString("abi/Helloworld.abi");
        ArrayList funcParam = new ArrayList();
        funcParam.add("刘梦豪");
        String contractName = "Helloworld";
        String contractAddress = "0x5e57baee74612d2334a3d148f65c66dc67316f2e";
        Dict result = WBASEUtils.request(userAddress, contractName, contractAddress, funcName, ABI, funcParam);
        JSONObject resultBody = JSONUtil.parseObj(result);
        System.out.println(resultBody.toString());
    }
    /**
     *
     * @author liumenghao
     * @return: void
     * 调用 Helloworld 合约的 get 方法。
     */
    @Test
    public void get() {
        String userAddress = "0x314a690952ab92e7c8016a2a54a6022d6f32676d";
        String funcName = "get";
        String ABI = com.liu.helloworld.utils.IOUtil.readResourceAsString("abi/Helloworld.abi");
        String contractName = "Helloworld";
        String contractAddress = "0x5e57baee74612d2334a3d148f65c66dc67316f2e";
        Dict result = WBASEUtils.request(userAddress, contractName, contractAddress, funcName, ABI, null);
        JSONObject resultBody = JSONUtil.parseObj(result);
        System.out.println(resultBody.toString());
    }

(2)查询链上已经存在的用户

    /**
     *
     * @author liumenghao
     * @return: void
     * 查询链上已经存在的用户
     * response: 所有用户信息
     */
    @Test
    public void getAccounts() {
        String responseBoty = HttpRequest.get("http://192.168.182.139:5002/WeBASE-Front/privateKey/localKeyStores")
                .header(Header.CONTENT_TYPE, "application/json")
                .execute()
                .body();
        JSONArray jsonArray = JSONUtil.parseArray(responseBoty);
        jsonArray.forEach(json ->{
            JSONObject jsonObject = JSONUtil.parseObj(json);
            Object userName = jsonObject.get("userName");
            Object address = jsonObject.get("address");
            System.out.println(userName.toString() + " " + address.toString());
        });
    }

(3)获取链上总交易数

    /**
     *
     * @author liumenghao
     * @return: void
     * 获取链上总交易数
     * response: 总交易数、块高、失败交易数
     */
    @Test
    public void getTransNum() {
        String responseBody = HttpRequest.get("http://192.168.182.139:5002/WeBASE-Front/" + 1 + "/web3/transaction-total")
                .header(Header.CONTENT_TYPE, "application/json")
                .execute()
                .body();
        System.out.println(responseBody);
    }

(4)从外部导入私钥(导入过后可在 webase-front 查看,发现新增账户)

/**
     *
     * @author liumenghao
     * @return: void
     * 从外部导入私钥
     */
    @Test
    public void publicAndPrivateKeyGenerate() {
        CryptoSuite cryptoSuite = new CryptoSuite(CryptoType.ECDSA_TYPE);
        CryptoKeyPair cryptoKeyPair = cryptoSuite.createKeyPair();
        // 生成私钥
        String privateKey = cryptoKeyPair.getHexPrivateKey();
        String userName = "lmx";
        JSONObject data = JSONUtil.createObj();
        data.set("privateKey", privateKey);
        data.set("userName", userName);
        String dataString = JSONUtil.toJsonStr(data);
        String responseBody = HttpRequest.get("http://192.168.182.139:5002/WeBASE-Front/privateKey/import?privateKey=" + privateKey + "&userName=" + userName + "")
                .header(Header.CONTENT_TYPE, "application/json")
                .body(dataString)
                .execute()
                .body();
        System.out.println(responseBody);
    }

10、Java与区块链交互(SDK方式)

10.1 不采用 WeBASE 一键部署

上面提到的 Java 与 WeBASE 的交互的方式,是通过 HTTP 请求进行的,调用合约中的方法是通过 HTTP 请求的方式连接到 WeBASE,然后在 WeBASE 中操作。这种方式与直接采用sdk交互相比安全性欠佳。

接下来就说下不通过 WeBASE 时,Java 与区块链直接通过 sdk 来交互。
这里采用 maven 的方式,我是将 WeBASE 生成的 gradle 项目中我需要的文件都直接粘贴到了我自己的maven项目中。
gradle 转 maven 非常简单。只需要
build.gradle 中的配置 添加到 pom.xml 即可
其他的文件需要的直接复制过去。

如果没有用 WeBASE 来一键部署区块链的话,那么我们在生成合约的Java类时,就需要手动生成。以下是操作办法,首先IDEA连接到服务器。
(1)配置 IDEA 直接连接虚拟机服务器

IDEA File -> Tools -> Development -> Configration

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
IDEA File -> Tools -> Development -> Browse Remote Host
此时可在右侧看到连上的虚拟机目录

在这里插入图片描述

在这里插入图片描述
此时就切换到了根目录 root 下。
如下操作可进入到 虚拟机 控制台。

在这里插入图片描述

(2)执行脚本生成智能合约Java类

在 fisco 中 安装了 console控制台。可看到有一个 sol2java.sh 脚本。

在这里插入图片描述

通过如下指令可生成调用智能合约的 Java 类。

bash sol2java.sh -p com.liu.my_project.contracts // 指明在项目中的生成路径

此脚本会将 contracts/solidity 下的所有合约都生成 java 类,生成在 contracts/sdk/java 目录下。

在这里插入图片描述
在 maven 项目中,直接按照此格式依次创建包,并将 java 文件放到包下。
可看到 在sdk文件夹下也生成了 abi 和 bin 目录,直接将这两个目录 复制到 maven 项目的 resources 中。
然后再将 console 下的 conf 目录复制到 resources 中。并将 conf 中的 config-example.toml 文件拿出来,单独放到 resources 下。
将 config-example.toml 中的 127.0.0.1 改为 192.168.182.139
(3)导入 sdk 依赖

<!--    fisco-bcos 联盟链 -->
<dependency>
    <groupId>org.fisco-bcos.java-sdk</groupId>
    <artifactId>fisco-bcos-java-sdk</artifactId>
    <version>2.9.2</version>
</dependency>

然后就可以直接测试 生成的 java 类中的方法了。

10.2 采用 WeBASE 一键部署

在 9 中,也是采用了 WeBASE 一键部署。但上面例子在调用合约方法时用的是 HTTP 请求的方式。其实可以直接用 sdk 的方式。
以 9 中生成的 helloworld 为例,由于其生成的是一个 gradle 项目,我比较喜欢 maven 项目。

下面是生成的 gradle 项目结构。
在这里插入图片描述
我按照其结构,直接复制到我的 maven 项目中。(还有个 utils 下面的 IOUtil 类,用来读取文件如abi、bin为为字符串)

在这里插入图片描述
先说一下配置,从gradle项目中的 application.properties 文件中复制一些内容
在 application.yml 中

在这里插入图片描述

注意合约地址一定要是字符串 ,带 ’ ’
在 pom.xml 中

<!--       fisco-bcos 联盟链 -->
<dependency>
	 <groupId>org.fisco-bcos.java-sdk</groupId>
	 <artifactId>fisco-bcos-java-sdk</artifactId>
	 <version>2.9.2</version>
</dependency>

配置类:SdkBeanConfig

package com.liu.my_project.config.fisco;

import lombok.extern.slf4j.Slf4j;
import org.fisco.bcos.sdk.BcosSDK;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.config.ConfigOption;
import org.fisco.bcos.sdk.config.exceptions.ConfigException;
import org.fisco.bcos.sdk.config.model.ConfigProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Configuration
@Slf4j
public class SdkBeanConfig {

    @Autowired
    private SystemConfig config;

    @Bean // 注意这里将 Client 托管到 Spring 容器,在这里将用户私钥导入了Client中,在Service中调用合约中的方法时,都是此用户调的。
    public Client client() throws Exception {
        String certPaths = this.config.getCertPath();
        String[] possibilities = certPaths.split(",|;");
        for(String certPath: possibilities ) {
            try{
                ConfigProperty property = new ConfigProperty();
                configNetwork(property);
                configCryptoMaterial(property,certPath);

                ConfigOption configOption = new ConfigOption(property);
                Client client = new BcosSDK(configOption).getClient(config.getGroupId());

                BigInteger blockNumber = client.getBlockNumber().getBlockNumber();
                log.info("Chain connect successful. Current block number {}", blockNumber);

                configCryptoKeyPair(client);
                log.info("is Gm:{}, address:{}", client.getCryptoSuite().cryptoTypeConfig == 1, client.getCryptoSuite().getCryptoKeyPair().getAddress());
                return client;
            }
            catch (Exception ex) {
                log.error(ex.getMessage());
                try{
                    Thread.sleep(5000);
                }catch (Exception e) {}
            }
        }
        throw new ConfigException("Failed to connect to peers:" + config.getPeers());
    }

    public void configNetwork(ConfigProperty configProperty) {
        String peerStr = config.getPeers();
        List<String> peers = Arrays.stream(peerStr.split(",")).collect(Collectors.toList());
        Map<String, Object> networkConfig = new HashMap<>();
        networkConfig.put("peers", peers);

        configProperty.setNetwork(networkConfig);
    }

    public void configCryptoMaterial(ConfigProperty configProperty,String certPath) {
        Map<String, Object> cryptoMaterials = new HashMap<>();
        cryptoMaterials.put("certPath", certPath);
        configProperty.setCryptoMaterial(cryptoMaterials);
    }

    public void configCryptoKeyPair(Client client) {
        if (config.getHexPrivateKey() == null || config.getHexPrivateKey().isEmpty()){
            client.getCryptoSuite().setCryptoKeyPair(client.getCryptoSuite().createKeyPair());
            return;
        }
        String privateKey;
        if (!config.getHexPrivateKey().contains(",")) {
            privateKey = config.getHexPrivateKey();
        } else {
            String[] list = config.getHexPrivateKey().split(",");
            privateKey = list[0];
        }
        if (privateKey.startsWith("0x") || privateKey.startsWith("0X")) {
            privateKey = privateKey.substring(2);
            config.setHexPrivateKey(privateKey);
        }
        client.getCryptoSuite().setCryptoKeyPair(client.getCryptoSuite().createKeyPair(privateKey));
    }
}

SystemConfig 类是用于获取到 application.yml 中的设置

@Data
@Configuration
@ConfigurationProperties(
    prefix = "system"
)
public class SystemConfig {
  private String peers;

  private int groupId = 1;

  private String certPath = "conf";

  private String hexPrivateKey;

  @NestedConfigurationProperty // 此注解将 system.contract 下的多个地址 映射到 ContractConfig 中的多个属性
  private ContractConfig contract;
}

ContractConfig 类保存所有合约的地址

@Data
public class ContractConfig {

  private String helloworldAddress;
  private String tokenAddress;

  private String exchangeAddress;
}

针对每一个合约,WeBASE 都帮我们生成了一个 service 类,其中包含合约的所有方法,用来方便与合约进行交互。例如HelloworldService。

package com.liu.my_project.service.fisco;

import com.liu.my_project.bo.HelloworldSetnameInputBO;
import com.liu.my_project.utils.contracts.IOUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.transaction.manager.AssembleTransactionProcessor;
import org.fisco.bcos.sdk.transaction.manager.TransactionProcessorFactory;
import org.fisco.bcos.sdk.transaction.model.dto.CallResponse;
import org.fisco.bcos.sdk.transaction.model.dto.TransactionResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Arrays;
@Service
@NoArgsConstructor
@Data
public class HelloworldService {
  public static final String ABI = IOUtil.readResourceAsString("abi/Helloworld.abi");

  public static final String BINARY = IOUtil.readResourceAsString("bin/Helloworld.bin");


  @Value("${system.contract.helloworldAddress}")
  private String address;

  @Autowired
  private Client client;

  AssembleTransactionProcessor txProcessor;

  @PostConstruct
  public void init() throws Exception {
    this.txProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(this.client, this.client.getCryptoSuite().getCryptoKeyPair());
  }

  public CallResponse get() throws Exception {
    return this.txProcessor.sendCall(this.client.getCryptoSuite().getCryptoKeyPair().getAddress(), this.address, ABI, "get", Arrays.asList());
  }

  public TransactionResponse setname(HelloworldSetnameInputBO input) throws Exception {
    return this.txProcessor.sendTransactionAndGetResponse(this.address, ABI, "setname", input.toArgs());
  }
}

service里面的方法用到的所有参数WeBASE都帮我们生成了BO。
最后写个 HelloWorldController 测试下:

在这里插入图片描述
测试成功。

在这里插入图片描述

11. 修改 WeBASE 一键部署为10个节点

修改 common.properties 文件:
在这里插入图片描述
修改过后重新部署(build_chain.sh 文件不需要重新获取)

python3 deploy.py installAll

部署过后记得停止所有服务、修改 webase-node-mgr 中 application.yml 文件 加上 useSSL=false、再重新启动所有服务。
这里还是有个小bug,重启过后需要一直刷新页面,才有几率会出来验证码。

页面查看:

在这里插入图片描述

查看进程:

在这里插入图片描述

查看端口号占用情况:

在这里插入图片描述
java 在连接 WeBASE 时,所连接的端点就是根据端口号来的。

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐