Starting from v1.2, Fabric offers the ability to create private data collections, which allow a defined subset of organizations on a channel the ability to endorse, commit, or query private data without having to create a separate channel.
This repository proposes a sample chaincode, pdc, to allow the user to experiment and get more familiar with the concept of private data collection.
This chaincode is a repackaged code from fabric-samples - marbles02_private with the Fabkit's way to run commands which have been added to the main.go file itself, as commented lines above the corresponding fabric-samples' command.
Any chaincode which interacts with private data collections need to have a JSON file containing the configurations of those and this file needs to be passed in input during the instantiation step (see below).
The collections_config.json file resides in the same directory of the main code and defines collections with the following configuration:
collectionMarbles: Org1MSP, Org2MSPcollectionMarblePrivateDetails: Org1MSP
In order to provide with a basic demonstration of how private data collections work, it is recommended to run the network with the 3-orgs setup (2-orgs will also work).
# start the network with 3-orgs setup
fabkit network start --orgs 3The network will be initialized with the following components:
- orderer
- ca.org1
- peer0.org1 (mygocc installed)
- couchdb.peer0.org1
- ca.org2
- peer0.org2
- couchdb.peer0.org2
- ca.org3
- peer0.org3
- couchdb.peer0.org3
- cli
Install and instantiate the pdc chaincode:
# install the pdc chaincode on all the organizations' peer0
fabkit chaincode install pdc 1.0 pdc 1 0
fabkit chaincode install pdc 1.0 pdc 2 0
fabkit chaincode install pdc 1.0 pdc 3 0
# instantiate pdc chaincode on mychannel using org1 peer0
fabkit chaincode instantiate pdc 1.0 mychannel 1 0 --collections-config ${FABKIT_CHAINCODE_REMOTE_PATH}/pdc/collections_config.json -P 'OR("Org1MSP.member","Org2MSP.member","Org3MSP.member")'fabkit chaincode lifecycle deploy pdc 1.0 pdc mychannel 1 1 0 --collections-config ${FABKIT_CHAINCODE_REMOTE_PATH}/pdc/collections_config.jsonNote: if you are deploying your own chaincode, remember that your remote basename path should match the chaincode name. e.g. if the chaincode name is mycc, then the full remote path should look like ${FABKIT_CHAINCODE_REMOTE_PATH}/mycc .
Execute some actions:
# create a new marble as org1 peer0
export MARBLE=$(echo -n "{\"name\":\"marble1\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64 | tr -d \\n)
fabkit chaincode invoke mychannel pdc 1 0 '{"Args":["initMarble"]}' --transient '{"marble":"$MARBLE"}'
# query marble as org2 peer0 (successful)
fabkit chaincode query mychannel pdc 2 0 '{"Args":["readMarble","marble1"]}'
# query marble as org3 peer0 (fail, as org3 is not part of this collection)
fabkit chaincode query mychannel pdc 3 0 '{"Args":["readMarble","marble1"]}'You can access the CouchDB UI for each organization's peer to inspect the data which gets effectively stored and its format.
For each private collection your StateDB will create 2 databases, one public to the channel and one private. e.g.:
mychannel_pdc$$hcollection$marbles: it refers tocollectionMarbleswhere thehin front stands forhash. This will contain only the hash of the data and it is shared publicly across the channel.mychannel_pdc$$pcollection$marbles: it refers tocollectionMarbleswhere thepin front stands forprivate. This will contain the data in clear.
A few more examples of commands are available in the main chaincode file ./chaincode/pdc/main.go commented out in the header.
For a full overview about collections properties and definitions check the official documentation at this page.
export MARBLE=$(echo -n "{\"name\":\"marble1\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64 | tr -d \\n)
fabkit chaincode invoke mychannel pdc 1 0 '{"Args":["initMarble"]}' --transient '{"marble":"$MARBLE"}'
export MARBLE=$(echo -n "{\"name\":\"marble2\",\"color\":\"red\",\"size\":50,\"owner\":\"tom\",\"price\":102}" | base64 | tr -d \\n)
fabkit chaincode invoke mychannel pdc 1 0 '{"Args":["initMarble"]}' --transient '{"marble":"$MARBLE"}'
export MARBLE=$(echo -n "{\"name\":\"marble3\",\"color\":\"blue\",\"size\":70,\"owner\":\"tom\",\"price\":103}" | base64 | tr -d \\n)
fabkit chaincode invoke mychannel pdc 1 0 '{"Args":["initMarble"]}' --transient '{"marble":"$MARBLE"}'export MARBLE_OWNER=$(echo -n "{\"name\":\"marble2\",\"owner\":\"jerry\"}" | base64 | tr -d \\n)
fabkit chaincode invoke mychannel pdc 1 0 '{"Args":["transferMarble"]}' --transient '{"marble_owner":"$MARBLE_OWNER"}'export MARBLE_DELETE=$(echo -n "{\"name\":\"marble1\"}" | base64 | tr -d \\n)
fabkit chaincode invoke mychannel pdc 1 0 '{"Args":["delete"]}' --transient '{"marble_delete":"$MARBLE_DELETE"}'fabkit chaincode query mychannel pdc 1 0 '{"Args":["readMarble","marble1"]}'
fabkit chaincode query mychannel pdc 1 0 '{"Args":["readMarblePrivateDetails","marble1"]}'
fabkit chaincode query mychannel pdc 1 0 '{"Args":["getMarblesByRange","marble1","marble4"]}'fabkit chaincode query mychannel pdc 1 0 '{"Args":["queryMarblesByOwner","tom"]}'
fabkit chaincode query mychannel pdc 1 0 '{"Args":["queryMarbles","{\"selector\":{\"owner\":\"tom\"}}"]}'
fabkit chaincode query mychannel pdc 1 0 '{"Args":["queryMarbles","{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
fabkit chaincode query mychannel pdc 1 0 '{"Args":["queryMarbles","{\"selector\":{\"docType\":{\"$eq\":\"marble\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'