Private Data Collection Policy: Demonstrating Members-Only Read and Write Features
By: Kc Tam
Tags: Hyperledger Fabric, Private Data, Private Data Collection, Enterprise Blockchain

Overview

According to fabric documentation, private data collection policy allows more granular access control from non-policy members. In most cases it is desired that only policy members can read and write private data collection, while non-policy members cannot. There are still certain cases we have this need. In this article we will take a look on these two options, and observe the behaviour through some testing scenarios.

Test Setup

We are using the same setup and testing chaincode from a previous article about testing private data.

Basically it is a fabric network of three peer organizations, Org1, Org2 and Org3. Each comes with a peer node (peer0). A channel mychannel is created and joined by all peers. A testing chaincode sacc_private3org.go is created. It is adopted from sacc , with two chaincode functions setPrivate() and getPrivate() defined. The private data collection we coded in the chaincode is privateall.

After a network is up and mychannel is joined by all peers, we install the chaincode into all the three peers. During approval, we refer collection/org1–2.json as the collection definition file. Per the name suggested, this collection policy for privateall is set to Org1 and Org2. Which means private data is kept in peers of Org1 and Org2, while peer of Org3 only sees the hash of the private data.

For our test purpose, we will tune the following two options: membersOnlyRead and membersOnlyWrite. We will observe the following three scenarios.

membersOnlyRead: true, membersOnlyWrite: true membersOnlyRead: false, membersOnlyWrite: true membersOnlyRead: true, membersOnlyWrite: false

The overall testing flow is like this.

Bring up fabric network (network components, channel and anchor peer) Package chaincode and install chaincode package to all peers Approve chaincode definition with a private data collection file Commit chaincode definition Invoke setPrivate() for setting a key-value in the private data Get private data in local ledger for each of three organizations: only Org1 and Org2 can see the data Use Org3 to query getPrivate() Use Org3 to invoke setPrivate()

We will omit most steps and only focus on the steps relevant to our testing. If you wish to understand more in detail you can refer to my previous article.

For each scenario we will update the collection file in step 3. And our observation is mainly on step 7 and 8. Concretely, we will use CLI with Org3 setting to perform query and invoke, specifying collection policy members as endorser or target peer, and observe the result.

Test Step

Follow the Preparation of Test Step in my previous article.

Scenario 1

membersOnlyRead: true

membersOnlyWrite: true

After everything is rightly set and the first chaincode invoking is done, we observe the getPrivate() from each organization.

We see things are working well as expected. Org1 and Org2 can get the private data, and Org3 cannot.

Now we focus on Org3. First we query getPrivate() function and specify the peer of Org1 to process this query.

docker exec -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp -e CORE_PEER_ADDRESS=peer0.org3.example.com:11051 -e CORE_PEER_LOCALMSPID="Org3MSP" -e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt cli peer chaincode query -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 --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt -C mychannel -n mycc -c '{"Args":["getPrivate","name"]}'

We get back the same error message. Org3 does not have read access permission in this collection.

Then we invoke setPrivate() from Org3, specifying peers of Org1 and Org2 as endorsers.

docker exec -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp -e CORE_PEER_ADDRESS=peer0.org3.example.com:11051 -e CORE_PEER_LOCALMSPID="Org3MSP" -e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt cli peer chaincode invoke -o orderer.example.com:7050 --tls true --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 mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["setPrivate","name","Bob"]}'

We see the error message from the chaincode. We can conclude that the access control is enforced during endorsement.

Scenario 2

membersOnlyRead: false

membersOnlyWrite: true

This means that other organizations (e.g. Org3) can read the private data. We will take a look how this can be done.

As previous scenario, we first take a look at the query from each organization.

Though we still cannot read the data from Org3 (CLI), we see different error messages.

When membersOnlyRead is set true (previous scenario), Org3 cannot read the private data due to read access permission not allowed.

is set (previous scenario), Org3 cannot read the private data due to read access permission not allowed. When membersOnlyRead is set false (this scenario), Org3 cannot read the private data because the private data is not stored in the peer of Org3. Only hash is found.

As Scenario 1, first we query getPrivate() function and specify peer of Org1 to process this query.

We see that with membersOnlyRead is set false, the getPrivate() function can be queried by Org3, as far as it specifies one of the target peers which is the member of collection. Here the data is retrieved from peer of Org1.

Then we invoke setPrivate() from Org3, specifying peers of Org1 and Org2 as endorsers.

We get back the same error message. We can conclude that the access control is enforced during endorsement.

As summary, we can see the result of tuning membersOnlyRead:

Scenario 3

membersOnlyRead: true

membersOnlyWrite: false

As Scenario 1, first we query getPrivate() function and specify peer of Org1 to process this query.

As before, we encounter read access permission issue, which means that Org3 cannot read the private data.

Then we invoke setPrivate() from Org3, specifying peers of Org1 and Org2 as endorsers.

We see the chaincode function invoke working. The command is issued by Org3, which is outside the policy members. The endorsers are set to peers of Org1 and Org2. As far as the endorsement policy is satisfied, Org3 can perform this chaincode invoke and update the private data.

Finally we check the result with getPrivate()

Org3 invokes the chaincode function successfully. The private data is updated. It is interesting that Org3 is still unable to read the private data although it can invoke the chaincode function successfully.

As summary, we can see the result of tuning membersOnlyWrite:

Observation

These two options membersOnlyRead and membersOnlyWrite allow more control in private data collection. In scenario 1, both are set true. This is the strictest policy as only collection policy members can read and write the private data. Set false to either or both options the restriction is released to organizations outside the collection policy members, as what we see in scenario 2 and 3.

In our testing chaincode, getPrivate() performs reading only, and setPrivate() writing only. In case your chaincode function involves both reading a private data and later writing back, make sure you set both false if you allow non policy member organizations to invoke it.

Note that this opens a floodgate for any non policy member organizations. As suggested in the document, access control can be added in the chaincode in order to apply some limitation on the accessing. For example, in our setup, we can set only Org3 can invoke functions while Org4 (if there is) cannot, or only a designated user of Org3 can but others cannot, etc. This needs to be coded in the chaincode explicitly.