在 Fabric 中使用私有数据
本教程将带你练习在 Fabric 中定义、配置和使用私有数据。
关于私有数据的更多信息请参阅 私有数据 。
官方教程参考:https://hyperledger-fabric.readthedocs.io/zh_CN/release-1.4/private_data_tutorial.html
下边的图片阐明了弹珠私有数据示例所使用的私有数据模型。
收集器的 JSON 定义文件:
在通道中数据私有化的第一步是创建一个定义了私有数据权限的收集器。
收集器定义描述了谁可以持有数据、数据要分发到多少个节点上、多少节点可以传播私有数据 和私有数据要在私有数据库中存放多久。然后,我们将演示链码 API PutPrivateData 和 GetPrivateData 是如何将收集器映射到受保护的私有数据的。
收集器的定义包括一下属性:
- name: 收集器的名字。
- policy: 定义了可以持有数据收集器的组织节点。
- requiredPeerCount: 作为链码的背书条件,需要将私有数据传播到的节点数量。
- maxPeerCount: 为了数据冗余,现有背书节点需要尝试将数据分发到其他节点的数量。如果背书节点发生故障,当有请求提取私有数据时,则其他节点在提交时可用。
- blockToLive: 对于非常敏感的信息,比如价格或者个人信息,这个值表示在数据要以区块的形式在私有数据库中存放的时间。数据将在私有数据库中存在指定数量的区块数然后会被清除, 也就是数据会从网络中废弃。要永久保存私有数据,永远不被清除,就设置 blockToLive 为 0
- memberOnlyRead: 值为 true 则表示节点会自动强制只有属于收集器成员组织的客户端才有读取私有数据的权限。
为了说明私有数据的用法,弹珠私有数据示例包含了两个私有数据收集器的定义: collectionMarbles 和 collectionMarblePrivateDetails 。在 collectionMarbles 中的 policy 属性定义了允许通道中(Org1 和 Org2)所有成员使用私有数据库中的私有数据。 collectionMarblePrivateDetails 收集器只允许 Org1 的成员使用私有数据库中的私有数据。
创建策略定义的更多信息请参考 背书策略 主题。
// collections_config.json
[
{
"name": "collectionMarbles", //收集器的名字
"policy": "OR('Org1MSP.member', 'Org2MSP.member')", //定义了可以持有数据收集器的组织节点
"requiredPeerCount": 0, // 作为链码的背书条件,需要将私有数据传播到的节点数量
"maxPeerCount": 3, //为了数据冗余,现有背书节点需要尝试将数据分发到其他节点的数量
"blockToLive":1000000, //表示在数据要以区块的形式在私有数据库中存放的时间,数据将在私有数据库中存在指定数量的区块数然后会被清除
"memberOnlyRead": true //值为true则表示节点会自动强制只有属于收集器成员组织的客户端才有读取私有数据的权限
},
{
"name": "collectionMarblePrivateDetails",
"policy": "OR('Org1MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":3, //表示在数据要从其生成开始,网络中增加3个区块后就被删除
"memberOnlyRead": true
}
]
总结一下,上边我们为 collection.json 定义的策略允许 Org1 和 Org2 的所有节点在他们的私有数据库中存储和交易弹珠的私有数据 name, color, size, owner 。 但是只有 Org1 的节点可以在他的私有数据库中存储和交易 price 私有数据。
数据私有的一个额外的好处是,当使用了收集器以后,只有私有数据的哈希会通过排序节点, 而不是私有数据本身,从排序方面保证了私有数据的机密性。
具体操作开始:
1、安装fabric 1.4.0
参考教程:https://blog.csdn.net/glclh/article/details/108184471
2、修改docker-compose-cli.yaml文件(与官方教程最大的不同)
每个客户端每次只能操作一个peer节点,如果更改客户端连接的peer节点,必须同时更改相应的环境变量,否则客户端无法与其所连接的peer节点通信。
peer0.org1.example.com 对应cli客户端
peer1.org1.example.com 对应cli11客户端
peer0.org2.example.com 对应cli02客户端
peer1.org2.example.com 对应cli12客户端
在docker-compose-cli.yaml文件末尾添加以下内容:
cli11:
container_name: cli11
image: hyperledger/fabric-tools:$IMAGE_TAG
#是否存在终端
tty: true
#是否使用终端标准输入
stdin_open: true
environment:
#客户端容器启动后,go的工作目录
- GOPATH=/opt/gopath
#容器启动后,对应的守护进程的本地套接字
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
#日志级别
#- FABRIC_LOGGING_SPEC=DEBUG
- FABRIC_LOGGING_SPEC=INFO
#当前客户端节点的ID
- CORE_PEER_ID=cli11
#客户端当前连接的peer节点
- CORE_PEER_ADDRESS=peer1.org1.example.com:7051
#客户端连接的peer节点所属组织的ID
- CORE_PEER_LOCALMSPID=Org1MSP
#通信是否使用tls加密
- CORE_PEER_TLS_ENABLED=true
#客户端连接的peer节点的三个文件
#证书文件
- CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.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/peer1.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/peer1.org1.example.com/tls/ca.crt
#当前客户端的身份文件,指定客户端的身份
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/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
cli02:
container_name: cli02
image: hyperledger/fabric-tools:$IMAGE_TAG
tty: true
stdin_open: true
environment:
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
#- FABRIC_LOGGING_SPEC=DEBUG
- FABRIC_LOGGING_SPEC=INFO
- CORE_PEER_ID=cli02
- CORE_PEER_ADDRESS=peer0.org2.example.com:7051
- CORE_PEER_LOCALMSPID=Org2MSP
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.key
- 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
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/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
cli12:
container_name: cli12
image: hyperledger/fabric-tools:$IMAGE_TAG
tty: true
stdin_open: true
environment:
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
#- FABRIC_LOGGING_SPEC=DEBUG
- FABRIC_LOGGING_SPEC=INFO
- CORE_PEER_ID=cli12
- CORE_PEER_ADDRESS=peer1.org2.example.com:7051
- CORE_PEER_LOCALMSPID=Org2MSP
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/server.key
- 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.crt
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/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
3、启动网络
在安装和初始化弹珠私有数据链码之前,我们需要启动 BYFN 网络。下边的命令会关闭所有活动状态的或者存在的 docker 容 器并删除之前生成的构件。让我们运行下边的命令来清理之前的环境:
cd ~/go/src/github.com/hyperledger/fabric-samples/first-network
./byfn.sh down
如果你之前运行过本教程,你需要删除弹珠私有数据链码的 docker 容器。让我们运行下边 的命令清理之前的环境:
docker rm -f $(docker ps -a | awk '($2 ~ /dev-peer.*.marblesp.*/) {print $1}')
docker rmi -f $(docker images | awk '($1 ~ /dev-peer.*.marblesp.*/) {print $3}')
运行下边的命令来启动使用了 CouchDB 的 BYFN 网络:
详细参数可通过./byfn.sh help查看相关命令
./byfn.sh up -c mychannel -s couchdb
这会创建一个简单的 Fabric 网络,包含一个名为 mychannel 的通道,其中有两个组织 (每个组织有两个 peer 节点)和一个排序服务,还有四个客户端(cli, cli11, cli02, cli12),每个peer节点对应一个客户端,同时使用 CouchDB 作为状态数据库。LevelDB 或者 CouchDB 都可以使用收集器。这里使用 CouchDB 来演示。
4、安装和初始化带有收集器的链码
客户端应用通过链码和区块链账本交互。所以我们需要在每一个要执行和背书交易的节点上安装和初始化链码。链码安装在节点上然后在通道上使用 peer-commands 进行初始化。
4.1 在所有节点上安装链码
BYFN 网络包含两个组织, Org1 和 Org2 ,每个组织有两个节点。所以链码需要安装在四个节点上:
- peer0.org1.example.com
- peer1.org1.example.com
- peer0.org2.example.com
- peer1.org2.example.com
进入 CLI 容器:
docker exec -it cli bash
你的终端会变成类似这样的:
root@81eac8493633:/opt/gopath/src/github.com/hyperledger/fabric/peer#
- 使用下边的命令在 BYFN 网络上,安装弹珠链码到节点
peer0.org1.example.com
(默认情况下,启动 BYFN 网络以后,激活的节点被设置成了CORE_PEER_ADDRESS=peer0.org1.example.com:7051
):
peer chaincode install -n marblesp -v 1.0 -p github.com/chaincode/marbles02_private/go/
当完成之后,你会看到类似输出:
install -> INFO 003 Installed remotely response:<status:200 payload:"OK"
2. 切换当前节点为 Org1 的第二个节点并安装链码。执行下边的命令:
退出cli容器
exit
进入cli11容器
docker exec -it cli11 bash
安装链码
peer chaincode install -n marblesp -v 1.0 -p github.com/chaincode/marbles02_private/go/
果按照官方教程来操作,会报错(部分环境变量未修改),链码安装失败。类似的,官方教程后面的操作也会出现类似错误。本教程后面的操作就略过官方教程的操作,仅对本教程的操作进行阐述。
Error: error getting endorser client for install: endorser client failed to connect to peer1.org1.example.com:8051: failed to create new connection: context deadline exceeded
3. 给Org2的节点安装链码
退出之前的容器
exit
进入cli02容器
docker exec -it cli02 bash
安装链码
peer chaincode install -n marblesp -v 1.0 -p github.com/chaincode/marbles02_private/go/
退出之前的容器
exit
进入cli12容器
docker exec -it cli12 bash
安装链码
peer chaincode install -n marblesp -v 1.0 -p github.com/chaincode/marbles02_private/go/
参数说明:
- -n: 指定要安装的链码的名称
- -v: 指定链码的版本
- -p: 指定要安装的链码的所在路径
4.2 在通道上初始化链码
可以在任一cli容器中执行
export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C mychannel -n marblesp -v 1.0 -c '{"Args":["init"]}' -P "OR('Org1MSP.member','Org2MSP.member')" --collections-config $GOPATH/src/github.com/chaincode/marbles02_private/collections_config.json
参数说明:
-o: 指定Oderer服务节点地址
–tls: 开启 TLS 验证
–cafile: 指定 TLS_CA 证书的所在路径
-n: 指定要实例化的链码名称,必须与安装时指定的链码名称相同
-v: 指定要实例化的链码的版本号,必须与安装时指定的链码版本号相同
-C: 指定通道名称
-c: 实例化链码时指定的参数
-P: 指定背书策略
注解
当指定了 --collections-config 的时候,你需要指明 collections_config.json 文件完整清晰的路径。 例如: --collections-config $GOPATH/src/github.com/chaincode/marbles02_private/collections_config.json
当成功初始化完成的时候,你可能看到类似下边这些:
[chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
[chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
5、存储私有数据
以 Org1 成员的身份操作,Org1 的成员被授权可以交易弹珠私有数据示例中的所有私有数据,切换回 Org1 的节点并提交一个增加一个弹珠的请求:
退出之前的容器
exit
进入cli容器
docker exec -it cli bash
存储私有数据
export MARBLE=$(echo -n "{\"name\":\"marble1\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64 | tr -d \\n)
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 marblesp -c '{"Args":["initMarble"]}' --transient "{\"marble\":\"$MARBLE\"}"
调用 initMarble 函数来创建一个带有私有数据的弹珠 — 名字为 marble1 , 拥有者为 tom ,颜色为 blue ,尺寸为 35 ,价格为 99 。重申一下,私有数据 price 将会和私有数据 name, owner, color, size 分开存储。因为这个原因, initMarble 函数存储私有数据的时候调用两次 PutPrivateData() API ,每个收集器一次。同样要注意到,私有数据传输的时候使用了 --transient 标识。为了保证数据的隐私性,作为临时数据传递的输入不会保存在交易中(暂态数据)。临时数据以二进制的方式传输, 但是当使用 CLI 的时候,必须先进行 base64 编码。我们使用一个环境变量来获得 base64 编码的值。
你应该会看到类似下边的结果:
[chaincodeCmd] chaincodeInvokeOrQuery->INFO 001 Chaincode invoke successful. result: status:200
6、查询私有数据
我们收集器的定义允许 Org1 和 Org2 的所有成员在他们的侧数据库中使用 name, color, size, owner 私有数据,但是只有 Org1 的节点可以在他们的侧数据库中保存 price 私有数据。作为一个 Org1 中的授权节点,我们将查询两个私有数据集合。
6.1 以 Org1 成员的身份查询私有数据
以 Org1 成员的身份查询 marble1 的私有数据 name, color, size and owner 。
在cli容器中操作
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarble","marble1"]}'
你应该会看到如下结果:
{"color":"blue","docType":"marble","name":"marble1","owner":"tom","size":35}
以 Org1 成员的身份查询 marble1
的私有数据 price
。
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
你应该会看到如下结果:
{"docType":"marblePrivateDetails","name":"marble1","price":99}
6.2 以 Org2成员的身份查询私有数据
退出之前的容器
exit
进入cli02容器
docker exec -it cli02 bash
Org2 的节点在它们的数据库中有弹珠私有数据的第一个集合 ( name, color, size and owner
) 并且有权限使用 readMarble()
函数和 collectionMarbles
参数访问它。
查询 Org2 有权访问的私有数据:
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarble","marble1"]}'
你应该会看到类似下边的输出结果:
{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}
查询 Org2 没有权限的私有数据
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
在 Org2 的节点侧数据库中没有弹珠的私有数据 price
。当它们尝试查询这个数据的时候, 它们会得到符合公共状态键的哈希但是得不到私有数据。你应该会看到如下结果:
{"Error":"Failed to get private details for marble1: GET_STATE failed:
transaction ID: b04adebbf165ddc90b4ab897171e1daa7d360079ac18e65fa15d84ddfebfae90:
Private data matching public hash version is not available. Public hash
version = &version.Height{BlockNum:0x6, TxNum:0x0}, Private data version =
(*version.Height)(nil)"}
Org2 的成员只能看到私有数据的公共哈希。
7、清除私有数据(暂态数据的演示)
对于一些案例,私有数据仅需在账本上保存到在链下数据库复制之后就可以了,我们可以将数据在过了一定数量的区块后进行 “清除”,仅仅把数据的哈希作为不可篡改的证据保存下来。
私有数据可能会包含私人的或者机密的信息,比如我们例子中的价格数据,这是交易伙伴不想 让通道中的其他组织知道的。但是,它具有有限的生命周期,就可以根据收集器定义中的,在固定的区块数量之后清除。
我们的 collectionMarblePrivateDetails 中定义 blockToLive 属性的值为 3 , 表明这个数据会在侧数据库中保存三个区块的时间,之后它就会被清除。将所有内容放在一 起,回想一下绑定了私有数据 price 的收集器 collectionMarblePrivateDetails , 在函数 initMarble() 中,当调用 PutPrivateData() API 并传递了参数 collectionMarblePrivateDetails 。
我们将从在链上增加区块,然后来通过执行四笔新交易(创建一个新弹珠,然后转移三个 弹珠)看一看价格信息被清除的过程,增加新交易的过程中会在链上增加四个新区块。在 第四笔交易完成之后(第三个弹珠转移后),我们将验证一下价格数据是否被清除了。
先进入cli容器:
退出之前的容器
exit
进入cli容器
docker exec -it cli bash
打开一个新终端窗口,通过运行如下命令来查看这个节点上私有数据日志:
docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata'
你将看到类似下边的信息。注意列表中最高的区块号。在下边的例子中,当前最高的区块高度是 6
。
返回到节点容器,运行如下命令查询 marble1 的价格数据:
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
你将看到类似下边的信息:
{"docType":"marblePrivateDetails","name":"marble1","price":99}
price
数据仍然在私有数据账本上。
通过执行如下命令创建一个新的 marble2 。这个交易将在链上创建一个新区块。
export MARBLE=$(echo -n "{\"name\":\"marble2\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64 | tr -d \\n)
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 marblesp -c '{"Args":["initMarble"]}' --transient "{\"marble\":\"$MARBLE\"}"
再次切换回终端窗口并查看节点的私有数据日志。你将看到区块高度增加了 1 。
docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata'
返回到节点容器,再次运行如下命令查询 marble1 的价格数据:
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
私有数据没有被清除,之前的查询也没有改变查询结果:
{"docType":"marblePrivateDetails","name":"marble1","price":99}
运行下边的命令将 marble2 转移给 “joe” 。这个交易将使链上增加第二个区块。
export MARBLE_OWNER=$(echo -n "{\"name\":\"marble2\",\"owner\":\"joe\"}" | base64 | tr -d \\n)
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 marblesp -c '{"Args":["transferMarble"]}' --transient "{\"marble_owner\":\"$MARBLE_OWNER\"}"
再次切换回终端窗口并查看节点的私有数据日志。你将看到区块高度增加了 1 。
docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata'
返回到节点容器,再次运行如下命令查询 marble1 的价格数据:
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
将看到价格私有数据。
{"docType":"marblePrivateDetails","name":"marble1","price":99}
运行下边的命令将 marble2 转移给 “tom” 。这个交易将使链上增加第三个区块。
export MARBLE_OWNER=$(echo -n "{\"name\":\"marble2\",\"owner\":\"tom\"}" | base64 | tr -d \\n)
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 marblesp -c '{"Args":["transferMarble"]}' --transient "{\"marble_owner\":\"$MARBLE_OWNER\"}"
再次切换回终端窗口并查看节点的私有数据日志。你将看到区块高度增加了 1 。
docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata'
返回到节点容器,再次运行如下命令查询 marble1 的价格数据:
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
你将看到价格数据。
{"docType":"marblePrivateDetails","name":"marble1","price":99}
最后,运行下边的命令将 marble2 转移给 “jerry” 。这个交易将使链上增加第四个区块。在 此次交易之后, price
私有数据将会被清除。
export MARBLE_OWNER=$(echo -n "{\"name\":\"marble2\",\"owner\":\"jerry\"}" | base64 | tr -d \\n)
> 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 marblesp -c '{"Args":["transferMarble"]}' --transient "{\"marble_owner\":\"$MARBLE_OWNER\"}"
再次切换回终端窗口并查看节点的私有数据日志。你将看到区块高度增加了 1 。
docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata'
返回到节点容器,再次运行如下命令查询 marble1 的价格数据:
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
因为价格数据已经被清除了,你就查询不到了。你应该会看到类似下边的结果:
Error: endorsement failure during query. response: status:500
message:"{\"Error\":\"Marble private details does not exist: marble1\"}"
8、使用私有数据索引
索引也可以用于私有数据收集器,可以通过打包链码旁边的索引 META-INF/statedb/couchdb/collections/<collection_name>/indexes 来使用。有一个索引的例子在 这里 。
在生产环境下部署链码时,建议和链码一起定义索引,这样当链码在通道中的节点上安 装和初始化时就可以自动作为一个单元进行安装。当使用 --collections-config 标识 指定收集器 JSON 文件路径时,通道上链码初始化的时候相关的索引会自动被部署。