自Fabric1.2版本提出了隐私数据,后续版本进行了完善。此篇文章主要针对fabric2.0版本介绍隐私数据。
1.4.x版本:Fabric Hyperledger之隐私数据(Private data)
客户端提交提案: 客户端提交提案给背书节点(隐私集合授权的组织节点)注:私有数据通过proposal中的transient字段发送给背书节点。下面命令来自fabric-samples的marbles,可以看到- -transient参数。
export MARBLE=$(echo -n "{\"name\":\"marble1\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64 | tr -d \\n)
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marblesp -c '{"Args":["initMarble"]}' --transient "{\"marble\":\"$MARBLE\"}"
模拟交易执行: 背书节点模拟交易执行,过程中会将隐私数据存储在transient data store(Peer节点的临时本地存储),通过gossip协议及集合策略分发给其它拥有对应集合隐私数据权限的节点。
返回背书结果: 背书节点返回模拟执行的提案结果给客户端,需注意的一点是返回结果的读写集中会包含公共数据和隐私数据的hash,隐私数据本身是不返回给客户端的。
客户端提交给排序节点: 客户端将模拟执行的交易结果提交给排序节点,排序节点会将包含私有数据hash的块分发给所有背书节点(包括隐私集合授权节点和无权限的组织背书节点),由此通道的所有节点通过一致的方式,也就是隐私数据的hash去验证交易的合法性,而非隐私数据本身。
提交块: 在提交块时,授权节点通过隐私集合策略确定其是否拥有隐私数据的存储权限,对于授权节点来说,它们会先检查本地的临时存储来确认在交易背书时是否已接收到隐私数据;如果本地存储不存在隐私数据,即交易时节点未背书的情形,会尝试从其它时候节点通过gossip协议拉取隐私数据,然后通过公共块上的hash验证交易,验证通过会提交交易和块,然后私有数据会写入边数据库(sideDB),与此同时删除本地对应的临时存储。
fabric2.0版本会为通道中的每个组织定义一个隐私集合,从而无需用户定义。每个特定组织的隐式集合都具有匹配组织的分发策略和认可策略。
使用方法:编写链码时,PutPrivateData和GetPrivateData函数中,collection指定为_implicit_org_ +MSPID,例如_implicit_org_Org1MSP。
notes:应用程序定义的集合名称不允许以下划线开头,因此隐式集合名称不会与应用程序定义的集合名称冲突。
对于一些特殊情形,一个集合中的隐私数据可能需要共享给其它通道成员,例如当某个授权节点需要和通道中的某个或是某些未授权成员间进行私有数据的交易时。作为交易的一部分,会根据链上的hash的验证隐私数据。
作为理论支撑:
1.只要满足认可策略,非隐私数据集合中的成员也能写入隐私数据,可以在链码层面(背书策略),key层面(基于key的背书策略,注:状态数据库为key-value数据库),集合层(2.0新特性)面定义策略.
2.从1.4.2版本开始,提供了运行非隐私集合授权节点读取私有数据hash的API: GetPrivateDataHash()
当然也可以定义各种隐私集合,缺点是可能需要定义很多的隐私集合,fabric2.0为每个组织提供了隐式集合,无需用户再次定义,基于隐私数据共享,在一定程度上会减轻工作量。
除定义各种隐私集合外,可以通过多种方式共享和传输隐私数据:
公私钥加密: 传统做法,此处不再赘述
链码层面控制: 可以在链码中实现访问控制,指定哪些客户端可以查询集合中的私有数据。例如,在链码中获取客户端提交者的凭据(使用GetCreator()链码API或CID库API GetID()或GetMSPID())以验证其权限。
与其他集合共享私有数据: 可以使用链码在链上“共享”私有数据,从而在另一个组织的私有数据集合中创建匹配的键/值。 你可以通过transient 字段将私有数据键/值传递给链码,并且链码可以使用GetPrivateDataHash()确认所传递的私有数据的哈希值与你集合中的链上哈希值匹配,然后将私有数据写入其他组织的隐私数据集合。
隐私数据转移: 可以使用链码“转移”私有数据,先通过GetPrivateDataHash() API 进行隐私数据验证,然后再删除隐私集合中的数据,在其他隐私集合再次创建,从而实现了隐私数据的转移。此场景可能需要其它方的认可,例如监管者或审计师。
key层面的背书策略: 链码提供了相应的API:SetPrivateDataValidationParameter()
对于非常敏感的数据,即使共享私有数据的各方也可能希望(或政府法规可能要求)定期“清除”其Peer上的数据,仅保留链上隐私数据的hash。对于某些情况,隐私数据可能需要短暂存在Peer节点的sideDB,以便将私有数据转移到外部的数据库。当然也有些情况下,需要完成相应链码业务流程(交易结算,合同履行等)。
下面为一隐私集合定义文件
[{
"name": "collectionMedium",
"policy": "OR('Org1MSP.member', 'Org2MSP.member','Org3MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive": 1000000,
"memberOnlyRead": true
},
{
"name": "collectionPrivate",
"policy": "OR('Org1MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive": 5,
"memberOnlyRead": true
}
]
其中blockToLive属性:以块为单位,指定隐私数据在sideDB的存活时间,此处解释一下,假设某隐私数据交易所在区块号为commit_numer,伴随链上交易最新区块号为current_numer;条件current_numer>commit_numer+blockToLive满足时,隐私数据将会被清除,无法通过链码查询,也无法在节点上得到隐私数据,链上仅保留hash。如果想无限期地保留隐私数据,可将blockToLive属性设置成0。
在1.4版本,在peer节点实例化/升级链码,通过–collections-config指定collection文件的路径,从而提交集合定义给channel.
2.0版本提供了新的链码周期,当启用新的链码周期时,集合定义伴随链码commit( peer lifecycle chaincode commit)会提交给channel.
隐私集合同样可以创建索引,将索引定义文件(json格式)放置到META-INF/statedb/couchdb/collections/
参考样例indexOwner.json:
{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}