使用fabric-sdk-go搭建Hyperledger Fabric测试环境
最近在研究一些HyperLedger Fabric平台的相关内容,需要搭建出一个Fabric的运行环境进行测试,在此对搭建的过程进行一个记录与分享,如有不够完善或者做得不对的地方欢迎评论区留言讨论。目前的搭建计划是将利用Go版本的Fabric SDK,从最简单的单Orderer单通道单组织逐步扩充,最终实现多Orderer多通道多组织的Fabric网络环境。
基础环境
1. 安装Docker和Docker Compose
安装Docker
如果原先系统中存在docker,需要先进行卸载
1 | sudo apt-get remove docker docker-engine docker.io |
卸载完成后再进行安装,在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,Ubuntu 系统上可以使用这套脚本安装,另外可以通过 --mirror
选项使用国内源进行安装:
若安装测试版的 Docker, 脚本url为 test.docker.com
1 | curl -fsSL get.docker.com -o get-docker.sh |
启动docker:
1 | sudo systemctl enable docker |
允许非root用户使用docker需要将使用docker的用户添加到docker用户组:
建立docker组:
1 | sudo groupadd docker |
将当前用户加入docker组:
1 | sudo usermod -aG docker $USER |
重启Ubuntu系统后生效
安装Docker Compose
从 官方 GitHub Release 处直接下载编译好的二进制文件
1 | sudo curl -L https://github.com/docker/compose/releases/download/v2.7.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose |
测试
使用命令输出版本信息,能正常输出则说明安装成功
1 | docker version |
2. 安装Go语言环境
下载Go软件包并解压到/usr/local
目录,下载前请访问Go官方下载页面 并检查是否有可用的新版本:
1 | wget -c https://go.dev/dl/go1.18.4.linux-amd64.tar.gz -O - | sudo tar -xz -C /usr/local |
通过将 Go 目录的位置添加到$PATH
环境变量中:
1 | vim ~/.profile |
验证安装:
1 | go version |
Fabric源码、Docker以及样例安装
文件下载
添加工作目录:
1 | mkdir -p $HOME/go/src |
获取安装脚本:
1 | curl -sSLO https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh && chmod +x install-fabric.sh |
指定要下载的组件,需要在执行脚本时添加以下一个或多个参数。每个参数都可以缩短为它的第一个字母。
docker
使用 Docker 下载 Fabric 容器镜像podman
使用 podman 下载 Fabric Container Imagesbinary
下载 Fabric 二进制文件samples
将 fabric-samples github repo 克隆到当前目录
拉取最新版本的 Docker 容器并克隆示例存储库和二进制文件:
1 | ./install-fabric.sh d s b |
使用Fabric测试网络
进入到测试网络目录:
1 | cd fabric-samples/test-network |
启动和停止测试网络:
1 | ./network.sh up |
测试过程出现报错的话需要修改host文件对example.com
域名进行地址映射,在/etc/hosts
文件中添加以下条目:
1 | 127.0.0.1 orderer.example.com |
fabric-go-sdk(1 orderer, 1 org, 2 peers)
跟着sxguan的搭建过程创建Fabric环境的话会出现很多问题,于是直接使用大佬在github上的项目进行修改
修改配置文件
使用如下命令将项目文件拉到本地$GOPATH/src
目录中
1 | git clone https://github.com/sxguan/fabric-go-sdk.git |
下载完后需要对config.yaml
和main.go
中的路径配置进行修改(改到自己实际的项目路径)
将这两个文件中的/root/go/src
全局替换为/home/baymrx/go/src
,保存
至此运行环境就没什么问题了
运行项目
在项目根目录$GOPATH/src/fabric-go-sdk
启动Docker:
1 | cd fixtures && docker-compose up -d && cd .. |
如果前面使用Fabric脚本完整安装过Docker环境,建议将fixtures/docker-compose.yaml
文件中的镜像版本设置为latest
,避免镜像的重复拉取
比如,将下面的镜像配置
1 | image: hyperledger/fabric-orderer:2.2 |
分别替换为
1 | image: hyperledger/fabric-orderer:latest |
使用go build
命令编译项目文件,运行完成后会在项目根目录中生成fabric-go-sdk
文件
./fabric-go-sdk
执行文件就可以将代码顺利跑到最后
运行结束后使用下面的命令结束并清理Docker环境
cd fixtures && docker-compose down && docker volume prune && cd …
cd fixtures && docker-compose down -v && cd ..
fabric-go-sdk(1 orderer, 2 orgs, 2 peers)
2个组织的fabric网络的搭建,在之前1个组织的项目中对相关配置文件照葫芦画瓢即可,部分过程会参考sxguan的搭建过程
配置文件中相关端口分配情况:
Peer0.Org1:70xx
Peer1.Org1:80xx
Peer0.Org2:90xx
Peer1.Org2:100xx
修改configtx.yaml
这个文件包含了整个Fabric网络的定义,定义了网络组件的拓扑结构和每个网络实体加密证书的存储位置,一般用来配置系统通道初试区块文件、应用通道配置文件、锚节点配置文件等
1、在Organization条目下复制一份Org1的配置,并将其中的org1替换为org2
1 | - |
2、在Application下的Organization中添加Org2
1 | Application: |
3、在Profiles条目中相对应位置添加Org2
1 | Profiles: |
修改crypto-config.yaml
crypto-config.yaml是MSP和TLS相关文件的依赖配置文件
在PeerOrgs中复制一份并更改为Org2
1 | - Name: Org2 |
修改docker-compose.yaml
1、volumes中添加Org2 的peer节点
1 | peer0.org2.example.com: |
2、services中添加Org2的peer节点和ca节点
根据端口分配情况首先将Org1中的90xx端口改为80xx,
为方便后续管理,将所有节点(在同一主机的Docker环境下这样后面会报错)environment
参数中变量的端口全部改为70xx,最后在ports
再进行映射
直接复制Org1节点的配置并将Org1替换为Org2(替换时注意区分大小写),然后修改对应端口即可
3、在services中为Org2添加两个couchDB数据库
1 | couchdb2: |
附上整个docker-compose.yaml文件
重新生成证书文件和通道文件
为避免后续调试的重复操作,将生成证书和通道文件的命令全部放到一个shell脚本中(vim create.sh
):
执行下面的命令需要fabric-samples文件夹与fabric-go-sdk项目文件夹在同一父目录下,并且当前目录为
fixtures
1 | rm -rf crypto-config channel-artifacts && mkdir crypto-config |
执行脚本./create.sh
重新生成证书和通道文件,回显中没有报错信息则说明生成成功
修改config.yaml
1、在channels-mychannel-peers中添加org2
1 | peer0.org2.example.com: |
2、在organizations中添加org2
1 | org2: |
3、在peers中添加org2
注意修改peer1.org1的端口为8051
1 | peer0.org2.example.com: |
4、在entityMatchers-peer中添加org2
1 | - pattern: (\w+).org2.example.com:(\d+) |
修改main.go
在main()的orgs变量中添加Org2
1 | orgs := []*sdkInit.OrgInfo{ |
启动Docker并运行代码
1 | cd fixtures && docker-compose up -d && cd .. |
报错了o(╯□╰)o
>> 加入通道…
>> Create channel and join error: Org1 peers failed to JoinChannel: join channel failed: Multiple errors occurred: - SendProposal failed: Transaction processing for endorser [peer1.org1.example.com:8051]: Endorser Client Status Code: (2) CONNECTION_FAILED. Description: dialing connection on target [peer1.org1.example.com:8051]: connection is in TRANSIENT_FAILURE - SendProposal failed: Transaction processing for endorser [peer0.org1.example.com:7051]: Endorser Client Status Code: (2) CONNECTION_FAILED. Description: dialing connection on target [peer0.org1.example.com:7051]: connection is in TRANSIENT_FAILURE
执行docker ps -a
查看容器的运行状态,其中所有的peer节点状态都为Exited (2)
那么找到其中一个peer节点容器的ID(我这里找的是peer0.org1.example.com,对应ID为39c5829f2a4f),查看其日志docker logs 39c
日志中显示couchDB报错:
2022-08-01 06:27:51.842 UTC 0046 DEBU [couchdb] verifyCouchConfig -> Exiting VerifyCouchConfig()
panic: Error in instantiating ledger provider: Get “http://couchdb0:7984/”: dial tcp 172.21.0.2:7984: connect: connection refused
http error calling couchdb
根据报错信息发现原因是找不到couchdb0:7984
这个地址,一番检索后发现是因为我之前在修改docker-compose.yaml时,将CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS
所对应的端口都改为了couchDB容器的外部端口,而这种方式搭建的Docker环境由于networks参数的存在,会将所有的容器都放到同一个网络中,访问时使用容器名即可。所以这里的解决方法是将所有的CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS
的端口都改为5984
那么调整完成之后,重启环境再来运行一遍
Oops!又报错了,这次换了个报错信息
>> 组织认可智能合约定义…
>>> chaincode approved by Org1 peers:
peer0.org1.example.com:7051
peer1.org1.example.com:8051
>>> chaincode approved by Org2 peers:
peer0.org2.example.com:9051
peer1.org2.example.com:10051
[fabsdk/fab] 2022/08/01 07:22:35 UTC - dispatcher.(*Dispatcher).HandleConnectEvent -> WARN error creating connection: could not connect to peer1.org2.example.com:7051: dialing connection on target [peer1.org2.example.com:7051]: connection is in TRANSIENT_FAILURE
>> create chaincode lifecycle error: %v queryApprovedCC error: Org Org2 Peer peer0.org2.example.com:9051 NewInvoker error: Test status Code: (12) UNKNOWN. Description: LifecycleQueryApprovedCC returned error: querying for installed chaincode failed: Transaction processing for endorser [peer0.org2.example.com:9051]: Chaincode status Code: (500) UNKNOWN. Description: failed to invoke backing implementation of ‘QueryApprovedChaincodeDefinition’: could not fetch approved chaincode definition (name: ‘simplecc’, sequence: ‘1’) on channel ‘mychannel’
从报错信息来看是由于连不上peer节点造成的,由于之前将所有节点environment
参数中变量的端口全部改为70xx,只在端口参数处进行映射,参考上一个报错,一部分网络连接走Docker内网络一部分走Docker外网络,造成端口不一致。**修改方案:**修改docker-compose.yaml文件,将environment参数下的所有端口都改为对应节点所分配的端口(而不是统一的70xx)
修改完后,再次重启环境运行fabric-go-sdk,这次可以完美运行~
总结一下,遇到类似上面两种报错信息,多检查一下docker-compose.yaml这类配置文件是否正确,特别是端口号、组织号这些
fabric-go-sdk(1 orderer, 3 orgs, 2 peers)
修改配置文件
与上个章节2个组织的配置修改过程基本相同,其他不同的地方单独记录
1、configtx.yaml
中的Application-Policies的LifecycleEndorsement和Endorsement需要将Org3添加进去
1 | LifecycleEndorsement: |
2、create.sh
中追加一行Org3的命令
1 | ../../fabric-samples/bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org3MSPanchors.tx -channelID mychannel -asOrg Org3MSP |
这里的
TwoOrgsChannel
是否修改为Three并不重要,只要它与configtx.yaml
中Profiles指定的参数相对应即可
运行Fabric
1 | cd fixtures && docker-compose up -d && cd .. |
代码可以正常跑完,没有问题
Fabcar on fabric-go-sdk(1 orderer, 3 orgs, 2 peers)
组织和peer节点的创建都没有问题的话,换个链码再试一下,这里使用Fabric官方提供的Fabcar链码
chaincode更改
将fabric-samples/chaincode/fabcar/go/
目录下的所有文件拷贝到项目目录下的chaincode
文件夹中,并清除文件夹中之前的所有文件
1 | rm -rf ./chaincode/* && cp ../fabric-samples/chaincode/fabcar/go/* ./chaincode |
在chaincode文件夹中生成链码的依赖文件
1 | cd chaincode && go mod vendor |
由于链码的初始化需要从Init()
函数方法开始,但直接拷贝过来的fabcar.go
中没有这一方法,这里曲线救国,直接将链码中的InitLedger()
方法改为Init()
如果链码中没有Init方法,可能发生如下错误:
>> 调用智能合约初始化方法…
>> create chaincode lifecycle error: %v initCC error: Failed to init: Multiple errors occurred: - Transaction processing for endorser [peer0.org3.example.com:11051]: Chaincode status Code: (500) UNKNOWN. Description: error in simulation: transaction returned with failure: Function init not found in contract SmartContract - Transaction processing for endorser [peer0.org2.example.com:9051]: Chaincode status Code: (500) UNKNOWN. Description: error in simulation: transaction returned with failure: Function init not found in contract SmartContract - Transaction processing for endorser [peer1.org1.example.com:8051]: Chaincode status Code: (500) UNKNOWN. Description: error in simulation: transaction returned with failure: Function init not found in contract SmartContract
main.go更改
修改后的文件可在此下载
找到文件fabric-samples/fabcar/go/fabcar.go
,将此文件内容与原先项目中的main.go
进行合并,在原文件设置链码状态完成
之后,将fabcar.go
中的功能性代码添加进去,并修改文件依赖,除此之外,链码的名称也要进行修改
1 | import ( |
其中还有三个路径变量ccpPath
、credPath
和certPath
需要修改,否则可能会报如下错误:
Failed to populate wallet contents: open …/…/test-network/organizations/peerOrganizations/org1.example.com/users/[email protected]/msp/signcerts/cert.pem: no such file or directory
三个路径变量的内容分别为:
1 | ccpPath := filepath.Join( |
ccpPath
中的connection-org1.yaml
在之前的配置操作中并没有生成,需要使用脚本手动添加
添加过程如下:
1、将ccp模板文件拷贝到项目目录中
1 | cp ../fabric-samples/test-network/organizations/ccp-template.yaml ./fixtures |
2、将ccp脚本文件拷贝到项目目录中(或者直接进入第3步创建ccp-generate.sh
文件并授予可执行权限)
1 | cp ../fabric-samples/test-network/organizations/ccp-generate.sh ./fixtures |
3、修改脚本文件,文件内容如下
1 |
|
4、进入到fixtures
目录执行脚本
1 | ./ccp-generate.sh |
对go文件编译运行
在项目根目录中执行
1 | go mod init |
在执行go build
构建可执行文件时,可能会报错
# github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/discovery/client
…/…/pkg/mod/github.com/hyperledger/[email protected]/internal/github.com/hyperledger/fabric/discovery/client/api.go:47:38: undefined: discovery.ChaincodeCall
…/…/pkg/mod/github.com/hyperledger/[email protected]/internal/github.com/hyperledger/fabric/discovery/client/client.go:83:63: undefined: discovery.ChaincodeInterest
…/…/pkg/mod/github.com/hyperledger/[email protected]/internal/github.com/hyperledger/fabric/discovery/client/client.go:120:65: undefined: discovery.ChaincodeCall
…/…/pkg/mod/github.com/hyperledger/[email protected]/internal/github.com/hyperledger/fabric/discovery/client/client.go:124:23: undefined: discovery.ChaincodeInterest
…/…/pkg/mod/github.com/hyperledger/[email protected]/internal/github.com/hyperledger/fabric/discovery/client/client.go:229:105: undefined: discovery.ChaincodeCall
…/…/pkg/mod/github.com/hyperledger/[email protected]/internal/github.com/hyperledger/fabric/discovery/client/client.go:247:64: undefined: discovery.ChaincodeCall
…/…/pkg/mod/github.com/hyperledger/[email protected]/internal/github.com/hyperledger/fabric/discovery/client/client.go:604:48: undefined: discovery.ChaincodeInterest
…/…/pkg/mod/github.com/hyperledger/[email protected]/internal/github.com/hyperledger/fabric/discovery/client/client.go:620:35: undefined: discovery.ChaincodeCall
这种问题是由于依赖fabric-protos-go
在最新版本中删掉了ChaincodeCall和ChaincodeInterest,将go.mod
中的fabric-protos-go
改为较早版本即可解决(我这里使用的是v0.0.0-20210318103044-13fdee960194,改为此版本或更早版本就行)
1 | require ( |
修改完保存后,重新执行
1 | go mod tidy |
如果在运行的时候报如下错误:
>> 设置链码状态完成
[fabsdk/fab] 2022/08/07 11:53:33 UTC - peer.(*peerEndorser).sendProposal -> ERRO process proposal failed [rpc error: code = Unknown desc = error validating proposal: access denied: channel [mychannel] creator org unknown, creator is malformed]
Failed to get network: Failed to create new channel client: event service creation failed: could not get chConfig cache reference: QueryBlockConfig failed: QueryBlockConfig failed: queryChaincode failed: Transaction processing for endorser [localhost:7051]: gRPC Transport Status Code: (2) Unknown. Description: error validating proposal: access denied: channel [mychannel] creator org unknown, creator is malformed
通常是因为fabcar生成了wallet
,在钱包中会使用到证书的公私钥文件,如果有重新生成过crypto-config文件夹,而没有删除根目录生成的walle,则会报此类错误。**解决方案:**删除项目根目录的wallet文件夹后再重新运行程序
如果运行时遇到下面几类报错,比较玄学,暂时不知道原因,多试几次,可能就行了。
[fabsdk/fab] 2022/08/01 12:15:58 UTC - dispatcher.(*Dispatcher).HandleConnectEvent -> WARN error creating connection: could not connect to localhost:8051: dialing connection on target [localhost:8051]: connection is in TRANSIENT_FAILURE
Failed to submit transaction: Failed to submit: error registering for TxStatus event: could not create client conn: could not connect to localhost:8051: dialing connection on target [localhost:8051]: connection is in TRANSIENT_FAILURE
>> 设置链码状态完成
Failed to evaluate transaction: Failed to evaluate: Multiple errors occurred: - Transaction processing for endorser [localhost:7051]: Chaincode status Code: (500) UNKNOWN. Description: error in simulation: failed to execute transaction 3639c35486a66b66c34667fde1e975d4cc07b3cbdc353d1cb7e1f5c7a319a780: invalid invocation: chaincode ‘fabcar’ has not been initialized for this version, must call as init first - Transaction processing for endorser [localhost:9051]: Chaincode status Code: (500) UNKNOWN. Description: error in simulation: failed to execute transaction 3639c35486a66b66c34667fde1e975d4cc07b3cbdc353d1cb7e1f5c7a319a780: invalid invocation: chaincode ‘fabcar’ has not been initialized for this version, must call as init first
Tape性能测试
部署
部署过程参考官方文档,我这里采用克隆仓库本地编译的形式
1 | git clone https://github.com/Hyperledger-TWGC/tape.git && cd tape && make t |
然后需要在tape目录下修改config.yaml
文件
- 修改证书和私钥文件的路径,将
./organizations
改为../fixtures/crypto-config
,与文件实际路径相对应即可 - 添加策略文件
policyFile: ./test/andLogic.rego
,否则可能会报错empty endorsement policy%
- 修改客户端执行参数,这里只保留一个参数,使用
- queryAllCars
进行测试 - 完善peer和orderer节点的配置,完整config样例参考,若报错
rpc error: code = Unavailable desc = closing transport due to: connection error: desc = "error reading from server: EOF", received prior goaway: code: ENHANCE_YOUR_CALM, debug data: "too_many_pings"%
则需要完善此配置
修改后的config.yaml
文件如下
1 | # Definition of nodes |
运行
在tape目录下执行./tape -c config.yaml -n 10000
,该命令的含义是,使用 config.yaml 作为配置文件,向 Fabric 网络发送10000条交易进行性能测试