Michael.W谈hyperledger Fabric第11期-手动搭建Fabric网络之启动网络与功能调试二
7 背书策略
链码安装后需要进行初始化,且只需要初始化一次。在做链码初始化之前,我先讲一下背书策略的概念。
举个很生动的例子:假如你要结婚,那么在结婚前应该买车买房。如果没有买车买房,那么对应的彩礼就要多一些。如果你不买房买车,也不多给彩礼,那你这个婚就基本结不成了。
以上例子中,结婚就是Fabirc中的交易,买车买房还是多给彩礼就是交易之前的背书策略。
在Fabric这个平台上的任何交易都是要通过执行链码来控制的。每笔交易都需要有一个交易的过程,即限定这个交易怎么样才算是一笔成功的交易。
背书策略可以简单地理解为执行链码产生交易之前定义的一个交易规则。
目前我构建的网络中有两个组织org1和org2,每个组织中都有两个peer节点peer0和peer1。假如我指定每笔交易必须经过org1组织和org2组织的节点同意之后才会生效忙或者只要org1组织中有节点同意就可以生效,以上两种规则都是Fabric交易中的背书策略。
这种背书策略不只局限于以上两种,还有更多的组合。
7.1 定义背书策略的关键字
如何表示背书策略?可以使用关键字拼接成的字符串来完成。
关键字有:
- AND
- OR
AND表示"与"关系。如果需要经过A和B都同意才生效,使用AND ;
OR表示“或”关系。如果A和B之中有一个同意即可,使用OR。
7.2 限定组织成员
关键字后面要加组织和成员类别。
组织用组织的ID表示,可以去前面编写的configtx.yaml文件中查找。
组织ID后用.
来连接组织内的成员类型,借此来限定用户群。成员类型分成两类:
- 管理员:admin
- 普通用户:member
下面举几个例子以供参考:
# 必须通过4个组织的用户共同验证才可以使交易生效
“AND ('Org1Msp.member','Org2Msp.member','Org3Msp.member','Org4Msp.member')”
# 只需要通过前三个组织中的任一组织中成员验证即可生效
"OR ('Org1Msp.member','Org2Msp.member','Org3Msp.member')"
# 还可以将二者结合使用,表示组织一以及组织二、三种的任一成员同意即生效
“AND (‘Org1MSP.member’,OR ('Org2Msp.member','Org3Msp.member'))”
那背书策略什么时候会被调用?
答:当Fabric网络中执行交易的时候,背书策略才会被调用。
还有一点需要注意一下:
背书策略只对链码中的“写数据”操作起作用,对“读数据”的查询类操作不起作用。
8 链码的初始化
背书策略需要在链码初始化的过程中指定。链码初始化,就相当于初始化网络账本中的数据资产,同时指定背书策略。
# 设置环境变量,如果前面有设置过可以跳过
tlsfile=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/michael.com/orderers/orderer.michael.com/msp/tlscacerts/tlsca.michael.com-cert.pem
# 链码初始化,给michael账户和wang账户分别初始化为1000和2000元。
$ peer chaincode instantiate -o orderer.michael.com:7050 --tls --cafile $tlsfile -C michaelchannel -n michaelchaincode -v 1.0 -P "AND ('Org1MSP.member','Org2MSP.member')" -c '{"Args":["init","michael","1000","wang","2000"]}'
# -o:orderer节点的地址+端口
# --tls:通信是否进行tls加密,参数后面什么都不写默认为true
# --cafile:orderer节点的msp下tlsca对应的证书文件
# -C:当前所处的通道ID
# -c:JSON格式的构造参数,默认值是“{}"。相当于给链码传递指令。
# -n:要初始化的链码名称
# -v:要初始化的链码版本
# -P:背书策略
# -l:编写链码的语言,默认为go语言
只需要在一个peer节点上执行即可。目前我客户端所连接的peer节点为组织2的peer1节点。
注意:相同的链码只需要初始化一次即可!
9 查询账本
链码初始化成功后,可以用客户端连接到任一peer节点来查询账户信息:
# 切换到组织1的peer1节点
$ CORE_PEER_ADDRESS=peer1.org1.michael.com:7051
$ CORE_PEER_LOCALMSPID=Org1MSP
$ CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.michael.com/users/[email protected]/msp
$ CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.michael.com/peers/peer1.org1.michael.com/tls/ca.crt
$ CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.michael.com/peers/peer1.org1.michael.com/tls/server.crt
$ CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.michael.com/peers/peer1.org1.michael.com/tls/server.key
# 查询michael账户的余额
$ peer chaincode query -n michaelchaincode -C michaelchannel -c '{"Args":["query","michael"]}'
# -n:要查询的链码名称
# -C:当前所处的通道ID
# -c:JSON格式的构造参数。相当于给链码传递指令。
# 查询wang账户的余额
$ peer chaincode query -n michaelchaincode -C michaelchannel -c '{"Args":["query","wang"]}'
可见账户余额都是正确的。
一个peer节点在链码初始化之后执行的第一条查询或者写账本的操作指令时,执行时间都相对较长。因为此时该节点会将当前容器的镜像(包含链码和账本)复制到仓库中。之后的操作执行速度便会快的飞起。
目前我只在peer1.org1和peer1.org2上执行过链码操作命令,所以镜像仓库中只有这两个节点的数据镜像文件。这是Fabric出于对数据安全性的考虑,将数据与逻辑分离的一种设计。即这个"dev-"开头的跑链码(逻辑),peer节点容器上存数据。
这些镜像不会随着docker-compose down
指令自动删除。如果当你想销毁该网络内的所有容器,请将这些数据镜像文件也删除。否则在重启网络后会干扰到下一次的链码初始化。
# 一键删除仓库中第一列以"dev-"开头的镜像文件
$ docker rmi $(docker images | grep "dev-" | awk '{print $1}') -f
10 背书节点
在执行操作之前讲一下背书节点的概念。
当客户端发起一笔交易时,客户端会根据初始化时候设定的背书策略进行消息请求的分发。
我设置的背书策略为"AND ('Org1MSP.member','Org2MSP.member')"
。假如org1的peer0和org2的peer0收到了客户端分发过来的消息请求,这两个节点需要进行模拟交易。
此时org1的peer0和org2的peer0就是背书节点。
注:至于哪些peer节点可以收到分发的消息请求(成为背书节点),是在交易操作的时候指定的。所以背书节点的个数是无法确定的,受到背书策略和客户端发出交易消息的双重影响。
11 发起交易
# 设置环境变量,如果前面有设置过可以跳过
tlsfile=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/michael.com/orderers/orderer.michael.com/msp/tlscacerts/tlsca.michael.com-cert.pem
# 设置指定的背书节点的TLS根证书文件
$ rootcrt1=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.michael.com/peers/peer1.org1.michael.com/tls/ca.crt
$ rootcrt2=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.michael.com/peers/peer1.org2.michael.com/tls/ca.crt
# 发起交易
$ peer chaincode invoke -o orderer.michael.com:7050 --tls --cafile $tlsfile -C michaelchannel -n michaelchaincode --peerAddresses peer0.org1.michael.com:7051 --tlsRootCertFiles $rootcrt1 --peerAddresses peer0.org2.michael.com:7051 --tlsRootCertFiles $rootcrt2 -c '{"Args":["invoke","michael","wang","300"]}'
# -o:orderer节点的地址+端口
# --tls:通信是否进行tls加密,参数后面什么都不写默认为true
# --cafile:orderer节点的msp下tlsca对应的证书文件
# -C:当前所处的通道ID
# -c:JSON格式的构造参数,默认值是“{}"。相当于给链码传递指令。
# -n:要调用的链码名称
# --peerAddresses:指定的背书节点(地址+端口)
# --tlsRootCertFiles:设定的背书节点的TLS根证书, 必须是绝对路径!
# 如果是多个背书节点,依次利用参数--peerAddresses和--tlsRootCertFiles向后罗列。
我上面的交易指令指定了peer0.org1和peer0.org2做背书节点,发起交易:由账户michael向账户wang转账300元。
当前客户端连接的是peer1.org1节点,最后查询一下交易后两个账户中的资金情况:
# 查询交易后michael账户的余额
$ peer chaincode query -n michaelchaincode -C michaelchannel -c '{"Args":["query","michael"]}'
# 查询交易后wang账户的余额
$ peer chaincode query -n michaelchaincode -C michaelchannel -c '{"Args":["query","wang"]}'
通过资产余额表明交易已成功!
最后来看一下本地的镜像仓库:
可见peer0.org1和peer0.org2节点由于被指定为背书节点,在接收到分发的交易请求后调用了链码。进而进行了容器的数据与逻辑的分离。具体解释可参见本帖第9部分。
至此,自定义的Fabric网络从零开始搭建成功!
12 历史遗留问题讲解
在docker-compose-cli.yaml前面有volums关键字挂载了5个数据卷。
base/docker-compose-base.yaml文件中的peer节点的数据卷挂载也都涉及到了这五个变量。
# 文件:base/docker-compose-base.yaml
version: '2'
services:
orderer.michael.com:
...
volumes:
- orderer.michael.com:/var/hyperledger/production/orderer
...
peer0.org1.michael.com:
...
volumes:
...
- peer0.org1.michael.com:/var/hyperledger/production
...
peer1.org1.michael.com:
...
volumes:
..
- peer1.org1.michael.com:/var/hyperledger/production
...
peer0.org2.michael.com:
...
volumes:
...
- peer0.org2.michael.com:/var/hyperledger/production
...
peer1.org2.michael.com:
...
volumes:
...
- peer1.org2.michael.com:/var/hyperledger/production
...
那这五个变量到底是什么?
利用docker volume ls
查看一下数据卷:
可见本地生成的数据卷文件的命名是工程路径名+前面定义的那五个变量。
继续利用docker volume inspect test_peer0.org1.michael.com
来进一步查看一下本地的数据卷:
可知,前面声明的数据卷变量peer0.org1.michael.com
挂载在宿主机的/var/lib/docker/volumes/test_peer0.org1.michael.com/_data
路径下。
进到这个路径中可以看到里面都是一些数据信息文件。
所以这是docker-compose配置文件中一种很特殊的挂载方式。
定义一些数据卷变量(而不是真正的宿主机的地址),然后挂载到容器某路径。该变量对应的宿主机路径不是我们人为指定的,而是默认地挂载到宿主机的/var/lib/docker/volumes/
路径下。
在我们自己写docker-compose配置文件时,这个变量映射部分可以不写。如果不做base/docker-compose-base.yaml文件中对应的数据卷映射,一些运行时的缓存信息就不会保存到宿主机中,并不会影响到我们容器的正常运行。
ps:
本人热爱图灵,热爱中本聪,热爱V神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人