2. Hyperledger Fabric 环境搭建详解
2.1 准备阶段
虚拟机的安装与配置
本教程以Vmware Workstation 16 Pro和xw版本为例进行安装与配置讲解,如果已经安装好linux系统,请跳过这一步!
Vmware Workstation 下载:
官方下载地址
中文官方网址
Vmware Workstation和Ubuntu镜像下载好以后,先对Vmware Workstation 激活,激活码推荐:
ZF3R0-FHED2-M80TY-8QYGC-NPKYF
下面开始进行Ubuntu的安装,打开Vmware Workstation,选择创建一个新的虚拟机,然后按下面图示进行操作即可,无需切换到u盘安装模式。
然后分别点击关闭、完成,结束虚拟机向导设置。接下来,我们可以启动该虚拟机,进一步进行安装和配置,步骤入下图所示:
等所有文件安装完成后,重启虚拟机就可以正常使用Ubuntu了,初次进入时会提示你登录Ubuntu账号或者Google账号,这些均可以跳过,直接来到Ubuntu桌面即可,调出命令行窗口,可右击选择Preferences查看Ubuntu命令行窗口的快捷键,这将大大提高的你的工作效率!可以看到该Ubuntu版本是带有图形界面的,在后面的区块链浏览器开发中会使用到,但是其他项目几乎都用不到,如果你的内存有限,可以关闭图形界面,使用单命令行模式进行操作。(推荐使用Xshell软件与虚拟机建立ssh远程连接)
至此你的Linux环境就已经搭建完成了,下面可以我们需要把所有先决条件安装在运行Hyperledger Fabric的平台上。
安装git与Curl
使用下面命令安装git
sudo apt-get install git
使用下面命令安装最新版Curl
sudo apt-get install curl
Golang的安装与配置
- 安装wget
sudo apt install wget
- 创建Go树
将下载的存档解压缩到 /usr/local/目录下并在/usr/local/go中创建一个 Go 树(推荐go1.15版本,比较稳定)
wget -c https://go.dev/dl/go1.15.7.linux-amd64.tar.gz -O - | sudo tar -xz -C /usr/local
- 安装vim
sudo apt-get install vim
- Go环境变量配置
vim /etc/profile #进入全局配置
export GOPATH=/home/smy/go
export PATH=$PATH:$GOPATH/bin
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
export GOPROXY="https://goproxy.io,direct"
- 配置生效
source /etc/profile
- 验证Go安装
go version
若成功安装,则显示
Docker的安装与配置
首先卸载旧版本的Docker
sudo apt-get remove docker docker-engine docker.io containerd runc
更新apt包索引并安装包以允许apt通过 HTTPS 使用存储库
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
添加Docker官方的GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
设置稳定存储库
echo \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
安装 Docker 引擎
需要更新apt包索引
sudo apt-get update
安装最新版本的Docker Engine和containerd
sudo apt-get install docker-ce docker-ce-cli containerd.io
验证安装
sudo docker run hello-world
设置以非root用户管理Docker
创建docker组
sudo groupadd docker
将您的用户添加到docker组中
sudo usermod -aG docker $USER
激活对组的更改
newgrp docker
验证您是否可以无管理员权限下使用docker
docker run hello-world
配置 Docker 开机启动
sudo systemctl enable docker.service
sudo systemctl enable containerd.service
配置代理
推荐使用阿里云免费镜像加速器登录容器镜像服务控制台后,在左侧导航栏选择镜像工具 > 镜像加速器,在镜像加速器页面就会显示为您独立分配的加速器地址。
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://3xgzqe2m.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
卸载docker引擎(如果你想清理环境时)
卸载 Docker Engine、CLI 和 Containerd 包
sudo apt-get purge docker-ce docker-ce-cli containerd.io
删除所有镜像、容器和卷
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
手动删除任何已编辑的配置文件
禁用开机启动(如果你不需要docker服务开机启动时)
sudo systemctl disable docker.service
sudo systemctl disable containerd.service
安装docker-compose
使用下面的命令安装docker-compose(如果你想下载其他版本,修改1.29.2即可)
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
为下载的二进制文件添加可执行权限(+x 加执行权限 +w 加写权限 +r 加读权限)
sudo chmod +x /usr/local/bin/docker-compose
对二进制文件应用可执行权限
验证安装
docker-compose --version
若安装成功则显示:
2.2 安装fabric
创建安装目录
mkdir -p gopath/src/github.com/hyperledger
进入安装目录下,并执行官网下载命令
cd gopath/src/github.com/hyperledger && curl -sSL https://bit.ly/2ysbOFE | bash -s
一般都无法成功执行,推荐的一种方法是用自己的浏览器访问 https://bit.ly/2ysbOFE ,将该脚本文件保存在本机,然后复制到虚拟机中。本机与虚拟机之间的交互有多种方法:
- 开启虚拟机中的文件共享功能,步骤如下:
可以看到,我设置的共享文件夹是在E盘的fabric目录,点击完成后重启虚拟机即可使用共享文件夹。将刚才下载的脚本文件bootstrap.sh放在该目录下,默认挂载到虚拟机的/mnt/hgfs/目录下,将该目录下的脚本文件复制到Hyperledger目录下,然后为脚本文件增加执行权限并执行该脚本文件
./bootstrap.sh
该脚本会自动拉取所需要fabric二进制文件、fabric示例和docker镜像,脚本执行完成后,将fabric-samples中的bin目录下的所有二进制文件都拷贝到/usr/local/bin中,以后便可在本地运行这些二进制文件
cp * /usr/local/bin
2.3 启动byfn网络
2.3.1 使用脚本启动网络
下面就可以运行测试网络了,官方示例提供了一个有完整注释的脚本——byfn.sh,它可以通过镜像快速启动一个 Hyperledger Fabric 网络,这个网络由代表两个组织的四个 Peer 节点和一个排序节点组成。它还将启动一个容器用于运行一个将 Peer 节点加入通道,部署并且实例化链码以及驱动已经部署的链码执行交易的脚本。
我们来到fabric-samples/first-network/目录下,可以看到一个byfn.sh脚本,这个脚本是创建第一个网络的脚本(build your first network)。我们执行这个脚本
./byfn.sh up
因为后面并没有指明哪一种排序服务,系统将会使用默认的"solo"排序服务,也即是只使用1个排序节点(虽然byfn网络中定义了5个排序节点)。如果你想支持多种链码语言,请指定参数启动五个 Raft 节点或者 Kafka 的排序服务来取代一个节点的 Solo 排序。输入上述命令后,接下来就是漫长的自动执行过程,过程中会生成很多的日志,脚本执行完成后,不妨看一看日志,帮助你理解一下这个脚本都是做了些什么。
2.3.2 手动搭建网络环境并启动网络
我们已经成功启动了一个测试网络,可能过程有点磕磕绊绊,但是结果还是令人满意的。有些人会在这里卡很久,甚至有些人放弃了区块链的学习,但是永远要相信,轻易得到的都没有任何价值。而我们也要认识到:事实上你自己并没有参与到网络搭建的每一步,只有自己亲自动手去实现一个区块链网络,才能更好的帮助你理解网络搭建的每一步是为了实现什么,以及为什么要这样做,这样做的逻辑是什么。我们不仅要做到知其然,更重要的是做到知其所以然!所以我们要尝试手动构建自己的第一个网络(BYFN),该网络提供了一个包含两个组织的Hyperledger Fabric网络,每个组织包含两个 Peer 节点,一个 “Solo” 模式的排序服务。
首先我们先关闭已经启动的网络,清理网络环境,然后一步步去探索网络设置。这个步骤很重要,可以避免出现一些不必要的问题和冲突。比如创建通道时报错通道已经创建,在节点上安装链码时报错链码已经安装等等。因此,需要养成一个比较好的习惯,每次关闭一个网络的时候,都要将无用的挂载清空,以此测试网络为例,接下来的命令会结束掉你所有的容器,移除加密的材料和四个构件,并且从 Docker 仓库删除链码镜像。
先关闭byfn网络
./byfn down
清理无用挂载(输入y确认)
docker volume prune
删除已经生成的证书和组件,尝试自己手动生成
rm -rf channel-artifacts
rm -rf crypto-config
2.3.2.1 生成加密材料和证书
执行完上述命令后,我们就可以开始我们的手动搭建网络的历程了,首先我们需要知道网络中所有成员,包括组织、节点、用户都是有自己的身份证书文件来证明自己是网络中一员,Fabric 项目提供了 cryptogen 工具(基于 crypto 标准库)实现自动化生成各个组织、节点和用户成员的身份证书。这些证书是身份的代表,在实体之间通信和交易的时候,它们允许对身份验证进行签名和验证。
我们简要介绍一下从cryptogen是怎么工作的:cryptogen 通过一个包含网络拓扑的文件 crypto-config.yaml,为所有组织和属于这些组织的组件生成一组证书和秘钥。每一个组织被分配一个唯一的根证书(ca-cert),它绑定该组织的特定组件(Peer 节点和排序节点)。Fabric 中的事务和通信由一个实体的私钥(keystore)签名,然后通过公钥(signcerts)验证。在这个文件里你会发现一个 count 变量。我们通过它来指定每个组织的 Peer 节点数量,如果你设计的网络中需要每个组织中有两个peer节点,你需要将它设为2,那么生成证书时,你就发现生成两个peer节点的证书文件,同理,3个,4个peer节点也是如此。当然,仅做这些改动是不够的,还需要在docker-compose文件中进行修改。
在我们运行 cryptogen 工具之后,生成的证书和密钥将保存到一个名为 crypto-config 的文件夹中。注意, crypto-config.yaml 文件在排序组织中设置了五个排序节点。Solo 排序服务只会使用一个排序节点来创建系统通道和 mychannel,,除非使用 Raft 或者 Kafka 排序服务,cryptogen 才会为这五个排序节点生成证书。
cryptogen工具生成加密材料和证书需要依赖于crypto-config.yaml文件,1.4版本crypto-config.yaml的模板如下:
OrdererOrgs: # 定义 orderer 排序节点组织结构
- Name: Orderer # Orderer 排序节点组织的名称
Domain: example.com # orderer 排序节点组织的根域名
Specs: # 根据 “{{.Hostname}}.{{.Domain}}” 格式的模板生成各个排序节点的访问域名和该排序节点各种证书文件
- Hostname: orderer # 生成的访问域名为 orderer.example.com
- Hostname: orderer2 # 生成的访问域名为 orderer2.example.com
- Hostname: orderer3 # 生成的访问域名为 orderer3.example.com
- Hostname: orderer4 # 生成的访问域名为 orderer4.example.com
- Hostname: orderer5 # 生成的访问域名为 orderer5.example.com
PeerOrgs: # 定义 Peer 节点组织结构
- Name: Org1 # 第一个组织名称 Org1
Domain: org1.example.com # 第一个组织的根域名
EnableNodeOUs: true # 如果为 true,则在 msp 目录下就会生成 config.yaml 文件
Template: # 模板,根据默认的规则在 Org1 下生成 "0 ~ Count-1" 个peer节点
Count: 2 # 定义生成 2 个 peer 节点(peer0.org1.example.com,peer1.org1.example.com)
Users: # 定义创建普通用户的数量,管理员用户([email protected])会默认创建
Count: 1 # 创建一个普通用户([email protected])
- Name: Org2 # 第二个组织名称 Org2
Domain: org2.example.com # 第一个组织的根域名
EnableNodeOUs: true # 如果为 true,则在 msp 目录下就会生成 config.yaml 文件
Template: # 模板,根据默认的规则在 Org2 下生成 "0 ~ Count-1" 个peer节点
Count: 2 # 定义生成 2 个 peer 节点(peer0.org2.example.com,peer1.org2.example.com)
Users: # 定义创建普通用户的数量,管理员用户([email protected])会默认创建
Count: 1 # 创建一个普通用户([email protected])
你可以创建一个crypto-config.yaml配置文件,
touch crypto-config.yaml
然后编辑这个配置文件,可以使用以下任一命令进行编辑,
vi crypto-config.yaml
vim crypto-config.yaml
gedit crypto-config.yaml
将上述配置模板复制到该文件中,保存后退出(vim命令行中,i 插入模式 按ESC退出当前模式 输入 :wq保存并退出 :q退出 :q!退出不保存修改)
执行下面的命令生成网络的加密材料和证书
cryptogen generate --config=./crypto-config.yaml --output ./crypto-config
2.3.2.2 生成排序通道创世区块、通道配置交易、锚节点更新配置文件
接下来,我们使用configtxgen工具创建排序通道创世区块、通道配置交易、锚节点更新配置文件。
还是老规矩我们先介绍一下configtxgen工具是怎么工作的,Configtxgen 使用一个文件——configtx.yaml,为了帮助你理解,提前将该文件内容展示
################################################################################
# 组织部分:
# - 本节定义了稍后将在配置中引用的不同组织的标识
################################################################################
Organizations: # 组织定义配置部分
# 定义排序服务(Orderer)组织
- &OrdererOrg # &OrdererOrg 这个用法类似指针,此处是被 Profiles 中的 - *OrdererOrg 所引用,下面还有很多类型的用例
Name: OrdererOrg # Orderer 排序节点组织的名称
ID: OrdererMSP # Orderer 排序节点组织的ID,也就是本地MSP的名称(这是引用组织的关键)
MSPDir: crypto-config/ordererOrganizations/example.com/msp # Orderer 排序节点组织的 MSP 证书文件目录路径
# 定义本层级的应用控制策略,对于排序节点(Orderer)的策略,其权威路径为:/Channel///
Policies:
Readers: # 可读
Type: Signature
Rule: "OR('OrdererMSP.member')"
Writers: # 可写
Type: Signature
Rule: "OR('OrdererMSP.member')"
Admins: # 管理员 admin
Type: Signature
Rule: "OR('OrdererMSP.admin')"
- &Org1 # 定义组织1(Org1)
Name: Org1MSP # 组织1(Org1) 的名称
ID: Org1MSP # 组织1(Org1) 的ID,也就是 localMSP 的名称
MSPDir: crypto-config/peerOrganizations/org1.example.com/msp # 组织1(Org1)的 MSP 证书文件目录路径
# 定义本层级的应用控制策略,对于组织1(Org1)的策略,其权威路径为:/Channel///
Policies:
Readers: # 可读
Type: Signature
Rule: "OR('Org1MSP.admin', 'Org1MSP.peer', 'Org1MSP.client')"
Writers: # 可写
Type: Signature
Rule: "OR('Org1MSP.admin', 'Org1MSP.client')"
Admins: # 管理员 admin
Type: Signature
Rule: "OR('Org1MSP.admin')"
# 定义组织1的锚节点,用于跨组织进行 Gossip 通信
AnchorPeers:
- Host: peer0.org1.example.com # 锚节点的主机名(域名)
Port: 7051 # 锚节点的端口号
- &Org2 # 定义组织2(Org2)
Name: Org2MSP # 组织2(Org2) 的名称
ID: Org2MSP # 组织2(Org2) 的ID,也就是 localMSP 的名称
MSPDir: crypto-config/peerOrganizations/org2.example.com/msp # 组织2(Org2)的 MSP 证书文件目录路径
# 定义本层级的应用控制策略,对于组织2(Org2)的策略,其权威路径为:/Channel///
Policies:
Readers: # 可读
Type: Signature
Rule: "OR('Org2MSP.admin', 'Org2MSP.peer', 'Org2MSP.client')"
Writers: # 可写
Type: Signature
Rule: "OR('Org2MSP.admin', 'Org2MSP.client')"
Admins: # 管理员 admin
Type: Signature
Rule: "OR('Org2MSP.admin')"
# 定义组织2的锚节点,用于跨组织进行 Gossip 通信
AnchorPeers:
- Host: peer0.org2.example.com # 锚节点的主机名(域名)
Port: 9051 # 锚节点的端口号
################################################################################
#
# Capabilities 部分(这是在 v1.1.0 版本之后提出来的,不能用于在更早的版本上面)
# - 本节定义了 fabric 网络的功能,这是 v1.1.0中一个新概念,不应该在 v1.0.x 的 peer 和
# orderer 中使用,Capabilities 定义了存在于结构二进制文件中的功能,以便该二进制文件安全的
# 参与网络,如果添加新的 MSP 类型,则较新的二进制文件可能会识别并验证此类型的签名,而没有此
# 支持的旧二进制文件将无法验证这些事务,这可能导致具有不同世界状态的不同版本的结构二进制文件。
# 相反,为通道定义功能(Capabilities)会通知那些没有此功能的二进制文件,他们必须在升级之前
# 停止处理事务,对于v1.0.x,如果定义了任何功能(Capabilities)[包括关闭所有的功能配置],
# 那么v1.0.1 的 peer 会主动崩溃
################################################################################
Capabilities:
# 通道功能(Capabilities)适用于 orderer 节点和 peer 节点,并且必须得到两者的支持
# 将功能设置为 true 以要求它
Channel: &ChannelCapabilities
# 通道(Channel)的 v1.4.2 是一个行为标记,已被确定为所有在v1.4,2版本运行的 orderers 和 peers
# 但与先前版本的 orderers 和 peers 不兼容
V1_4_2: true
# Orderer功能(Capabilities)仅适用于排序节点,可以安全地与之前版本的 peers 一起使用
# 将功能设置为 true 以要求它
Orderer: &OrdererCapabilities
# Orderer的 v1.4.2 是一个行为标记,已被确认为所有在 v1.4.2 版本运行的 orderers 所需的行为标志,但与之前版本的orderers 不兼容
# 在启用 v1.4.2 版本的orderer功能之前,请确保通道上所有的排序节点(orderers)都为v1.4.2或更高版本。
V1_4_2: true
# 应用程序(Application)仅适用于 peer 节点网络,并且可以安全地与之前版本的 orderer 节点一起使用
# 将功能的值设置为 true 以使其需要
Application: &ApplicationCapabilities
# 应用程序(Application)的 v1.4.2 版本启用了 Fabric 1.4.2 的新的非向后兼容功能和修复
V1_4_2: true
# 应用程序(Application)的 v1.3 版本启用了 Fabric 1.3 的新的非向后兼容功能和修复
V1_3: false
# 应用程序(Application)的 v1.2 版本启用了 Fabric 1.2 的新的非向后兼容功能和修复(如果设置了更高版本的功能,则无需设置此功能)
V1_2: false
# 应用程序(Application)的 v1.1 版本启用了 Fabric 1.1 的新的非向后兼容功能和修复(如果设置了更高版本的功能,则无需设置此功能)
V1_1: false
################################################################################
# 应用程序(Application)部分
# - 这个部分定义了配置事务或创世区块应用相关的参数设置的值
################################################################################
Application: &ApplicationDefaults
# Organizations 是在网络的应用程序端定义为参与者的组织列表
Organizations:
# 定义本层级的应用控制策略,对于应用程序(application)策略,其权威路径为 /Channel/Application/
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
# 指定功能能力(Capabilities)为上面定义的 ApplicationCapabilities
Capabilities:
<<: *ApplicationCapabilities
################################################################################
# Orderer 节点部分
# - 本节定义了要为orderer节点的配置事务或创世区块的相关参数设置的值
################################################################################
Orderer: &OrdererDefaults
# 要启动的 Orderer 节点的共识类型,现在可以选的是 solo,kafka,etcdraft
OrdererType: solo # 这里使用的是 solo 模式
# orderer 节点的服务地址,如果是集群,需要设置多个地址
Addresses:
- orderer.example.com:7050
#- orderer.example2.com:8050
#- orderer.example3.com:9050
#- orderer.example4.com:10050
#- orderer.example5.com:11050
# 区块打包的最大超时时间(到了该时间就要打包区块)
BatchTimeout: 2s
# 区块打包的最大包含交易数
BatchSize:
# 一个区块里最大的交易数(最大到了该数量就要打包区块)
MaxMessageCount: 10
# 一个区块的最大字节数(任何时候都不能超过,最大到了该大小就要打包区块)
AbsoluteMaxBytes: 99 MB
# 一个区块的建议字节数,如果一个交易消息的大小超过了该值,就会被放到一个更大的区块中去
PreferredMaxBytes: 512 KB
# kafka 共识的相关配置设置
Kafka:
# kafka 的 brokens 服务地址,允许有多个
# 注意: 使用IP:端口(IP:port)表示法
Brokers:
- 127.0.0.1:9092
# 参与维护 Orderer 排序节点的组织,默认为空
Organizations:
# 定义本层级的应用控制策略,对于 Orderer 策略,其权威路径为:/Channel/Orderer/
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
# BlockValidation 配置项指定了哪些签名必须包含在区块中,以便对 peer 节点进行验证
BlockValidation:
Type: ImplicitMeta
Rule: "ANY Writers"
################################################################################
# Channel 通道部分
# - 本节定义要写入创世区块或配置交易的通道参数
################################################################################
Channel: &ChannelDefaults
# 定义本层级的应用控制策略,对于 Channel 策略,其权威路径为: /Channel/
Policies:
# 谁可以调用(invoke)交付区块(Deliver)的 API
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
# 谁可以调用(invoke)广播区块(Broadcast)的 API
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
# 默认情况下,谁可以修改此配置级别的元素
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
# Capabilities 配置描述应用层级的能力需求,这里直接引用前面 Capabilities 配置段中的 ChannelCapabilities 配置项
Capabilities:
<<: *ChannelCapabilities
################################################################################
# Profile 部分配置
# - 本节定义用于 configtxgen 工具的配置入口,包含联盟(Consortiums)的配置入口。 Profile
# 部分的配置主要是引入上面五个部分的配置参数。 configtxen 工具通过调用 Profile 参数,可以
# 实现生成特点的区块文件
################################################################################
Profiles:
# 创建 Orderer 创世区块的相关参数配置(Solo共识),包含 orderer 和联盟(Consortiums)两个部分
TwoOrgsOrdererGenesis:
# 合并引入 Channel 部分的配置
<<: *ChannelDefaults
# 合并引入 Orderer 部分的配置
Orderer:
<<: *OrdererDefaults
# 指定组织 配置为 OrdererOrg
Organizations:
- *OrdererOrg
# 指定能力要求为 OrdererCapabilities
Capabilities:
<<: *OrdererCapabilities
#定义 Orderer 所服务的联盟列表
Consortiums:
# 指定联盟列表名称为 SampleConsortium
SampleConsortium:
# 定义联盟中的组织,这里为 Org1,Org2
Organizations:
- *Org1
- *Org2
# 创建应用通道(Channel)的配置
TwoOrgsChannel:
# 应用通道关联的联盟列表为 SampleConsortium
Consortium: SampleConsortium
# 合并引入通道(Channel)配置
<<: *ChannelDefaults
# 合并引入 Application 的相关配置
Application:
<<: *ApplicationDefaults
# 初始加入通道的组织,这里为 Org1,Org2
Organizations:
- *Org1
- *Org2
# 指定能力要求为 ApplicationCapabilities
Capabilities:
<<: *ApplicationCapabilities
# 创建 Orderer 创世区块的相关参数配置(kafka 共识)
# 生成创世区块时,configtxgen 指定的 -profile 参数为 SampleDevModeKafka
SampleDevModeKafka:
<<: *ChannelDefaults
Capabilities:
<<: *ChannelCapabilities
Orderer:
<<: *OrdererDefaults
OrdererType: kafka
Kafka:
Brokers:
- kafka.example.com:9092
Organizations:
- *OrdererOrg
Capabilities:
<<: *OrdererCapabilities
Application:
<<: *ApplicationDefaults
Organizations:
- <<: *OrdererOrg
Consortiums:
SampleConsortium:
Organizations:
- *Org1
- *Org2
# 创建 Orderer 创世区块的相关参数配置(raft 共识)
# 生成创世区块时,configtxgen 指定的 -profile 参数为 SampleMultiNodeEtcdRaft
SampleMultiNodeEtcdRaft:
<<: *ChannelDefaults
Capabilities:
<<: *ChannelCapabilities
Orderer:
<<: *OrdererDefaults
OrdererType: etcdraft
EtcdRaft:
# 指定参与共识的 orderer 节点地址,端口,和证书文件
Consenters:
- Host: orderer.example.com
Port: 7050
ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt
ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt
- Host: orderer2.example.com
Port: 7050
ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.crt
ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.crt
- Host: orderer3.example.com
Port: 7050
ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/server.crt
ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/server.crt
- Host: orderer4.example.com
Port: 7050
ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer4.example.com/tls/server.crt
ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer4.example.com/tls/server.crt
- Host: orderer5.example.com
Port: 7050
ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer5.example.com/tls/server.crt
ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer5.example.com/tls/server.crt
Addresses:
- orderer.example.com:7050
- orderer2.example.com:7050
- orderer3.example.com:7050
- orderer4.example.com:7050
- orderer5.example.com:7050
Organizations:
- *OrdererOrg
Capabilities:
<<: *OrdererCapabilities
Application:
<<: *ApplicationDefaults
Organizations:
- <<: *OrdererOrg
Consortiums:
SampleConsortium:
Organizations:
- *Org1
- *Org2
这个文件包含了一个示例网络的定义。它拥有三个成员:一个排序组织(OrdererOrg)和两个 Peer 组织(Org1 & Org2),这两个 Peer 组织每个都管理和维护两个 Peer 节点。这个文件还定义了一个联盟——SampleConsortium,包含了我们的两个 Peer 组织。注意一下文件中 “Profiles” (系统级配置)部分的最下边,有几个特别的标题,理解他们可以提升你对网络配置的理解。
- TwoOrgsOrdererGenesis: 为 Solo 排序服务生成创世区块。
- SampleMultiNodeEtcdRaft: 为 Raft 排序服务生成创世区块。只有将 -o 参数指定为 etcdraft 时才可用。
- SampleDevModeKafka: 为 Kafka 排序服务生成创世区块。只有将 -o 参数指定为 kafka 时才可用。
- TwoOrgsChannel: 为我们的通道 mychannel 生成创世区块。
这些标题很重要,因为在我们创建网络各项构件的时侯,需要将它们将作为参数传入,在下面的命令中请你多注意观察一下这些标题。还要注意我们的 SampleConsortium 在系统级配置项中定义,并且在通道级的配置项中引用。通道存在于联盟的范围内,所有的联盟必须定义在整个网络范围内。该文件还包含两个值得注意的附加规范。第一,我们为每个组织指定了锚节点(peer0.org1.example.com & peer0.org2.example.com)。第二,我们为每个成员指定 MSP 文件位置,进而我们可以在排序节点的创世区块中存储每个组织的根证书。这是一个关键概念,现在每个和排序服务通信的网络实体都可以验证它们的数字签名。
Orderer 节点在启动时,可以指定使用提前生成的初始区块文件作为系统通道的初始配置,创世区块中包括了 Ordering 服务的相关配置信息以及联盟信息。创世区块可以使用configtxgen工具生成。生成的过程需要依赖configtx.yaml配置文件。其中configtx.yaml配置文件定义了整个网络中相关配置和拓扑结构信息。
接下来,我们需要告诉 configtxgen 工具去哪儿寻找它需要的 configtx.yaml 文件。我们会告诉它在当前的工作目录:
export FABRIC_CFG_PATH=$PWD
创建好用来存放组件的目录channel-artifacts
mkdir channel-artifacts
2.3.2.2 生成创世区块、通道交易配置和锚节点更新配置文件
使用configtxgen工具生成创世区块
configtxgen -profile TwoOrgsOrdererGenesis -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block
可以注意到,上述命令指定配置为 TwoOrgsOrdererGenesis(在configtx.yaml文件最后可以找到),通道ID为 byfn-sys-channel(系统通道ID),生成的创世区块路径及名称为 ./channel-artifacts/genesis.block,执行完上述命令后,可以在channel-artifacts目录下看到genesis.block文件,为了方便查看,将Block详细内容导入到json文件中。
configtxgen -inspectBlock channel-artifacts/genesis.block > channel-artifacts/genesis.block.json
您可以使用cat命令来查看json文件具体的内容:
cat ./channel-artifacts/genesis.block.json
创建通道配置交易
接下来,我们需要去创建通道配置交易。使用以下命令:
configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel
可以注意到,上述命令指定配置为 TwoOrgsChannel(在configtx.yaml文件最后可以找到),通道ID为 mychannel(应用通道ID),生成的通道配置交易路径及名称为 ./channel-artifacts/channel.tx。我将configtx.yaml中Profiles中TwoOrgsChannel这一段配置单独拎出来讲一下:
TwoOrgsChannel:
Consortium: SampleConsortium #定义联盟
<<: *ChannelDefaults #需要实现通道默认定义的能力
Application:
<<: *ApplicationDefaults #需要实现应用默认定义的能力
Organizations: #该联盟中包含组织1和组织2
- *Org1
- *Org2
Capabilities:
<<: *ApplicationCapabilities
设想一下这样的场景,角色有渔民A、饭店1老板B和饭店2老板C,渔民A同时向饭店1、2供货,,从实际角度考虑,渔民A卖给两个饭店同样的鱼,但可能卖的价格不同,那渔民A肯定不希望B和C之间有直接的通信,否则高价购买鱼的老板肯定不乐意了。那么A和B,A和C之间就有了独立通信的需求,那么我们就可以设计两个联盟,一个联盟中包含A和B,另一个联盟中包含A和C。然后每一个联盟分别创建一个通道,因为通道具有隔离通信的作用,因此C无法获得A和B的交易信息,B也无法获得A和C的交易信息。这样就模拟实现了我们的现实需求,在此示例网络中,我们可以添加一个通道配置定义:
ThreeOrgsChannel:
Consortium: SampleConsortium #定义联盟
<<: *ChannelDefaults #需要实现通道默认定义的能力
Application:
<<: *ApplicationDefaults #需要实现应用默认定义的能力
Organizations: #该联盟中包含组织1和组织2
- *Org2
- *Org3
Capabilities:
<<: *ApplicationCapabilities
这样就实现了,三个组织之间的隔离通信,实现了数据隐私性。当然这还涉及到添加一个新的组织的操作,在下面的章节会详细介绍如何静态、动态的向一个网络中添加一个新组织。
生成为组织定义锚节点的更新配置文件
为组织1生成定义锚节点的更新配置文件
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP
为组织2生成定义锚节点的更新配置文件
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP
2.3.2.3 使用docker容器启动fabric网络
我们已经手动生成证书/密钥和各种配置,现在我们可以使用Docker容器方式启动Fabric网络,docker-compose 文件关联了我们之前下载的镜像,然后通过我们之前生成的创世区块 genesis.block 引导排序节点。我们要手动运行这些命令,目的是为了探索每个语法和调用的功能。
docker容器的启动依赖于文件docker-compose-cli.yaml,而docker-compose-cli.yaml文件也引用了base目录下的peer-base.yaml和docker-compose-base.yaml文件,接下来重点介绍docker-compose-cli.yaml文件,1.4版本文件内容如下:
version: '2'
volumes:
orderer.example.com:
peer0.org1.example.com:
peer1.org1.example.com:
peer0.org2.example.com:
peer1.org2.example.com:
networks:
byfn:
services:
orderer.example.com:
extends:
file: base/docker-compose-base.yaml
service: orderer.example.com
container_name: orderer.example.com
networks:
- byfn
peer0.org1.example.com:
container_name: peer0.org1.example.com
extends:
file: base/docker-compose-base.yaml
service: peer0.org1.example.com
networks:
- byfn
peer1.org1.example.com:
container_name: peer1.org1.example.com
extends:
file: base/docker-compose-base.yaml
service: peer1.org1.example.com
networks:
- byfn
peer0.org2.example.com:
container_name: peer0.org2.example.com
extends:
file: base/docker-compose-base.yaml
service: peer0.org2.example.com
networks:
- byfn
peer1.org2.example.com:
container_name: peer1.org2.example.com
extends:
file: base/docker-compose-base.yaml
service: peer1.org2.example.com
networks:
- byfn
cli:
container_name: cli
image: hyperledger/fabric-tools:$IMAGE_TAG
tty: true
stdin_open: true
environment:
- SYS_CHANNEL=$SYS_CHANNEL
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
#- FABRIC_LOGGING_SPEC=DEBUG
- FABRIC_LOGGING_SPEC=INFO
- CORE_PEER_ID=cli
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key
- CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: /bin/bash
volumes:
- /var/run/:/host/var/run/
- ./../chaincode/:/opt/gopath/src/github.com/chaincode
- ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
- ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
- ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
depends_on:
- orderer.example.com
- peer0.org1.example.com
- peer1.org1.example.com
- peer0.org2.example.com
- peer1.org2.example.com
networks:
- byfn
首先,启动我们的网络(-d表示在后台执行,不显示详细网络日志信息)
docker-compose -f docker-compose-cli.yaml up -d
若启动成功,显示如下:
如果你想要查看日志流,你可以打开第二个终端来执行CLI调用
2.3.2.4 创建和加入通道
目前fabric网络已经成功启动,我们进入cli容器中进行下一步操作
docker exec -it cli bash
成功的话你会看到下面的输出:
root@0d78bb69300d:/opt/gopath/src/github.com/hyperledger/fabric/peer#
要想运行后边的 CLI 命令,我们需要使用下边的命令来设置四个环境变量。这些 peer0.org1.example.com 的环境变量已经在 CLI 容器中设置过了,所以不用再设置了。但是,如果你想向其他 Peer 节点或者排序节点发送调用,那么你发送任何 CLI 调用的时候都需要像下边的命令一样覆盖这些环境变量(也就是发送CLI调用时,先声明我想操作的是哪一个组织的哪一个peer节点,这样就可以实现在一个cli中配置4个peer)
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/A[email protected]/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
接下来,我们会把之前创建好的通道配置交易(channel.tx)作为创建通道请求的一部分传递给排序节点。
我们使用 -c 标志指定通道的名称,-f 标志指定通道配置交易文件路径,在这个例子中它是 channel.tx,当然你也可以使用不同的名称挂载你自己的交易配置。例如,我们上面讲过,在三个组织的场景,我们在Profiles中增加了一个通道配置ThreeOrg3Channel,那么我们完全可以使用configtxgen工具再生成一个新的通道配置交易newchannel.tx。
使用下面的命令创建一个名为mychannel的通道
peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/channel.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
--tls 表示允许TLS加密通信,--cafile 是排序节点的根证书的本地路径,允许我们去验证 TLS 握手。
这个命令执行完成后返回一个创世区块,
现在我们把 peer0.org1.example.com 加入通道
peer channel join -b mychannel.block
接下来我们将peer0.org2.example.com 加入通道
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/A[email protected]/msp
CORE_PEER_ADDRESS=peer0.org2.example.com:9051
CORE_PEER_LOCALMSPID="Org2MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crtpeer channel join -b mychannel.block
将peer1.org1.example.com 加入通道
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/A[email protected]/msp
CORE_PEER_ADDRESS=peer1.org1.example.com:8051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crtpeer channel join -b mychannel.block
将peer1.org2.example.com 加入通道
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/A[email protected]/msp
CORE_PEER_ADDRESS=peer1.org2.example.com:10051
CORE_PEER_LOCALMSPID="Org2MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crtpeer channel join -b mychannel.block
到此为止,4个peer节点都已经加入通道,但是并不是说每个组织定义了两个peer,就必须将peer全部加入通道中,在此只是演示一下如何通过声明环境变量来操作不同组织的不同peer。在一般情况下,您可以将您想设为锚节点的那个节点加入通道即可,节点之间可以通过Gossip协议通信。
(介绍一下Gossip协议:Gossip协议是用来确保在不同组织中的Peer节点知道彼此)
具体来说,当提交一个包含锚节点更新的配置区块时,Peer节点会联系锚节点并从锚节点那里获取该节点所知道的所有Peer节点的信息。一旦每个组织中至少有一个Peer节点已经联系到一个或者多个锚节点的话,锚节点就会知道这个通道中的每个Peer节点。因为Gossip通信是不变的,并且Peer节点总是会告知它们不知道的Peer节点,这样就建立起了一个通道成员的通用视图。(可以联想计算机网络中的RIP路由协议)
举个例子,假设通道中有三个组织:A、B和C,组织C定义的锚节点为peer0.orgC,当peer1.orgA与peer0.orgC通信的时候,它会告诉peer0.orgC关于同一个组织中peer0.orgA的信息。然后当peer1.orgB与peer0.orgC通信的时候,peer0.orgC会告诉peer1.orgB关于peer0.orgA的信息。这样B就知道了A的信息,之后组织A和B就可以开始直接地交换成员信息而不需要任何来自peer0.orgC的帮助了。
由于组织间的通信要基于Gossip来工作,所以在通道配置中至少要定义一个锚节点。建议每个组织提供它们自己的一组锚节点)
您可以使用命令来查看当前操作的节点加入了哪些通道
peer channel list
2.3.2.5 更新锚节点
接下来的命令是通道更新,它会传递到通道的定义中去。实际上,我们在通道创世区块的头部添加了额外的配置信息。注意我们没有编辑创世区块,但是简单的把将会定义锚节点的增量添加到了链中。对于在头部添加额外配置信息,在我们动态添加Org3中会提及,在这里我们只需要将其理解为更新配置信息会被添加到链中即可。
更新通道定义,将 Org1 的锚节点定义为 peer0.org1.example.com
先把操作对象转换到peer0.org1
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/A[email protected]/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
使用以下命令更新通道定义,将 Org1 的锚节点定义为 peer0.org1.example.com
peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
操作对象转换到peer0.org2
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/A[email protected]/msp CORE_PEER_ADDRESS=peer0.org2.example.com:9051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
使用以下命令更新通道定义,将 Org2 的锚节点定义为 peer0.org2.example.com
peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
2.3.2.6 安装链码
应用程序和区块链账本通过链码 chaincode 进行交互。因此,我们要在每个会执行以及背书我们交易的节点安装链码,然后在通道上实例化链码。首先,在 Org1 的 peer0 节点上安装 Go、Node.js 或者 Java 链码。这些命令把指定的源码放在节点的文件系统里。
每个链码的名称和版本号规定你只能安装一个版本的源码。源码存在于 Peer 节点文件系统上的链码名称和版本号的上下文里,它是与语言无关的。同样,被实例化的链码容器将反映出是什么编程语言的链码被安装在 Peer 节点上。
安装基于Golang语言开发的链码
peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
安装基于Node.js语言开发的链码
peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/
安装基于Java语言开发的链码
peer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/
首先在peer0.org1.example.com上安装基于Go语言开发的链码
将操作对象转换成peer0.org1
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/A[email protected]/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
在peer0.org1.exmaple.com上安装链码
peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
-n 指定链码的名称,-v 指定链码版本号,-l 指定链码的语言,-p 指定链码所在的路径
当我们在通道上实例化链码之后,背书策略被设定为需要 Org1 和 Org2 的节点都背书。所以,我们需要在 Org2 的节点上也安装链码。
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/A[email protected]/msp
CORE_PEER_ADDRESS=peer0.org2.example.com:9051
CORE_PEER_LOCALMSPID="Org2MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crtpeer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
2.3.2.7 实例化链码
chaincode安装后需要实例化才能使用,在channel中,一个chaincode只需要进行一次实例化(instantiate)操作即可。链码的实例化过程有点慢,请您耐心等待!
peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"
在通道上实例化链码,意味着会在通道上初始化链码,为链码指定背书策略,然后为目标节点启动链码容器。注意 -P 这个参数。这是我们的策略,我们在此策略中指定针对要验证的此链码的交易所需的背书级别。在下面的命令里你将会注意到我们指定 -P "AND ('Org1MSP.peer','Org2MSP.peer')" 作为策略。这表明我们需要属于 Org1 和 Org2 的节点“背书” (就是说要两个背书)。如果我们把语法改成 OR ,那我们将只需要一个背书。
重点关注一下最后的-c标志,这表示对链码的操作,Args表示参数,其参数值就是链码中定义的一些方法,init表示参数为初始化,这时候就会调用链码中的init方法,该链码中init方法定义如下:
该链码中定义很多方法,你可以在/opt/gopath/src/github.com/chaincode/chaincode_example02/go/中进行查看,这可以帮助你更好的理解链码的一系列操作背后的逻辑!
cat chaincode_example02.go
如果你想让其他的节点与账本交互,你需要将他们加入通道,然后在节点的文件系统上安装名字、版本和语言一样的链码。一旦它们尝试与特定的链代码进行交互,就会为每一个节点启动一个链码容器。一旦链码在通道上实例化,我们可以放弃 l 标志。我们只需传递通道标识符和链码的名称。
例如,我想让peer1.org2.example.com也与该链码进行交互,我只需要进行如下操作:
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/A[email protected]/msp
CORE_PEER_ADDRESS=peer1.org2.example.com:10051
CORE_PEER_LOCALMSPID="Org2MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crtpeer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
是不是和之前的操作一个原理,俗话说:万变不离其宗,我们要善于从个性中找到共性,从共性中发掘个性,善于发现规律,就能在学习上事半功倍。
2.3.2.8 与链码交互
查询(query)
让我们查询 a 的值,以确保链码被正确实例化并且向状态数据库写入了数据。查询的语法是这样的:
peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
因为调用初始化方法时,我们赋给键a的值为100,因此查询结果应该也是100
调用(invoke)
现在我们从 a 账户向 b 账户转账 10 。这个交易将会产生一个新的区块并更新状态数据库。 调用的语法是这样的:
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'
命令很长,不要害怕,我们分解来看。
-o 表示使用的排序服务(共识服务) --tls 使用TLS加密通信 --cafile 这是排序节点的根证书的本地路径,允许我们去验证TLS握手 -C 通道名称 -n 链码名称 --peerAddresses 组织1锚节点的地址(域名+端口号) --tlsRootCertFiles TLS根证书路径 -c 链码操作,调用链码函数为invoke( ) 实现a向b转账10
此时再查询键a的值和键b的值,看是否发生变化:
peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
peer chaincode query -C mychannel -n mycc -c '{"Args":["query","b"]}'
现在,你可以随意重新开始并操纵键值对和后续调用。
2.3.2.10 关停网络、清理网络环境
我们已经完成fabric网络的启动以及相关测试,那么我们开始新的测试之前,需要将当前网络关停并清理网络环境,防止和下面的测试出现冲突。
关停byfn网络使用命令
./byfn down
清理网络环境使用命令
docker volume prune
该命令会清理网络无效的挂载
2.3.2.10 一些补充
链码必须安装在节点上才能实现对账本的读写操作。此外,一个链码容器不会在节点里启动,除非让链码执行 init 或者交易,(例如查询“a”的值)。交易导致容器的启动。当然,所有通道中的节点都持有以块的形式顺序存储的不可变的账本精确的备份,以及用来保存当前状态的快照状态数据库。这包括了没有在其上安装链码的节点(例如上面例子中的 peer1.org1.example.com)。最后,链码在被安装后将是可用状态(例如上面例子中的 peer1.org2.example.com),因为它已经被实例化了。
链码在 mychannel 上“实例化”。实例化是把链码添加到通道上,为目标节点启动容器。。向 peer1.org2.example.com 发送一次对 “a” 的值的查询。启动了第三个名为 dev-peer1.org2.example.com-mycc-1.0 的链码容器。
此时网络中共启动了三个这样的容器,如下图所示:
另外,如果你想查看链码日志,可以使用以下命令:
docker logs dev-peer0.org1.example.com-mycc-1.0
docker logs dev-peer0.org2.example.com-mycc-1.0
docker logs dev-peer1.org2.example.com-mycc-1.0
2.4 体验使用couchdb
状态数据库可以从默认的从 goleveldb 切换到 CouchDB 。链码就可以使用 CouchDB 的功能了, CouchDB 提供了额外的能力来根据 JSON 形式的链码服务数据提供更加丰富以及复杂的查询。
使用 CouchDB 代替默认的数据库(goleveldb),除了在启动网络的时侯传递 docker-compose-couch.yaml 之外,还需要遵循前面提到的生成配置文件的过程,使用下面的命令选择使用CouchDB状态数据库:
docker-compose -f docker-compose-cli.yaml -f docker-compose-couch.yaml up -d
该命令拉取了couchdb的镜像,并且创建了4个状态数据库couchdb,重新创建了之前已经创建过的几个容器。
为了联系 CouchDB 的查询能力,你将需要使用被格式化为 JSON 的数据(例如 marbles02)。你可以在 fabric/examples/chaincode/go 目录中找到 marbles02 链码。
我们将同样按照其他节点创建和加入通道步骤进行操作,当 peer1.org2.example.com节点加入到了通道后,就可以与marbles02 链码交互:
- 在 peer0.org1.example.com 上安装链码:
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
CORE_PEER_ADDRESS=peer1.org1.example.com:8051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt
peer chaincode install -n marbles -v 1.0 -p github.com/chaincode/marbles02/go
因为这是一个新的chaincode,所以需要实例化一下链码
peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -v 1.0 -c '{"Args":["init"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer')"
创建一些 marble 并转移它们
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["initMarble","marble1","blue","35","tom"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["initMarble","marble2","red","50","tom"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["initMarble","marble3","blue","70","tom"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["delete","marble1"]}'
- 在CLI中运行常规的查询(例如查询marble2)
peer chaincode query -C mychannel -n marbles -c '{"Args":["readMarble","marble2"]}'
- 您也可以检索特定 marble 的历史记录,例如 marble1:
peer chaincode query -C mychannel -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'
以下的几个命令可能会在网络搭建过程中用到:
列出当前节点所加入的Channel
peer channel list
列出当前节点所有已经安装的Chaincode
peer chaincode list --installed
获取特定Channel上的区块信息
peer channel getinfo -c mychannel
通道mychannel目前的区块(最新区块)高度为10,当前区块的哈希值以及前一个区块的哈希值
获取特定高度的Block的详细内容(注意区块编号是从0开始的,和数组类似)
peer channel fetch 5 mychannel_5.block -o orderer.example.com:7050 -c mychannel --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
上述命令执行完成后,在当前目录生成一个名为mychannel_5.block文件,里面包含了区块高度为6的区块信息。
我们将该文件转换为易读的JSON格式
configtxgen -inspectBlock mychannel_5.block > mychannel_5.block.json
超过字数限制了,下面一节是关于私有数据的使用。