本教程将演示收集器(collection)的使用,收集器为区块链网络上已授权的组织节点 提供私有数据的存储和检索。

本教程假设您已了解私有数据的存储和他们的用例。更多的信息请参阅 私有数据 。

本教程将带你通过以下步骤练习在 Fabric 中定义、配置和使用私有数据:

  1. 创建一个收集器的 JSON 定义文件
  2. 使用链码 API 读写私有数据
  3. 安装和初始化带有收集器的链码
  4. 存储私有数据
  5. 使用一个授权节点查询私有数据
  6. 以授权节点的身份查询私有数据
  7. 清除私有数据
  8. 使用私有数据索引
  9. 其他资源

本教程将使用 弹珠私有数据示例(marbles private data sample) --- 运行在“构建你的第一个网络(BYFN)”教程的网络上 --- 来演示创建、部署和使用私有数 据收集器。弹珠私有数据示例将部署在 构建你的第一个网络 (BYFN)教程的网络上。你 需要先完成 安装示例、二进制文件和 Docker 镜像 任务;但是在本教程中不需要运行 BYFN 教程。除了本教程中提 供的使用网络所必须的命令,我们还会讲解每一步都发生了什么,让你不运行示例也可以理解 每一步的意义。

创建一个收集器的 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
}, {
"name": "collectionMarblePrivateDetails",
"policy": "OR('Org1MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":3,
"memberOnlyRead": true
}
]

被这些策略保护的数据会被映射到链码,教程的后边会进行介绍。

当和它关联的链码在通道上参照 节点链码初始化命令(peer chaincode instantiate command) 初始化以后,这个收集器定义文件会被部署到通道上。更多的细节会在下边的三个部分讲解。

使用链码 API 读写私有数据

理解如何在通道上私有化数据的下一步工作是构建链码的数据定义。弹珠私有数据示例根据数 据的使用权限将私有数据分成了两个部分。

// Peers in Org1 and Org2 will have this private data in a side database
type marble struct {
ObjectType string `json:"docType"`
Name string `json:"name"`
Color string `json:"color"`
Size int `json:"size"`
Owner string `json:"owner"`
} // Only peers in Org1 will have this private data in a side database
type marblePrivateDetails struct {
ObjectType string `json:"docType"`
Name string `json:"name"`
Price int `json:"price"`
}

私有数据的特定权限将会被限制为如下:

  • name, color, size, and owner 通道中所有成员可见(Org1 and Org2)
  • price 只有 Org1 的成员可见

在弹珠私有数据示例中定义了两个不同的私有数据收集器。数据映射到收集器策略(权 限限制)是通过链码 API 控制的。特别地,使用收集器定义进行读和写私有数据是通过调用 GetPrivateData() 和 PutPrivateData() 来实现的,你可以在 这里 找到。

下边的图片阐明了弹珠私有数据示例所使用的私有数据模型。

 

读取收集器数据

使用链码 API GetPrivateData() 来查询数据库中的私有数据。 GetPrivateData() 需要两个参数, 收集器名 和数据的键值。再说一下收集器 collectionMarbles 允许 Org1 和 Org2 的成员使用侧数据库中的私有数据,收集器 collectionMarblePrivateDetails 只允许 Org1 的成员使用侧数据库中的私有数据。详情请参阅下边的两个 弹珠私有数据函数(marbles private data functions) :

  • readMarble 用于查询 name, color, size and owner 属性的值
  • readMarblePrivateDetails 用于查询 price 属性的值

本教程后边在节点上执行数据库查询的命令时,我们就是调用这两个函数。

写入私有数据

使用链码 API PutPrivateData() 将私有数据存入私有数据库。这个 API 同样需要收集器的 名字。因为弹珠私有数据示例包含两个不同的收集器,它在链码中会被调用两次:

  1. 使用名为 collectionMarbles 的收集器写入私有数据 name, color, size and owner 。
  2. 使用名为 collectionMarblePrivateDetails 的收集器写入私有数据 price 。

例如,在下边的 initMarble 函数片段中, PutPrivateData() 被调用了两次, 每个私有数据集合各一次。

// ==== Create marble object, marshal to JSON, and save to state ====
marble := &marble{
ObjectType: "marble",
Name: marbleInput.Name,
Color: marbleInput.Color,
Size: marbleInput.Size,
Owner: marbleInput.Owner,
}
marbleJSONasBytes, err := json.Marshal(marble)
if err != nil {
return shim.Error(err.Error())
} // === Save marble to state ===
err = stub.PutPrivateData("collectionMarbles", marbleInput.Name, marbleJSONasBytes)
if err != nil {
return shim.Error(err.Error())
} // ==== Create marble private details object with price, marshal to JSON, and save to state ====
marblePrivateDetails := &marblePrivateDetails{
ObjectType: "marblePrivateDetails",
Name: marbleInput.Name,
Price: marbleInput.Price,
}
marblePrivateDetailsBytes, err := json.Marshal(marblePrivateDetails)
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutPrivateData("collectionMarblePrivateDetails", marbleInput.Name, marblePrivateDetailsBytes)
if err != nil {
return shim.Error(err.Error())
}

总结一下,上边我们为 collection.json 定义的策略允许 Org1 和 Org2 的所有 节点在他们的私有数据库中存储和交易弹珠的私有数据 name, color, size, owner 。 但是只有 Org1 的节点可以在他的私有数据库中存储和交易 price 私有数据。

数据私有的一个额外的好处是,当使用了收集器以后,只有私有数据的哈希会通过排序节点, 而不是私有数据本身,从排序方面保证了私有数据的机密性。

启动网络

现在我们准备通过一些命令来演示使用私有数据。

Try it yourself

在安装和初始化弹珠私有数据链码之前,我们需要启动 BYFN 网络。为了本教程,我们需要 在一个已知的初始化环境下操作。下边的命令会关闭所有活动状态的或者存在的 docker 容 器并删除之前生成的构件。让我们运行下边的命令来清理之前的环境:

cd 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 up -c mychannel -s couchdb

这会创建一个简单的 Fabric 网络,包含一个名为 mychannel 的通道,其中有两个组织 (每个组织有两个 peer 节点)和一个排序服务,同时使用 CouchDB 作为状态数据库。LevelDB 或者 CouchDB 都可以使用收集器。这里使用 CouchDB 来演示如何对私有数据进行索引。

注解

为了让收集器能够工作,正确配置跨组织的 gossip 是很重要的。参考文档 Gossip 数据传播协议 , 重点关注 "锚节点" 部分。我们的教程不关注 gossip ,它已经在 BYFN 示例中配置过了, 但是当配置通道的时候,gossip 锚节点的配置对于收集器的正常工作是很重要的。

安装和初始化带有收集器的链码

客户端应用通过链码和区块链账本交互。所以我们需要在每一个要执行和背书交易的节点 上安装和初始化链码。链码安装在节点上然后在通道上使用 peer-commands 进行初始化。

在所有节点上安装链码

就像上边讨论的,BYFN 网络包含两个组织, Org1 和 Org2 ,每个组织有两个节点。所以 链码需要安装在四个节点上:

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

使用 peer chaincode install 命令在每一个节点上安装弹珠链码。

Try it yourself

如果你已经启动了 BYFN 网络,进入 CLI 容器。

docker exec -it cli bash

你的终端会变成类似这样的:

root@81eac8493633:/opt/gopath/src/github.com/hyperledger/fabric/peer#

  1. 使用下边的命令在 BYFN 网络上,安装 git 仓库的弹珠链码到节点 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. 利用 CLI 切换当前节点为 Org1 的第二个节点并安装链码。复制和粘贴下边的命令 到 CLI 容器并运行他们。

    export CORE_PEER_ADDRESS=peer1.org1.example.com:8051
    peer chaincode install -n marblesp -v 1.0 -p github.com/chaincode/marbles02_private/go/
  3. 利用 CLI 切换到 Org2 。复制和粘贴下边的一组命令到节点容器并执行。

    export CORE_PEER_LOCALMSPID=Org2MSP
    export PEER0_ORG2_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
    export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
    export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
  4. 切换当前节点为 Org2 的第一个节点并安装链码:

    export CORE_PEER_ADDRESS=peer0.org2.example.com:9051
    peer chaincode install -n marblesp -v 1.0 -p github.com/chaincode/marbles02_private/go/
  5. 切换当前节点为 Org2 的第二个节点并安装链码:

    export CORE_PEER_ADDRESS=peer1.org2.example.com:10051
    peer chaincode install -n marblesp -v 1.0 -p github.com/chaincode/marbles02_private/go/

在通道上初始化链码

使用 peer chaincode instantiate 命令在通道上初始化弹珠链码。为了在通道上配置链码收集器,使用 --collections-config 标识来指定收集器的 JSON 文件,我们的示例中是 collections_config.json 。

Try it yourself

在 BYFN 的 mychannel 通道上运行下边的命令来初始化弹珠私有数据链码。

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

注解

当指定了 --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

存储私有数据

以 Org1 成员的身份操作,Org1 的成员被授权可以交易弹珠私有数据示例中的所有私有数据,切换 回 Org1 的节点并提交一个增加一个弹珠的请求:

Try it yourself

复制并粘贴下边的一组命令到 CLI 命令行。

export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID=Org1MSP
export 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
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export PEER0_ORG1_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt

调用 initMarble 函数来创建一个带有私有数据的弹珠 --- 名字为 marble1 , 拥有者为 tom ,颜色为 blue ,尺寸为 35 ,价格为 99 。重申一下,私 有数据 price 将会和私有数据 name, owner, color, size 分开存储。因为这个原 因, initMarble 函数存储私有数据的时候调用两次 PutPrivateData() API ,每个 收集器一次。同样要注意到,私有数据传输的时候使用了 --transient 标识。为了保证 数据的隐私性,作为临时数据传递的输入不会保存在交易中。临时数据以二进制的方式传输, 但是当使用 CLI 的时候,必须先进行 base64 编码。我们使用一个环境变量来获得 base64 编码的值。 .. code:: 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\"}"

你应该会看到类似下边的结果:

[chaincodeCmd] chaincodeInvokeOrQuery->INFO 001 Chaincode invoke successful. result: status:200

使用一个授权节点查询私有数据

我们收集器的定义允许 Org1 和 Org2 的所有成员在他们的侧数据库中使用 name, color, size, owner 私有数据,但是只有 Org1 的节点可以在他们的侧数据库中保存 price 私有数据。作为一个 Org1 中的授权节点,我们将查询两个私有数据集合。

第一个 query 命令调用传递了 collectionMarbles 作为参数的 readMarble 函数。

// ===============================================
// readMarble - read a marble from chaincode state
// =============================================== func (t *SimpleChaincode) readMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var name, jsonResp string
var err error
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")
} name = args[0]
valAsbytes, err := stub.GetPrivateData("collectionMarbles", name) //get the marble from chaincode state if err != nil {
jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
return shim.Error(jsonResp)
} else if valAsbytes == nil {
jsonResp = "{\"Error\":\"Marble does not exist: " + name + "\"}"
return shim.Error(jsonResp)
} return shim.Success(valAsbytes)
}

第二个 query 命令调用传递了 collectionMarblePrivateDetails 作为参数 的 readMarblePrivateDetails 函数。

// ===============================================
// readMarblePrivateDetails - read a marble private details from chaincode state
// =============================================== func (t *SimpleChaincode) readMarblePrivateDetails(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var name, jsonResp string
var err error if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")
} name = args[0]
valAsbytes, err := stub.GetPrivateData("collectionMarblePrivateDetails", name) //get the marble private details from chaincode state if err != nil {
jsonResp = "{\"Error\":\"Failed to get private details for " + name + ": " + err.Error() + "\"}"
return shim.Error(jsonResp)
} else if valAsbytes == nil {
jsonResp = "{\"Error\":\"Marble private details does not exist: " + name + "\"}"
return shim.Error(jsonResp)
}
return shim.Success(valAsbytes)
}

Now Try it yourself

以 Org1 成员的身份查询 marble1 的私有数据 name, color, size and owner 。 注意,由于查询动作不记录在账本上,所以没必要将弹珠名作为临时输入传递。

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}

以授权节点的身份查询私有数据

现在我们将切换到 Org2 成员,在它的侧数据库中有弹珠私有数据的 name, color, size, owner ,但是没有私有数据 price 。我们将查询两个私有数据集合。

切换到 Org2 的节点

在 docker 容器内,运行下边的命令切换到有权限访问弹珠私有数据 price 的节点。

Try it yourself

export CORE_PEER_ADDRESS=peer0.org2.example.com:9051
export CORE_PEER_LOCALMSPID=Org2MSP
export PEER0_ORG2_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp

查询 Org2 有权访问的私有数据

Org2 的节点在它们的数据库中有弹珠私有数据的第一个集合 ( name, color, size and owner ) 并且有权限使用 readMarble() 函数和 collectionMarbles 参数访问它。

Try it yourself

peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarble","marble1"]}'

你应该会看到类似下边的输出结果:

{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}

查询 Org2 没有权限的私有数据

在 Org2 的节点侧数据库中没有弹珠的私有数据 price 。当它们尝试查询这个数据的时候, 它们会得到符合公共状态键的哈希但是得不到私有数据。

Try it yourself

peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'

你应该会看到如下结果:

{"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 的成员只能看到私有数据的公共哈希。

清除私有数据

对于一些案例,私有数据仅需在账本上保存到在链下数据库复制之后就可以了,我们可以将 数据在过了一定数量的区块后进行 “清除”,仅仅把数据的哈希作为不可篡改的证据保存下来。

私有数据可能会包含私人的或者机密的信息,比如我们例子中的价格数据,这是交易伙伴不想 让通道中的其他组织知道的。但是,它具有有限的生命周期,就可以根据收集器定义中的,在 固定的区块数量之后清除。

我们的 collectionMarblePrivateDetails 中定义 blockToLive 属性的值为 3 , 表明这个数据会在侧数据库中保存三个区块的时间,之后它就会被清除。将所有内容放在一 起,回想一下绑定了私有数据 price 的收集器 collectionMarblePrivateDetails , 在函数 initMarble() 中,当调用 PutPrivateData() API 并传递了参数 collectionMarblePrivateDetails 。

我们将从在链上增加区块,然后来通过执行四笔新交易(创建一个新弹珠,然后转移三个 弹珠)看一看价格信息被清除的过程,增加新交易的过程中会在链上增加四个新区块。在 第四笔交易完成之后(第三个弹珠转移后),我们将验证一下价格数据是否被清除了。

Try it yourself

使用如下命令切换到 Org1 的 peer0 。复制和粘贴下边的一组命令到节点容器并执行:

export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID=Org1MSP
export 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
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export PEER0_ORG1_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt

打开一个新终端窗口,通过运行如下命令来查看这个节点上私有数据日志:

docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata'

你将看到类似下边的信息。注意列表中最高的区块号。在下边的例子中,最高的区块高度是 4 。

[pvtdatastorage] func1 -> INFO 023 Purger started: Purging expired private data till block number [0]
[pvtdatastorage] func1 -> INFO 024 Purger finished
[kvledger] CommitWithPvtData -> INFO 022 Channel [mychannel]: Committed block [0] with 1 transaction(s)
[kvledger] CommitWithPvtData -> INFO 02e Channel [mychannel]: Committed block [1] with 1 transaction(s)
[kvledger] CommitWithPvtData -> INFO 030 Channel [mychannel]: Committed block [2] with 1 transaction(s)
[kvledger] CommitWithPvtData -> INFO 036 Channel [mychannel]: Committed block [3] with 1 transaction(s)
[kvledger] CommitWithPvtData -> INFO 03e Channel [mychannel]: Committed block [4] with 1 transaction(s)

你将看到类似下边的信息。注意列表中最高的区块号。在下边的例子中,最高的区块高度是 4 。

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\"}"

使用私有数据索引

索引也可以用于私有数据收集器,可以通过打包链码旁边的索引 META-INF/statedb/couchdb/collections/<collection_name>/indexes 来使用。有一个索引的例子在 这里 。

在生产环境下部署链码时,建议和链码一起定义索引,这样当链码在通道中的节点上安 装和初始化时就可以自动作为一个单元进行安装。当使用 --collections-config 标识 指定收集器 JSON 文件路径时,通道上链码初始化的时候相关的索引会自动被部署。

其他资源

这里有一个额外的私有数据学习的视频。

在 Fabric 中使用私有数据的更多相关文章

  1. Hyperledger Fabric私有数据

    官方文档:点这里 1简介 在同一个通道中,允许某一组织在对同一通道内其他组织保持部分的数据私有.也就是说有一部分被标识为私有的数据只能具有权限的组织查看和操作,而其余组织不具备查看和操作私有数据的权限 ...

  2. 160803、如何在ES6中管理类的私有数据

    如何在ES6中管理类的私有数据?本文为你介绍四种方法: 在类的构造函数作用域中处理私有数据成员 遵照命名约定(例如前置下划线)标记私有属性 将私有数据保存在WeakMap中 使用Symbol作为私有属 ...

  3. pthread线程私有数据

    进程内所有的线程共享地址空间,任何声明为静态或外部的变量,或在进程堆声明的变量都可以被进程内的所有线程读写. static,extern,或堆变量的值是上次线程改写的值 一个线程真正拥有的唯一私有存储 ...

  4. Fabric v2.0中的隐私数据

    文章来源于https://hyperledger-fabric.readthedocs.io/en/release-2.0/ 私有数据集在v1.4中提出,一直使用的是隐私数据集方式,即建立一个隐私数据 ...

  5. 线程私有数据TSD——一键多值技术,线程同步中的互斥锁和条件变量

    一:线程私有数据: 线程是轻量级进程,进程在fork()之后,子进程不继承父进程的锁和警告,别的基本上都会继承,而vfork()与fork()不同的地方在于vfork()之后的进程会共享父进程的地址空 ...

  6. 如何把遗留的Java应用托管在Service Fabric中

    一.概述 众所周知,微服务化尤其对遗留系统进行微服务化一般采用"Lift and Shift"的模式进行. Service Fabric作为一个微服务托管平台,不仅仅可以在上面跑. ...

  7. [Effective JavaScript 笔记]第35条:使用闭包存储私有数据

    js的对象系统并没有特别鼓励或强制信息隐藏.所有的属性名都是一个字符串,任意一个程序都可以简单地通过访问属性名来获取相应的对象属性.例如,for...in循环.ES5的Object.keys()和Ob ...

  8. C++中的static数据成员与static成员函数

    本文要点: 1.static成员它不像普通的数据成员,static数据成员独立于该类的任意对象而存在,每个static数据成员是与类关联的对象,并不与该类的对象相关联! aka:每个static数据成 ...

  9. Posix线程编程指南(2) 线程私有数据

    概念及作用 在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据.在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有.但有时应用程序设计中有必要提供 ...

随机推荐

  1. fastadmin 框架中图片点击放大

    fastadmin的原生图片预览,重新打开一个窗口太麻烦,使用layui做一个弹窗式的图片预览 1.将下面代码放在backend-init.js文件中 $('body').on('click', '[ ...

  2. python3练习100题——011

    原题链接:http://www.runoob.com/python/python-exercise-example11.html 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔 ...

  3. python 数组array的一些操作

    对一些特定大小的元素进行操作 1.将数组Arr中大于100的值都设定为100 Arr[Arr >100] = 100    利用array索引的内置 numpy.minimum(Arr, 100 ...

  4. 第k个数(排序)

    给定一个长度为n的整数数列,以及一个整数k,请用快速选择算法求出数列的第k小的数是多少. 输入格式 第一行包含两个整数 n 和 k. 第二行包含 n 个整数(所有整数均在1~109109范围内),表示 ...

  5. UI高级组件

    今天继续学习UI高级组件 网格视图 GridView 用GridView标签添加,显示网格视图,需要用到适配器,共有四种适配器 ArrayAdapter,SmipleAdapter,SmipleCou ...

  6. 如何做好 Android 端音视频测试?

    在用户眼中,优秀的音视频产品应该具有清晰.低延时.流畅.秒开.抗丢包.高音效等特征.为了满足用户以上要求,网易云信的工程师通过自建源站,在SDK端为了适应网络优化进行QoS优化,对视频编码器进行优化, ...

  7. [lua]紫猫lua教程-命令宝典-L1-01-07. table表

    L1[table]01. table表的定义与赋值 小知识:声明表的例子 xx={}--创建一个空表xx --给这表的元素赋值 test="a" xx[test]="a& ...

  8. Go_sql注入

    我们任何时候都不应该自己拼接SQL语句! sqlInjectDemo("xxx' or 1=1#") sqlInjectDemo("xxx' union select * ...

  9. vue $router.push 传参的问题

    $router 和 $route的区别 $route为当前router跳转对象里面可以获取name.path.query.params等 $router为VueRouter实例,想要导航到不同URL, ...

  10. 395. 至少有K个重复字符的最长子串

    Q: A: 分治,对于字符串s的任何一个字符,如果它的频数(在s中出现的次数)小于k,则它一定不会出现在最后的结果里,也就是从它的位置一劈两半,考察左右.对于当前字符串s,我们先建立字典统计其中每种字 ...