前言
本篇主要参考官方技术文档,介绍了如何测试网络的准备工作、使用脚本搭建测试网络的细节以及示例应用的运行。
一、准备测试网络
1.1. 概述
Fabric官方提供了一个基础示例项目fabric-samples,项目仓库为hyperledger/fabric-samples。该项目可以部署一个测试网络,专用于引导用户学习Fabric区块链网络的基本特性和操作。
部署这个测试网络需要预先准备三部分内容:
- 示例,即fabric-samples仓库。
注1:包括示例链码等,正常开发不需要这部分内容。
- 平台特定的Hyperledger Fabric CLI工具二进制文件和配置文件(platform-specific Hyperledger Fabric CLI tool binaries and config files)。
注2:详见后文。
- Fabric Docker镜像。
注3:Fabric的节点以Docker容器的形式运行。而镜像(Image)和容器(Container)的关系就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。因此需要先获取所需的镜像。
官方提供了两个脚本bootstrap.sh和install-fabric.sh用于获取上述内容。两个脚本的源代码都位于GitHub上的hyperledger/fabric项目的scripts文件夹中,使用方法和运行效果也相同,但是install-fabric.sh脚本的语法较bootstrap.sh脚本有所改进。
具体来讲,脚本的作用如下:
- 如果当前目录中没有hyperledger/fabric-samples,则从GitHub克隆hyperledger/fabric-samples项目仓库并签出(checkout)指定的版本标签。
- 将指定版本的平台特定的Hyperledger Fabric CLI工具二进制文件和配置文件分别放到本地fabric-samples仓库的根目录下的/bin和/config目录中。
- 从Docker Hub上下载指定版本的Hyperledger Fabric Docker镜像文件到本地Docker注册表中,并将下载的镜像文件标记为"latest"。
1.2. 完成准备工作
1.2.1. 运行install-fabric.sh脚本
为了方便后期管理,在当前登录用户的HOME目录下创建一个空目录并进入该目录:
mkdir hyfa && cd hyfa
在当前目录下新建一个名为install-fabric.sh的文件:
vim install-fabric.sh
打开浏览器,在网址栏输入install-fabric.sh脚本源代码的URL(https://github.com/hyperledger/fabric/blob/main/scripts/install-fabric.sh),将其中的代码复制过来,保存后退出。
赋予install-fabric.sh脚本文件可执行权限:
chmod +x install-fabric.sh
执行install-fabric.sh:
./install-fabric.sh
注1:不建议在执行该脚本时使用root权限,即,不要执行sudo ./install-fabric.sh
。
注2:下载可能需要几分钟,也可能需要十几分钟,取决于当前的网络环境。如果下载太慢,可以换个时间段,无需其它操作。
注3:使用该脚本时如遇到任何错误,再次执行即可。
注4:可以执行./install-fabric.sh -h
打印帮助文档查看如何有选择地安装特定版本或者特定组件。
1.2.2. 文件夹去锁(可选)
执行install-fabric.sh脚本时,如果使用了root权限,脚本运行后,在GUI中会发现fabric-samples文件夹是带锁的,无法移动文件到该文件夹。同时fabric-samples文件夹以及其中的全部文件的属主都是root,当前用户在运行文件夹中的脚本时会因为权限不够而报错。
如果使用chmod指令解锁文件夹,虽然可以去锁,但是会使里面的全部文件夹带锁;如果递归解锁,则文件在终端中都会绿色高亮(因为所有用户都有读/写/执行的权限,Linux系统默认认为这些文件是高风险文件),如此一来不美观。另外这也没有解决运行脚本时可能的权限不足问题。
最好的解决方法是把fabric-samples文件夹的属主更改为当前用户:
sudo chown -R $USER fabric-samples
1.3. install-fabric.sh脚本运行结果
默认情况下,运行install-fabric.sh的结果如下:
- 克隆hyperledger/fabric-samples到当前目录并签出。之后在该文件夹下可以用git branch命令查看当前的分支。
- 拉取Hyperledger Fabric二进制文件:实际上是下载了hyperledger-fabric-linux-amd64-x.x.x.tar.gz和hyperledger-fabric-ca-linux-amd64-x.x.x.tar.gz这两个压缩文件,然后解压到指定目录,然后删除压缩文件。
- 拉取Hyperledger Fabric Docker镜像文件:具体来讲是拉取fabric-peer、fabric-orderer、fabric-ccenv、fabric-tools、fabric-baseos和fabric-ca这6个镜像文件。
1.4. 什么是二进制文件?
如前一节所示,拉取二进制文件其实是先下载了两个.tar.gz格式的压缩文件。
.tar.gz格式的压缩文件是源代码的安装包,解压时会编译一些所谓的二进制文件,即可执行文件(可以理解成一个工具)。将该格式的压缩文件放到Windows的系统中,用压缩软件打开,可以看到最终的可执行文件都有哪些。
如图所示,fabric-samples/bin目录里的即为二进制文件,而fabric-samples/config目录里的则是配置文件。查看GitHub上的fabric-samples仓库,可以发现没有这两个目录及其中的内容,证明这是额外实现的。
hyperledger-fabric-ca-linux-amd64-x.x.x.tar.gz解压后是bin文件夹下的fabric-ca-client和fabric-ca-server这两个二进制文件。
hyperledger-fabric-linux-amd64-x.x.x.tar.gz解压后则是config文件夹下的.yaml配置文件和bin文件夹下的其他二进制文件。
1.5. 备用的准备方法(可选)
参考运行结果,也可以手动实现脚本完成的工作。
- 克隆fabric-samples仓库并签出:
git clone https://gitee.com/hyperledger/fabric-samples.git
cd fabric-samples
git checkout -q v2.4.7
- 下载压缩文件并解压:
wget https://github.com/hyperledger/fabric/releases/download/v2.4.7/hyperledger-fabric-linux-amd64-2.4.7.tar.gz
tar -zxvf hyperledger-fabric-linux-amd64-2.4.7.tar.gz
wget https://github.com/hyperledger/fabric-ca/releases/download/v1.5.5/hyperledger-fabric-ca-linux-amd64-1.5.5.tar.gz
tar -zxvf hyperledger-fabric-ca-linux-amd64-1.5.5.tar.gz
注:如果无法在虚机上下载,则直接从https://github.com/hyperledger/fabric/tags上下载对应版本的压缩文件,再复制到虚机的fabric-samples文件夹下即可。
3. 使用docker pull命令从Docker Hub拉取指定的Docker镜像文件并将已下载的镜像标记为最新:
docker pull hyperledger/fabric-peer:2.4.7 \
&& docker pull hyperledger/fabric-orderer:2.4.7 \
&& docker pull hyperledger/fabric-ca:1.5.5 \
&& docker pull hyperledger/fabric-tools:2.4.7 \
&& docker pull hyperledger/fabric-ccenv:2.4.7 \
&& docker pull hyperledger/fabric-baseos:2.4.7
docker tag hyperledger/fabric-peer:2.4.7 hyperledger/fabric-peer \
&& docker tag hyperledger/fabric-orderer:2.4.7 hyperledger/fabric-orderer \
&& docker tag hyperledger/fabric-ca:1.5.5 hyperledger/fabric-ca \
&& docker tag hyperledger/fabric-tools:2.4.7 hyperledger/fabric-tools \
&& docker tag hyperledger/fabric-ccenv:2.4.7 hyperledger/fabric-ccenv \
&& docker tag hyperledger/fabric-baseos:2.4.7 hyperledger/fabric-baseos
二、使用测试网络
2.1. 概述
Fabric官方提供了提供了一个脚本network.sh用于启动测试网络。进入测试网络的目录,可以找到该脚本:
cd ~/hyfa/fabric-samples/test-network
2.2. 基本用法
2.2.1. 启动网络
启动一个默认名称为“fabric_test”的网络:
./network.sh up
启动网络后可以docker ps查看容器。
2.2.2. 创建通道
创建一个默认名称为“mychannel”的通道:
./network.sh createChannel
2.2.3. 在通道部署链码
部署Go版本的asset-transfer-basic链码:
./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go
链码部署后可以再次查看容器,将会看到多出了两个实例。
2.2.4. 关停网络
./network.sh down
该命令将停止并删除节点和链码容器,删除组织加密材料,并从Docker Registry移除链码镜像。该命令还删除之前运行的通道项目和docker卷。
注:如果未正常关停网络,下次启动测试网络时,应先执行该命令。
2.3. 报错
2.3.1. 跟文件权限相关的报错
错误描述:permission denied
报错原因:当前用户对fabric-samples文件夹的权限不够。
解决方法:更改fabric-samples文件夹的权限(见1.2.2节);或者在运行脚本前先获得root shell权限(sudo bash
)。
2.3.2. 跟Docker相关的报错
错误1
错误描述:Got permission denied while trying to connect to the Docker daemon socket
报错原因:docker守护进程启动的时候,会默认赋予名字为docker的用户组读写Unix socket的权限,如果在安装docker时没有将当前用户添加到docker组,则会出现如下问题。
解决方法:创建docker用户组,并将当前用户加入到docker用户组中,那么当前用户就有权限访问Unix socket,进而也就可以执行docker相关命令。
sudo groupadd docker
sudo gpasswd -a $USER docker
newgrp docker
错误2
错误描述:Version in “./compose/compose-test-net.yaml” is unsupported
报错原因:docker-compose在使用.yaml文件时,文件里的版本号与docker和docker-compose的版本有要求,则需要更新的版本。
解决方法:更新docker和docker-compose,或者修改.yaml文件里的版本号。
错误3
错误描述:UnixHTTPConnectionPool(host=‘localhost’, port=None): Read timed out. (read timeout=60)
报错原因:docker-compose启动容器花费的时间超过默认设定的60S,见于性能特别差劲的系统。
解决方法:修改超时默认设置。
vim /etc/profile
export DOCKER_CLIENT_TIMEOUT=500
export COMPOSE_HTTP_TIMEOUT=500
source /etc/profile
2.3.3. 跟Golang相关的报错
错误1
错误描述:go: 未找到命令
报错原因:没有正确配置配置Golang环境。
解决方法:正确配置Golang。
错误2
错误描述:Error: failed to normalize chaincode path: ‘go list‘ failed …
报错原因:下载依赖的连接被拒绝。
解决方法:更换Golang代理。
2.4. 脚本使用语法
network.sh脚本的语法是:network.sh <mode> [flag]
其中,<…>代表必选项,[…]代表可选项。
mode有5种,说明如下。
- up:启动Fabric网络,但是不创建通道。
- createChannel:在网络启动后创建一个通道。
- up createChannel:启动Fabric网络并创建通道。
- deployCC:部署链码到通道。
- down:关闭Fabric网络。
flag根据mode的选择有不同,并且可以多选。
当mode为up或createChannel时,说明如下。
- -ca:使用CA生成网络加密材料。
- -c:定义要创建的通道的名称(默认为“mychannel”)。
- -s:选择Peer节点的状态数据库的类型,LevelDB(默认)或CouchDB。
- -r:客户端最大重试请求次数(默认为5次)。
- -d:客户端最大请求时延(默认为3秒)。
- -verbose:Verbose模式,用于输出详细的日志信息。
注:在Verbose模式下,脚本会在特定的地方使用env命令和grep命令在终端中列出相关的环境变量及其赋值,可以参见test-netork/scripts目录下的envVar.sh脚本。
当mode为deployCC时,部分说明如下。
- -c:部署链码的通道名。
- -ccn:链码名。
- -ccp:链码的文件路径。
- -ccl:链码的编程语言。
2.5. network.sh脚本代码详解
2.5.1. 脚本的功能说明
network.sh脚本会根据启动脚本时输入的子命令和参数运行对应的函数。
2.5.2. 脚本启动网络详解
2.5.2.1. 总体说明
如果使用up子命令,network.sh脚本会运行networkUp()函数。
该函数的作用如下:
-
调用checkPrereqs()函数。该函数会进行一些检查,包括二进制文件和配置文件是否存在、二进制文件跟镜像的版本是否匹配,等等。
-
调用createOrgs()函数。该函数为Peer节点和排序服务创建所有部署和操作网络所需要的加密材料并调用organisations文件夹中的ccp-generate.sh脚本为Org1和Org2生成CCP文件。
-
使用docker-compose启动节点的容器。
2.5.2.2. 加密材料的生成
Option 1:使用cryptogen工具
默认情况下,createOrgs()函数使用bin文件夹中的cryptogen工具的generate命令分别为Org1、Org2和Orderer Org创建证书和密钥,配置文件在organizations/cryptogen文件夹中。
Option 2:使用Fabric CA组件
如果带了CA标志,createOrgs()函数使用docker-compose和配置文件compose/compose-ca.yaml及compose/docker/docker-compose-ca.yaml来启动三个Fabric CA服务器的容器。
容器的镜像为fabric-ca,配置文件位于organizations/fabric-ca文件夹中。
这些容器在启动时会执行命令fabric-ca-server start -b admin:adminpw -d
并使用各自的配置文件生成CA证书、秘钥和数据库等内容,并和配置文件存放在一起。
createOrgs()函数继续调用位于organizations/fabric-ca文件夹的registerEnroll.sh脚本中的函数,使用Fabric CA客户端和刚刚生成的内容为Org1、Org2和Orderer Org创建证书和密钥。
2.5.2.3. docker-compose启动容器
Option 1:使用LevelDB
默认情况下,docker-compose使用配置文件compose/compose-test-net.yaml以及compose/docker/docker-compose-test-net.yaml启动容器peer0.org1.example.com、peer0.org2.example.com、orderer.example.com及cli。其中,peer0.org1.example.com和peer0.org2.example.com分别是Org1和Org2的peer节点,orderer.example.com是排序节点,cli是测试网络的维护节点。
Option 2:使用CouchDB
如果在启动网络时,数据库选用couchdb,docker-compose还会使用配置文件compose/compose-couch.yaml以及compose/docker/docker-compose-couch.yaml启动容器couchdb0和couchdb1分别作为Org1和Org2的的peer节点的数据库。
2.5.3. 脚本创建通道详解
如果使用createChannel子命令,network.sh脚本会运行createChannel()函数。该函数又会根据提供的通道名称调用scripts/createChannel.sh脚本。
createChannel.sh脚本首先运行creatChannelGenesisBlock()函数,使用configtxgen工具基于配置文件configtx/configtx.yaml中的TwoOrgsApplicationGenesis配置创建通道创世块。
createChannel.sh脚本然后运行createChannel()函数,使用osnadmin工具创建通道。
createChannel.sh脚本接着运行joinChannel()函数,使用Peer工具将Peer节点peer0.org1.example.com和peer0.org2.example.com连接到通道。
createChannel.sh脚本最后运行setAnchorPeer()函数,该函数直接调用scripts文件夹中的setAnchorPeer.sh脚本使这两个Peer节点设为锚节点。
2.5.4. 脚本部署链码详解
如果使用deployCC子命令,network.sh脚本会运行deployCC()函数。
该函数直接调用Scripts文件夹下的deployCC.sh脚本部署链码。
deployCC.sh脚本首先会安装链码依赖:
然后调用packageChaincode()方法,该方法使用peer lifecycle chaincode package命令创建链码包:
接着调用installChaincode()方法分别在组织1和组织2的peer节点上安装链码包,调用queryInstalled()方法查询安装结果。
接着调用approveForMyOrg()方法为组织1批准链码定义,并调用checkCommitReadiness()方法检查链码定义是否可以提交,期望结果是组织1已经批准而组织2还没有;再一次调用approveForMyOrg()方法为组织2批准链码定义,此时再调用checkCommitReadiness()方法检查链码定义是否可以提交,期望结果变为组织1和组织2都已经批准。
最后调用commitChaincodeDefinition()方法提交链码定义到通道,并调用querCommitted()方法查询提交是否成功。
可选的,可以额外调用chaincodeInvokeInit()方法初始化账本,但这要求链码中实现了InitLedger()方法。
上述这些方法的定义见Scripts文件夹下的ccutils.sh脚本。
2.6. 脚本运行结果说明
2.6.1. 加密材料
使用up子命令时,network.sh脚本运行networkUp()函数。networkUp()函数则会调用createOrgs()函数使用cryptogen工具或者Fabric CA组件为Peer节点和排序服务创建加密材料。
两种方式均会在organisations文件夹内创建如下两个子文件夹:
ordererOrganizations:保存负责管理排序节点的组织的相关资源。
peerOrganizations:保存负责管理Peer节点的组织的相关资源。
两个子文件夹下会以组织的域名创建文件夹,保存该组织的资源。管理排序节点的组织的域名下包含如下文件夹:
- ca:保存负责管理排序节点的CA证书和私钥。
- tlsca:保存排序节点通信时用到的证书和私钥。
- user:保存排序节点管理员用户的证书和私钥。
- msp:保存上面各种类型的所有证书和私钥,因为在一些配置文件中存在直接引用MSP文件夹的情况。
- orderers:保存各个排序节点的CA证书和私钥。
管理Peer节点的组织的域名下类似。
2.6.2. CCP文件
createOrgs()函数还会调用ccp-generate.sh脚本,该脚本使用同目录下的两个模板文件ccp-template.json和ccp-template.yaml,分别为Org1和Org2生成CCP文件,即organisations/peerOrganisations/org1.example.com下的connection-org1.json和connection-org1.yaml,以及organisations/peerOrganisations/org2.example.com下的connection-org2.json和connection-org2.yaml。
这些CCP文件供APP使用,使得APP能够连接到测试网络中的Peer节点。注释掉这一部分内容不会对测试网络的启动、创建通道和部署链码产生影响。
举例来说,可以在项目fabric-samples/asset-transfer-basic/application-java的App.java文件中找到对connection-org1.yaml的使用。
注:部分APP(例如fabric-samples/asset-transfer-basic/application-gateway-java项目)同样无需这些CCP文件。
2.7. 使用peer CLI测试链码
在测试网络部署链码后,可以使用peer CLI从Fabric网络内部来测试已部署的智能合约。peer CLI是网络启动时加入网络的一个容器,作为网络的维护节点。
首先设置环境变量:
export PATH=$PATH:${PWD}/../bin
export FABRIC_CFG_PATH=${PWD}/../config/
作为Org1操作peer CLI:
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
初始化账本:
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"InitLedger","Args":[]}'
查询账本:
peer chaincode query -C mychannel -n basic -c '{"Args":["GetAllAssets"]}'
修改账本:
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"TransferAsset","Args":["asset6","Christopher"]}'
设置环境变量,作为Org2操作peer CLI:
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
查询账本:
peer chaincode query -C mychannel -n basic -c '{"Args":["ReadAsset","asset6"]}'
三、运行示例应用
说明:示例应用演示了如何使用Fabric提供的API来从外部调用已部署的智能合约。尽管名为应用,但是它并不是一个严格意义上能够提供外部接入和人机交互的客户端。如果要开发一个完整的APP,还需要引入其它技术栈,比如SpringBoot。
asset-transfer-basic示例的Java版本应用demo的使用方法如下。
打开第一个终端,在test-network文件夹中启动网络,建立通道,部署asset-transfer-basic示例的链码:
./network.sh up -ca
./network.sh createChannel
./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-java -ccl java
完成后,打开第二个终端,进入asset-transfer-basic/application-gateway-java文件夹中,运行项目中的启动脚本:
./gradlew run
之后应用demo就会运行,并在当前终端中输出一些内容。
当想要结束时,在test-network文件夹下关闭网络即可:
./network.sh down
注意,这里启动网络时要使用CA标志,这是因为cryptogen工具和Fabric CA为测试网络生成的加密材料并不完全一样,而示例应用中使用的路径是基于Fabric CA的。