前言

前几天折腾实验 Fabric CA 网络搭建 ,今天直接使用官方脚本一键搭建测试网络,涉及了一点智能合约相关的内容。

测试网络中没有 CA 节点, 教程 最底下有些相关内容,结构刚好对应 Fabric CA 网络搭建中的 拓扑结构

实验环境

Ubuntu 18.04.5 Desktop

  • Docker version 19.03.12
    • 非 Root 权限运行
  • Docker Compose version 1.26.2
  • Go version go1.15.1
    • 阿里镜像

部署链码时遇到如下错误,未找到解决方法

  • Fabric v2.2.0 + Fabric CA v1.4.7
  • Fabric v2.1.1 + Fabric CA v1.4.7
1
2
# ./network.sh deployCC
~/fabric-samples/asset-transfer-basic/chaincode-go ~/fabric-samples/test-network verifying github.com/cucumber/godog@v0.8.0/go.mod: checksum mismatch

相关搜索:

改用 Fabric v2.0.1 + Fabric CA v1.4.6 成功复现实验

1
2
# curl -sSL https://bit.ly/2ysbOFE | bash -s -- <fabric_version> <fabric-ca_version> <thirdparty_version>
curl -sSL https://bit.ly/2ysbOFE | bash -s -- 2.0.1 1.4.6 0.4.18

部署测试网络

建立测试网络

Peer 节点负责验证和存储区块链交易信息,测试网络中有两个组织 Org1 和 Org2,每个组织都有一个节点 Peer0

  • peer0.org1.example.com
  • peer0.org2.example.com

Orderer 节点基于共识机制对交易进行排序,并将交易信息打包到区块链中,测试网络中有一个使用 Raft 共识算法的排序节点 Orderer

  • orderer.example.com
1
2
3
4
5
6
7
8
9
10
11
12
13
cd fabric-samples/test-network

# 脚本帮助信息
./network.sh -h

# 停止并移除容器
./network.sh down

# 生成 orderer 和 peer 节点,不创建通道
./network.sh up

# 查看容器
docker ps -a

创建通道

为两个组织创建通道,通道是区块链网络成员之间的私有“子网”,所有交易都在通道中进行

1
2
3
# 为 Org1 和 Org2 创建通道,将 Peer 节点加入通道
# 默认通道名称为 mychannel
./network.sh createChannel

通道上启动链码

智能合约用于管理资产,区块链网络成员运行的程序可以调用智能合约生成、交易资产

为了确保交易的有效性,打包进区块链的交易必须由多个组织签名,节点通过背书策略确定交易有效性

  • 信任模型(trust model)
  • 防止交易被篡改、伪造

关于链码和智能合约

  • 一个智能合约定义在一个链码中,多个智能合约也可以定义在同一个链码中
  • 管理员通常使用链码将相关的智能合约组织起来进行部署,并且链码也可以用于 Fabric 的底层系统编程
1
2
# 部署链码
./network.sh deployCC

以上命令首先在两个组织的 Peer 节点上安装名称为 fabcar 链码(及其依赖),然后将其部署到它们之间的通道 mychannel

与网络交互

在 Peer 节点上使用终端和区块链网络进行交互,调用已经部署的智能合约

1
2
3
4
5
# 可执行程序添加到环境变量
export PATH=${PWD}/../bin:${PWD}:$PATH

# 设置环境变量,指向配置文件
export FABRIC_CFG_PATH=$PWD/../config/

Org1

1
2
3
4
5
6
7
8
9
# Org1 环境变量
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 query -C mychannel -n fabcar -c '{"Args":["queryAllCars"]}'

当区块链网络成员想要转移或改变资产时,将会调用链码

  • fabcar 链码的倍数策略是 Org1 和 Org2 都要对交易进行签名
1
2
3
4
5
6
7
8
9
10
11
12
# 生成交易信息
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 fabcar \
--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":"changeCarOwner","Args":["CAR9","Dave"]}'

Ogr2

1
2
3
4
5
6
7
8
9
# Org2 环境变量
export CORE_PEER_TLS_ENABLED=true
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 fabcar -c '{"Args":["queryCar","CAR9"]}'

停止网络

部署智能合约实验需要重新搭建网络

1
2
# 停止并移除容器
./network.sh down

部署智能合约

搭建测试网络

重新搭建网络并创建通道,手动打包和部署智能合约

1
2
3
4
5
6
7
8
9
10
cd fabric-samples/test-network

# 停止并移除容器
./network.sh down

# 生成 orderer 和 peer 节点
./network.sh up

# 创建通道
./network.sh createChannel

打包智能合约

确认并下载依赖的 Go 模块,智能合约 fabric-samples/chaincode/fabcar/go/fabcar.go

1
2
3
4
5
6
7
cd ../chaincode/fabcar/go

# 查看依赖
cat go.mod

# 安装依赖
GO111MODULE=on go mod vendor

将链码进行打包,生成 fabcar.tar.gz

  • 智能合约定义在链码中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cd ../../../test-network

# 可执行程序添加到环境变量
export PATH=${PWD}/../bin:${PWD}:$PATH

# 设置环境变量,指向配置文件
export FABRIC_CFG_PATH=$PWD/../config/

# 确认 peer 版本 ≥ 2.0.0
peer version

# 设置环境变量,指向 MSP 文件夹
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp

# 打包链码
peer lifecycle chaincode package fabcar.tar.gz \
--path ../chaincode/fabcar/go/ \
--lang golang \
--label fabcar_1

安装智能合约

链码需要安装在所有可执行交易的节点上,Peer 节点负责验证和存储区块链交易信息,测试网络中有两个组织 Org1 和 Org2,每个组织都有一个节点 Peer0 ,需要在这两个节点上安装链码

  • peer0.org1.example.com
  • peer0.org2.example.com

Org1

1
2
3
4
5
6
7
8
9
# Org1 环境变量
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 lifecycle chaincode install fabcar.tar.gz

Org2

1
2
3
4
5
6
7
8
9
# Org2 环境变量
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_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 lifecycle chaincode install fabcar.tar.gz

审批链码定义

使用链码前必须审批链码定义,审批以组织为单位

  • 背书策略决定需要多少节点进行审批
    • 审批将通过 gossip 通信发送给组织中的其他成员
    • 建议所有通道成员在链码提交至通道前审批链码
  • 执行审批的节点需要拥有管理员权限
    • 通过 MSP 进行身份认证
  • Package ID :安装成功返回链码标识
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
# 查询链码信息
peer lifecycle chaincode queryinstalled

# 设置变量
CC_PACKAGE_ID=fabcar_1:533e8c6699b013f5b79a74e15be1bc1063f46f947da2c95fc679deb08e10acff

# 当前环境变量为 Org2
# Org2 审批
peer lifecycle chaincode approveformyorg \
-o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--channelID mychannel \
--name fabcar \
--version 1.0 \
--package-id $CC_PACKAGE_ID \
--sequence 1 \
--tls \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

# 设置 Org1 环境变量
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_ADDRESS=localhost:7051

# Org1 审批
peer lifecycle chaincode approveformyorg \
-o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--channelID mychannel \
--name fabcar \
--version 1.0 \
--package-id $CC_PACKAGE_ID \
--sequence 1 \
--tls \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

提交链码定义

组织可以将链码定义提交给通道,如果大多数通道成员已经审批过链码定义,则提交事务将成功,通道将根据链码定义中约定的参数执行链码

通道成员审批的链码将被提交至排序节点,添加到区块中并分发给通道,通道成员根据背书策略验证是否有足够的节点审批

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
# 查询通道成员是否审批了相同的链码定义
peer lifecycle chaincode checkcommitreadiness \
--channelID mychannel \
--name fabcar \
--version 1.0 \
--sequence 1 \
--tls \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
--output json

# 提交链码定义(组织管理员)
peer lifecycle chaincode commit \
-o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--channelID mychannel \
--name fabcar \
--version 1.0 \
--sequence 1 \
--tls \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
--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

# 确认链码是否被提交至通道
peer lifecycle chaincode querycommitted \
--channelID mychannel \
--name fabcar \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

调用链码

链码提交至通道后,安装了链码的通道成员运行链码,可以通过客户端程序进行调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 初始化账本
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 fabcar \
--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 fabcar -c '{"Args":["queryAllCars"]}'

问题解决

curl - connection refused

1
curl: (7) failed to connect to raw.githubusercontent.com port 443: connection refused

修改 hosts 文件,添加 github 域名记录

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
# 编辑 hosts 文件
sudo vi /etc/hosts

# 添加以下内容
# # GitHub Start
# 13.229.188.59 github.com
# 59.24.3.173 gist.github.com
# 185.199.109.153 assets-cdn.github.com
# 151.101.76.133 raw.githubusercontent.com
# 151.101.76.133 gist.githubusercontent.com
# 151.101.76.133 cloud.githubusercontent.com
# 151.101.76.133 camo.githubusercontent.com
# 151.101.76.133 avatars0.githubusercontent.com
# 151.101.76.133 avatars1.githubusercontent.com
# 151.101.76.133 avatars2.githubusercontent.com
# 151.101.76.133 avatars3.githubusercontent.com
# 151.101.76.133 avatars4.githubusercontent.com
# 151.101.76.133 avatars5.githubusercontent.com
# 151.101.76.133 avatars6.githubusercontent.com
# 151.101.76.133 avatars7.githubusercontent.com
# 151.101.76.133 avatars8.githubusercontent.com
# # GitHub End

# 重启
sudo reboot

docker - permission denied

1
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/json: dial unix /var/run/docker.sock: connect: permission denied

根据 Manage Docker as a non-root user 将当前用户加入 docker 用户组

1
2
3
4
5
6
7
8
# 创建用户组
sudo groupadd docker

# 当前用户加入用户组
sudo usermod -aG docker $USER

# 更新用户组
newgrp docker

查找指定目录中包含指定字符串的文件

当时排查下载的 godog v0.8.0 哈希值不对应,将目录中所有文件对应的字符串改为 godog@v0.8.1 ,问题并没有解决xd

1
grep -rnw '/home/jck/fabric-samples/' -e 'godog'

总结

阅读官方教程花了点时间,本以为可以无脑 copy 代码,没想到在哈希值错误的问题上花费了很长时间,结果还是没解决,转而使用旧版本进行实验了。

测试网络搭建完发现教程底下还有 CA 相关的内容,突然想到这不是和前几天做的实验对应上了吗(当时做得挺不清楚的,没有验证整个网络的可用性),应该是可以读脚本源代码学 CA 网络搭建的orz

Hyperledger Fabric 真厉害

参阅