参考资料:
在没有私有数据之前,在一个通道上的所有组织成员都可以访问通道账本上的数据(每个 peer 节点都是记账节点,需要记账)。如果通道中的一组组织想要对其他组织保持部分数据私密的话,它们可以选择创建一个新的通道,然后在这个新的通道上的组织可以访问这些数据。但是这种情况就会有新的通道的管理开销(维护链码版本,策略,MSP等),并且不允许在保持部分数据私有的同时,让所有通道参与者都看到交易。
Fabric 从 v1.2 开始提供了创建私有数据集合(Private Data Collection,PDC)的能力,这使得授权的组织对私有数据有访问的权利,而未授权的组织则没有权利访问私有数据
集合是两个元素的集合:
当在链码中引用私有数据集合时,为了保护私有数据在提案、背书和提交交易过程中的机密性,私有数据的交易流程略有不同。
transient
字段中(链码调用 GetTransient() 接口获取传进来的私有数据)transient data store
(节点的本地临时存储库)中。这些背书节点根据组织集合的策略将私有数据通过 Gossip 分发给授权的节点transient data store
,以确定它们是否在链码背书的时候已经接受到了私有数据。如果没有的话,它们会尝试从其他已授权节点那里拉取私有数据,然后对照公共区块上的哈希值来验证私有数据并提交交易和区块。当验证或提交结束后,私有数据会被移动到这些节点私有数据库和私有读写存储的副本中。随后 transient data store
中存储的这些私有数据会被删除(这个相当于只是一个临时的私有数据存储库)收集器定义描述了谁可以持有私有数据、数据要分发到多少个节点上、多少节点可以传播私有数据和私有数据要在私有数据库中存放多久。
收集器(Collection)的定义包括以下属性
属性 | 描述 |
---|---|
name | 收集器的名字 |
policy | 定义了可以持有收集器的组织节点 |
requiredPeerCount | 在节点为背书签名并将提案响应返回给客户端前,每个背书节点必须将私有数据分发到的节点(在被授权的组织当中)的最小数量。设为0代表分发不是必须的,但是如果 maxPeerCount 比0大的话就需要分发。通常不建议设置为0,因为那会造成在背书节点不可用的时候,网络中的私有数据可能会丢失。通常在背书的时候你会希望分发私有数据到多个节点保证网络中私有数据的冗余存储 |
maxPeerCount | 为了数据冗余,每个背书节点将会尝试将私有数据分发到的其他节点(在被授权的组织中) 的最大数量。如果在背书和提交之间一个背书节点不可用了,其他节点就可以在背书的时候从已经收到私有数据的节点拉取私有数据。如果这个值被设置为0,私有数据在背书的时候就不会分发,这会在提交的时候强制节点从授权的背书节点上拉取私有数据。 |
blockToLive | 私有数据要在私有数据库存放多久,以区块个数为单位,数据将在私有数据库中存在指定数量的区块数然后会被清除, 也就是数据会从网络中废弃。要永久保存私有数据,永远不被清除,就设置为0 |
memberOnlyRead | 值为 true 表示节点会自动强制只有属于收集器成员组织的客户端才有读取私有数据的权限。如果一个非成员组织的客户端试图执行一个链码方法来读取私有数据的话,会结束链码的调用并产生错误。如果你想在单独的链码方法中进行更细粒度的访问控制的话,可以使用 false 值。 |
官方的示例文件
// 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
}
]
在 collectionMarbles
中的 policy
属性 定义了允许通道中(Org1 和 Org2)所有成员使用私有数据库中的私有数据。 collectionMarblePrivateDetails
收集器只允许 Org1 的成员使用私有数据库中的私有数据。
由于私有数据不会被包含在提交到排序服务的交易中,因此也就不会被包含在区块中,背书节点扮演着将私有数据分发给其他授权组织的节点的重要角色。这确保了私有数据在背书节点完成背书之后变成不可用的时候的可用性。为了复制分发,在集合定义中的 maxPeerCount
和 requiredPeerCount
属性控制了在背书的时候分发的数量。
如果背书节点不能够成功地将私有数据分发到至少 requiredPeerCount
的要求,它将会返回一个错误给客户端。背书节点会尝试将私有数据分发到不同组织的节点,来确保每个被授权的组织具有私有数据的一个副本。因为交易在链码执行期间还没有被提交,背书节点和接收节点除了在它们的区块链之外,还在一个本地的 临时存储(transient store)
中存储了一个私有数据副本,直到交易被提交。
当一个被授权的节点在提交的时候,如果他们的临时存储中没有私有数据的副本(或者是因为他们不是一个背书节点,或者是因为他们在背书的时候没有接收到私有数据),他们会尝试从其他的被授权的节点那里拉取私有数据,尝试会持续一个可配置的时间长度 ,时间可以通过节点配置文件 core.yaml
中的属性 peer.gossip.pvtData.pullRetryThreshold
进行配置。
只有当提出请求的节点是私有数据分发策略定义的集合中的一员的时候,被询问的节点才会返回私有数据。
当使用 pullRetryThreshold
时候需要考虑的问题:
pullRetryThreshold
时间内拿到私有数据的话,它将会把交易提交到自己的账本(包括私有数据的哈希值),并且将私有数据存储在与其他的通道状态数据进行了逻辑隔离的状态数据库中。pullRetryThreshold
时间内拿到私有数据的话,它将会把交易提交到自己的账本(包括私有数据的哈希值),但是不会存储私有数据。因此,将 requiredPeerCount
和 maxPeerCount
设置成足够大的值来确保在你的通道中的私有数据的可用性是非常重要的。比如,如果在交易提交之前,每个背书节点都不可用了,requiredPeerCount
和 maxPeerCount
属性将会确保私有数据在其他的节点上是可用的。
我们可以用 shim APIS
设置和取回私有数据,需要指定相关的集合名字
PutPrivateData(collection,key,value)
GetPrivateData(collection,key)
链码提案中有个特殊字段 transient
,可以用它把私有数据从客户端传递给节点上的链码调用。链码可以通过 GetTransient()
来获取该字段。该字段会从通道交易中被排除。
直到1.3版本,基于集合成员的私有数据的访问控制仅限制在 Peer 节点。基于链码提案的提交者所在组织的访问控制需要编码在链码逻辑中。从v1.4开始,集合配置中的选项 memberOnlyRead
能够自动地强制使用基于链码提案提交者组织的访问控制。
如果你想要更细粒度的访问控制,你可以将
memberOnlyRead
设置为 false。然后你可以在链码中应用你自己的访问控制逻辑,比如通过调用链码 API GetCreator() 或者使用客户端身份。
私有集合数据能够像常见的通道数据那样使用 shim API 来进行查询:
GetPrivateDataByRange(collection, startKey, endKey string)
GetPrivateDataByPartialCompositeKey(collection, objectType string, keys []string)
对于 CouchDB 状态数据库,可以使用 shim API 查询 JSON 内容:
GetPrivateDataQueryResult(collection, query string)
在 Collection 配置的 blockToLive 字段可以指定周期性地删除私有数据,即私有数据可以存活几个区块。
在提交之前,私有数据存储在 Peer 节点的本地临时数据存储中。这些数据在交易提交之后会自动被删除。但是如果交易没有被提交,私有数据就会一直保存在临时数据存储中。Peer 节点会根据配置文件 core.yaml
中的 peer.gossip.pvtData.transientstoreMaxBlockRetention
的配置周期性的删除临时存储中的数据。
如果要升级一个集合定义或者增加一个新的定义,你可以将链码更新到新版本并将集合配置新的集合配置传递给链码更新交易。例如在 CLI 中使用 --collections-config
标示。如果在链码更新期间指定了集合配置,所有已存在的集合的定义也必须包含其中。
升级链码的时候,你可以新增和更新私有数据集合,例如向已存在的集合中添加新成员或者改变一个集合定一个的属性。注意,你不能更新集合名称和 blockToLive 属性,因为无论节点区块高度如何,都需要一个一致的 blockToLive 。
在一个 Peer 节点提交包含链码更新交易的区块时,集合的更新才会生效。注意,集合是不能够被删除的,因为在通道的区块链上可能有之前的私有数据的哈希,而这些哈希值是不能被删除的。
官方文档描述地再清楚不过了,我就不照搬了,直接看官方文档吧。
官方文档——在Fabric中使用私有数据