github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/docs/source/private_data_tutorial.rst (about) 1 2 Using Private Data in Fabric 3 ============================ 4 5 This tutorial will demonstrate the use of collections to provide storage 6 and retrieval of private data on the blockchain network for authorized peers 7 of organizations. 8 9 The information in this tutorial assumes knowledge of private data 10 stores and their use cases. For more information, check out :doc:`private-data/private-data`. 11 12 .. note:: These instructions use the new Fabric chaincode lifecycle introduced 13 in the Fabric v2.0 Alpha release. If you would like to use the previous 14 lifecycle model to use private data with chaincode, visit the v1.4 15 version of the `Using Private Data in Fabric tutorial <https://hyperledger-fabric.readthedocs.io/en/release-1.4/private_data_tutorial.html>`__. 16 17 The tutorial will take you through the following steps to practice defining, 18 configuring and using private data with Fabric: 19 20 #. :ref:`pd-build-json` 21 #. :ref:`pd-read-write-private-data` 22 #. :ref:`pd-install-define_cc` 23 #. :ref:`pd-store-private-data` 24 #. :ref:`pd-query-authorized` 25 #. :ref:`pd-query-unauthorized` 26 #. :ref:`pd-purge` 27 #. :ref:`pd-indexes` 28 #. :ref:`pd-ref-material` 29 30 This tutorial will use the `marbles private data sample <https://github.com/hyperledger/fabric-samples/tree/master/chaincode/marbles02_private>`__ 31 --- running on the Building Your First Network (BYFN) tutorial network --- to 32 demonstrate how to create, deploy, and use a collection of private data. 33 The marbles private data sample will be deployed to the :doc:`build_network` 34 (BYFN) tutorial network. You should have completed the task :doc:`install`; 35 however, running the BYFN tutorial is not a prerequisite for this tutorial. 36 Instead the necessary commands are provided throughout this tutorial to use the 37 network. We will describe what is happening at each step, making it possible to 38 understand the tutorial without actually running the sample. 39 40 .. _pd-build-json: 41 42 Build a collection definition JSON file 43 ------------------------------------------ 44 45 The first step in privatizing data on a channel is to build a collection 46 definition which defines access to the private data. 47 48 The collection definition describes who can persist data, how many peers the 49 data is distributed to, how many peers are required to disseminate the private 50 data, and how long the private data is persisted in the private database. Later, 51 we will demonstrate how chaincode APIs ``PutPrivateData`` and ``GetPrivateData`` 52 are used to map the collection to the private data being secured. 53 54 A collection definition is composed of the following properties: 55 56 .. _blockToLive: 57 58 - ``name``: Name of the collection. 59 60 - ``policy``: Defines the organization peers allowed to persist the collection data. 61 62 - ``requiredPeerCount``: Number of peers required to disseminate the private data as 63 a condition of the endorsement of the chaincode 64 65 - ``maxPeerCount``: For data redundancy purposes, the number of other peers 66 that the current endorsing peer will attempt to distribute the data to. 67 If an endorsing peer goes down, these other peers are available at commit time 68 if there are requests to pull the private data. 69 70 - ``blockToLive``: For very sensitive information such as pricing or personal information, 71 this value represents how long the data should live on the private database in terms 72 of blocks. The data will live for this specified number of blocks on the private database 73 and after that it will get purged, making this data obsolete from the network. 74 To keep private data indefinitely, that is, to never purge private data, set 75 the ``blockToLive`` property to ``0``. 76 77 - ``memberOnlyRead``: a value of ``true`` indicates that peers automatically 78 enforce that only clients belonging to one of the collection member organizations 79 are allowed read access to private data. 80 81 To illustrate usage of private data, the marbles private data example contains 82 two private data collection definitions: ``collectionMarbles`` 83 and ``collectionMarblePrivateDetails``. The ``policy`` property in the 84 ``collectionMarbles`` definition allows all members of the channel (Org1 and 85 Org2) to have the private data in a private database. The 86 ``collectionMarblesPrivateDetails`` collection allows only members of Org1 to 87 have the private data in their private database. 88 89 For more information on building a policy definition refer to the :doc:`endorsement-policies` 90 topic. 91 92 .. code:: json 93 94 // collections_config.json 95 96 [ 97 { 98 "name": "collectionMarbles", 99 "policy": "OR('Org1MSP.member', 'Org2MSP.member')", 100 "requiredPeerCount": 0, 101 "maxPeerCount": 3, 102 "blockToLive":1000000, 103 "memberOnlyRead": true 104 }, 105 106 { 107 "name": "collectionMarblePrivateDetails", 108 "policy": "OR('Org1MSP.member')", 109 "requiredPeerCount": 0, 110 "maxPeerCount": 3, 111 "blockToLive":3, 112 "memberOnlyRead": true 113 } 114 ] 115 116 The data to be secured by these policies is mapped in chaincode and will be 117 shown later in the tutorial. 118 119 This collection definition file is deployed when the chaincode definition is 120 committed to the channel using the `peer lifecycle chaincode commit command <http://hyperledger-fabric.readthedocs.io/en/latest/commands/peerchaincode.html#peer-chaincode-instantiate>`__. 121 More details on this process are provided in Section 3 below. 122 123 .. _pd-read-write-private-data: 124 125 Read and Write private data using chaincode APIs 126 ------------------------------------------------ 127 128 The next step in understanding how to privatize data on a channel is to build 129 the data definition in the chaincode. The marbles private data sample divides 130 the private data into two separate data definitions according to how the data will 131 be accessed. 132 133 .. code-block:: GO 134 135 // Peers in Org1 and Org2 will have this private data in a side database 136 type marble struct { 137 ObjectType string `json:"docType"` 138 Name string `json:"name"` 139 Color string `json:"color"` 140 Size int `json:"size"` 141 Owner string `json:"owner"` 142 } 143 144 // Only peers in Org1 will have this private data in a side database 145 type marblePrivateDetails struct { 146 ObjectType string `json:"docType"` 147 Name string `json:"name"` 148 Price int `json:"price"` 149 } 150 151 Specifically access to the private data will be restricted as follows: 152 153 - ``name, color, size, and owner`` will be visible to all members of the channel (Org1 and Org2) 154 - ``price`` only visible to members of Org1 155 156 Thus two different sets of private data are defined in the marbles private data 157 sample. The mapping of this data to the collection policy which restricts its 158 access is controlled by chaincode APIs. Specifically, reading and writing 159 private data using a collection definition is performed by calling ``GetPrivateData()`` 160 and ``PutPrivateData()``, which can be found `here <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#ChaincodeStub>`_. 161 162 The following diagrams illustrate the private data model used by the marbles 163 private data sample. 164 165 .. image:: images/SideDB-org1.png 166 167 .. image:: images/SideDB-org2.png 168 169 170 Reading collection data 171 ~~~~~~~~~~~~~~~~~~~~~~~~ 172 173 Use the chaincode API ``GetPrivateData()`` to query private data in the 174 database. ``GetPrivateData()`` takes two arguments, the **collection name** 175 and the data key. Recall the collection ``collectionMarbles`` allows members of 176 Org1 and Org2 to have the private data in a side database, and the collection 177 ``collectionMarblePrivateDetails`` allows only members of Org1 to have the 178 private data in a side database. For implementation details refer to the 179 following two `marbles private data functions <https://github.com/hyperledger/fabric-samples/blob/master/chaincode/marbles02_private/go/marbles_chaincode_private.go>`__: 180 181 * **readMarble** for querying the values of the ``name, color, size and owner`` attributes 182 * **readMarblePrivateDetails** for querying the values of the ``price`` attribute 183 184 When we issue the database queries using the peer commands later in this tutorial, 185 we will call these two functions. 186 187 Writing private data 188 ~~~~~~~~~~~~~~~~~~~~ 189 190 Use the chaincode API ``PutPrivateData()`` to store the private data 191 into the private database. The API also requires the name of the collection. 192 Since the marbles private data sample includes two different collections, it is called 193 twice in the chaincode: 194 195 1. Write the private data ``name, color, size and owner`` using the 196 collection named ``collectionMarbles``. 197 2. Write the private data ``price`` using the collection named 198 ``collectionMarblePrivateDetails``. 199 200 For example, in the following snippet of the ``initMarble`` function, 201 ``PutPrivateData()`` is called twice, once for each set of private data. 202 203 .. code-block:: GO 204 205 // ==== Create marble object, marshal to JSON, and save to state ==== 206 marble := &marble{ 207 ObjectType: "marble", 208 Name: marbleInput.Name, 209 Color: marbleInput.Color, 210 Size: marbleInput.Size, 211 Owner: marbleInput.Owner, 212 } 213 marbleJSONasBytes, err := json.Marshal(marble) 214 if err != nil { 215 return shim.Error(err.Error()) 216 } 217 218 // === Save marble to state === 219 err = stub.PutPrivateData("collectionMarbles", marbleInput.Name, marbleJSONasBytes) 220 if err != nil { 221 return shim.Error(err.Error()) 222 } 223 224 // ==== Create marble private details object with price, marshal to JSON, and save to state ==== 225 marblePrivateDetails := &marblePrivateDetails{ 226 ObjectType: "marblePrivateDetails", 227 Name: marbleInput.Name, 228 Price: marbleInput.Price, 229 } 230 marblePrivateDetailsBytes, err := json.Marshal(marblePrivateDetails) 231 if err != nil { 232 return shim.Error(err.Error()) 233 } 234 err = stub.PutPrivateData("collectionMarblePrivateDetails", marbleInput.Name, marblePrivateDetailsBytes) 235 if err != nil { 236 return shim.Error(err.Error()) 237 } 238 239 240 To summarize, the policy definition above for our ``collection.json`` 241 allows all peers in Org1 and Org2 to store and transact 242 with the marbles private data ``name, color, size, owner`` in their 243 private database. But only peers in Org1 can store and transact with 244 the ``price`` private data in its private database. 245 246 As an additional data privacy benefit, since a collection is being used, 247 only the private data hashes go through orderer, not the private data itself, 248 keeping private data confidential from orderer. 249 250 Start the network 251 ----------------- 252 253 Now we are ready to step through some commands which demonstrate how to use 254 private data. 255 256 :guilabel:`Try it yourself` 257 258 Before installing, defining, and using the marbles private data chaincode below, 259 we need to start the BYFN network. For the sake of this tutorial, we want to 260 operate from a known initial state. The following command will kill any active 261 or stale docker containers and remove previously generated artifacts. 262 Therefore let's run the following command to clean up any previous 263 environments: 264 265 .. code:: bash 266 267 cd fabric-samples/first-network 268 ./byfn.sh down 269 270 271 If you've already run through this tutorial, you'll also want to delete the 272 underlying docker containers for the marbles private data chaincode. Let's run 273 the following commands to clean up previous environments: 274 275 .. code:: bash 276 277 docker rm -f $(docker ps -a | awk '($2 ~ /dev-peer.*.marblesp.*/) {print $1}') 278 docker rmi -f $(docker images | awk '($1 ~ /dev-peer.*.marblesp.*/) {print $3}') 279 280 Start up the BYFN network with CouchDB by running the following command: 281 282 .. code:: bash 283 284 ./byfn.sh up -c mychannel -s couchdb 285 286 This will create a simple Fabric network consisting of a single channel named 287 ``mychannel`` with two organizations (each maintaining two peer nodes) and an 288 ordering service while using CouchDB as the state database. Either LevelDB or 289 CouchDB may be used with collections. CouchDB was chosen to demonstrate how to 290 use indexes with private data. 291 292 .. note:: For collections to work, it is important to have cross organizational 293 gossip configured correctly. Refer to our documentation on :doc:`gossip`, 294 paying particular attention to the section on "anchor peers". Our tutorial 295 does not focus on gossip given it is already configured in the BYFN sample, 296 but when configuring a channel, the gossip anchors peers are critical to 297 configure for collections to work properly. 298 299 .. _pd-install-define_cc: 300 301 Install and define a chaincode with a collection 302 ------------------------------------------------- 303 304 Client applications interact with the blockchain ledger through chaincode. 305 Therefore we need to install a chaincode on every peer that will execute and 306 endorse our transactions. However, before we can interact with our chaincode, 307 the members of the channel need to agree on a chaincode definition that 308 establishes chaincode governance, including the private data collection 309 configuration. We are going to package, install, and then define the chaincode 310 on the channel using :doc:`commands/peerlifecycle`. 311 312 Install chaincode on all peers 313 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 314 315 The chaincode needs to be packaged before it can be installed on our peers. 316 We can use the `peer lifecycle chaincode package <http://hyperledger-fabric.readthedocs.io/en/latest/commands/peerlifecycle.html#peer-lifecycle-chaincode-package>`__ command 317 to package the marbles chaincode. 318 319 The BYFN network includes two organizations, Org1 and Org2, with two peers each. 320 Therefore, the chaincode package has to be installed on four peers: 321 322 - peer0.org1.example.com 323 - peer1.org1.example.com 324 - peer0.org2.example.com 325 - peer1.org2.example.com 326 327 After the chaincode is packaged, we can use the `peer lifecycle chaincode install <http://hyperledger-fabric.readthedocs.io/en/latest/commands/peerlifecycle.html#peer-lifecycle-chaincode-install>`__ 328 command to install the Marbles chaincode on each peer. 329 330 :guilabel:`Try it yourself` 331 332 Assuming you have started the BYFN network, enter the CLI container: 333 334 .. code:: bash 335 336 docker exec -it cli bash 337 338 Your command prompt will change to something similar to: 339 340 .. code:: bash 341 342 bash-4.4# 343 344 1. Use the following command to package the marbles private data chaincode from 345 the git repository inside your local container. 346 347 .. code:: bash 348 349 peer lifecycle chaincode package marblesp.tar.gz --path github.com/hyperledger/fabric-samples/chaincode/marbles02_private/go/ --lang golang --label marblespv1 350 351 This command will create a chaincode package named marblesp.tar.gz. 352 353 2. Use the following command to install the chaincode package onto the peer 354 ``peer0.org1.example.com`` in your BYFN network. By default, after starting the 355 BYFN network, the active peer is set to 356 ``CORE_PEER_ADDRESS=peer0.org1.example.com:7051``: 357 358 .. code:: bash 359 360 peer lifecycle chaincode install marblesp.tar.gz 361 362 A successful install command will return the chaincode identifier, similar to 363 the response below: 364 365 .. code:: bash 366 367 2019-04-22 19:09:04.336 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nKmarblespv1:57f5353b2568b79cb5384b5a8458519a47186efc4fcadb98280f5eae6d59c1cd\022\nmarblespv1" > 368 2019-04-22 19:09:04.336 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: marblespv1:57f5353b2568b79cb5384b5a8458519a47186efc4fcadb98280f5eae6d59c1cd 369 370 3. Use the CLI to switch the active peer to the second peer in Org1 and install 371 the chaincode. Copy and paste the following entire block of commands into the 372 CLI container and run them: 373 374 .. code:: bash 375 376 export CORE_PEER_ADDRESS=peer1.org1.example.com:8051 377 peer lifecycle chaincode install marblesp.tar.gz 378 379 4. Use the CLI to switch to Org2. Copy and paste the following block of commands 380 as a group into the peer container and run them all at once: 381 382 .. code:: bash 383 384 export CORE_PEER_LOCALMSPID=Org2MSP 385 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 386 export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA 387 export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 388 389 5. Switch the active peer to the first peer in Org2 and install the chaincode: 390 391 .. code:: bash 392 393 export CORE_PEER_ADDRESS=peer0.org2.example.com:9051 394 peer lifecycle chaincode install marblesp.tar.gz 395 396 6. Switch the active peer to the second peer in org2 and install the chaincode: 397 398 .. code:: bash 399 400 export CORE_PEER_ADDRESS=peer1.org2.example.com:10051 401 peer lifecycle chaincode install marblesp.tar.gz 402 403 404 Approve the chaincode definition 405 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 406 407 Each channel member that wants to use the chaincode needs to approve a chaincode 408 definition for their organization. Since both organizations are going to use the 409 chaincode in this tutorial, we need to approve the chaincode definition for both 410 Org1 and Org2. 411 412 The chaincode definition includes the package identifier that was returned by 413 the install command. This packege ID is used to associate the chaincode package 414 installed on your peers with the chaincode definition approved by your 415 organization. We can also use the `peer lifecycle chaincode queryinstalled <http://hyperledger-fabric.readthedocs.io/en/latest/commands/peerlifecycle.html#peer-lifecycle-chaincode-queryinstalled>`__ 416 command to find the package ID of ``marblesp.tar.gz``. 417 418 Once we have the package ID, we can then use the `peer lifecycle chaincode approveformyorg <http://hyperledger-fabric.readthedocs.io/en/latest/commands/peerlifecycle.html#peer-lifecycle-chaincode-approveformyorg>`__ 419 command to approve a definition of the marbles chaincode for Org1 and Org2. To approve 420 the private data collection definition that accompanies the ``marbles02_private``, 421 sample, provide the path to the collections JSON file using the 422 ``--collections-config`` flag. 423 424 :guilabel:`Try it yourself` 425 426 Run the following commands inside the CLI container to approve a definition for 427 Org1 and Org2. 428 429 1. Use the following command to query your peer for the package ID of the 430 installed chaincode. 431 432 .. code:: bash 433 434 peer lifecycle chaincode queryinstalled 435 436 The command will return the same package identifier as the install command. 437 You should see output similar to the following: 438 439 .. code:: bash 440 441 Installed chaincodes on peer: 442 Package ID: marblespv1:57f5353b2568b79cb5384b5a8458519a47186efc4fcadb98280f5eae6d59c1cd, Label: marblespv1 443 Package ID: mycc_1:27ef99cb3cbd1b545063f018f3670eddc0d54f40b2660b8f853ad2854c49a0d8, Label: mycc_1 444 445 2. Declare the package ID as an environment variable. Paste the package ID of 446 marblespv1 returned by the ``peer lifecycle chaincode queryinstalled`` into 447 the command below. The package ID may not be the same for all users, so you 448 need to complete this step using the package ID returned from your console. 449 450 .. code:: bash 451 452 export CC_PACKAGE_ID=marblespv1:57f5353b2568b79cb5384b5a8458519a47186efc4fcadb98280f5eae6d59c1cd 453 454 3. Make sure we are running the CLI as Org1. Copy and paste the following block 455 of commands as a group into the peer container and run them all at once: 456 457 .. code :: bash 458 459 export CORE_PEER_ADDRESS=peer0.org1.example.com:7051 460 export CORE_PEER_LOCALMSPID=Org1MSP 461 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 462 export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA 463 export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 464 465 4. Use the following command to approve a definition of the marbles private data 466 chaincode for Org1. This command includes a path to the collection definition 467 file. The approval is distributed within each organization using gossip, so 468 the command does not need to target every peer within an organization. 469 470 .. code:: bash 471 472 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 473 peer lifecycle chaincode approveformyorg --channelID mychannel --name marblesp --version 1.0 --collections-config $GOPATH/src/github.com/hyperledger/fabric-samples/chaincode/marbles02_private/collections_config.json --signature-policy "OR('Org1MSP.member','Org2MSP.member')" --init-required --package-id $CC_PACKAGE_ID --sequence 1 --tls true --cafile $ORDERER_CA 474 475 When the command completes successfully you should see something similar to: 476 477 .. code:: bash 478 479 2019-03-18 16:04:09.046 UTC [cli.lifecycle.chaincode] InitCmdFactory -> INFO 001 Retrieved channel (mychannel) orderer endpoint: orderer.example.com:7050 480 2019-03-18 16:04:11.253 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [efba188ca77889cc1c328fc98e0bb12d3ad0abcda3f84da3714471c7c1e6c13c] committed with status (VALID) at 481 482 5. Use the CLI to switch to Org2. Copy and paste the following block of commands 483 as a group into the peer container and run them all at once. 484 485 .. code:: bash 486 487 export CORE_PEER_ADDRESS=peer0.org2.example.com:9051 488 export CORE_PEER_LOCALMSPID=Org2MSP 489 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 490 export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA 491 export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 492 493 6. You can now approve the chaincode definition for Org2: 494 495 .. code:: bash 496 497 peer lifecycle chaincode approveformyorg --channelID mychannel --name marblesp --version 1.0 --collections-config $GOPATH/src/github.com/hyperledger/fabric-samples/chaincode/marbles02_private/collections_config.json --signature-policy "OR('Org1MSP.member','Org2MSP.member')" --init-required --package-id $CC_PACKAGE_ID --sequence 1 --tls true --cafile $ORDERER_CA 498 499 Commit the chaincode definition 500 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 501 502 Once a sufficient number of organizations (in this case, a majority) have 503 approved a chaincode definition, one organization commit the definition to the 504 channel. 505 506 Use the `peer lifecycle chaincode commit <http://hyperledger-fabric.readthedocs.io/en/latest/commands/peerlifecycle.html#peer-lifecycle-chaincode-commit>`__ 507 command to commit the chaincode definition. This command needs to target the 508 peers in Org1 and Org2 to collect endorsements for the commit transaction. The 509 peers will endorse the transaction only if their organizations have approved the 510 chaincode definition. This command will also deploy the collection definition to 511 the channel. 512 513 We are ready to use the chaincode after the chaincode definition has been 514 committed to the channel. Because the marbles private data chaincode contains an 515 initiation function, we need to use the `peer chaincode invoke <http://hyperledger-fabric.readthedocs.io/en/master/commands/peerchaincode.html?%20chaincode%20instantiate#peer-chaincode-instantiate>`__ command 516 to invoke ``Init()`` before we can use other functions in the chaincode. 517 518 :guilabel:`Try it yourself` 519 520 1. Run the following commands to commit the definition of the marbles private 521 data chaincode to the BYFN channel ``mychannel``. 522 523 .. code:: bash 524 525 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 526 export ORG1_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt 527 export ORG2_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt 528 peer lifecycle chaincode commit -o orderer.example.com:7050 --channelID mychannel --name marblesp --version 1.0 --sequence 1 --collections-config $GOPATH/src/github.com/hyperledger/fabric-samples/chaincode/marbles02_private/collections_config.json --signature-policy "OR('Org1MSP.member','Org2MSP.member')" --init-required --tls true --cafile $ORDERER_CA --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $ORG2_CA 529 530 .. note:: When specifying the value of the ``--collections-config`` flag, you will 531 need to specify the fully qualified path to the collections_config.json file. 532 For example: 533 534 .. code:: bash 535 536 --collections-config $GOPATH/src/github.com/hyperledger/fabric-samples/chaincode/marbles02_private/collections_config.json 537 538 When the commit transaction completes successfully you should see something 539 similar to: 540 541 .. code:: bash 542 543 [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc 544 [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc 545 546 2. Use the following command to invoke the ``Init`` function to initialize the 547 chaincode: 548 549 .. code:: bash 550 551 peer chaincode invoke -o orderer.example.com:7050 --channelID mychannel --name marblesp --isInit --tls true --cafile $ORDERER_CA --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $ORG1_CA -c '{"Args":["Init"]}' 552 553 .. _pd-store-private-data: 554 555 Store private data 556 ------------------ 557 558 Acting as a member of Org1, who is authorized to transact with all of the private data 559 in the marbles private data sample, switch back to an Org1 peer and 560 submit a request to add a marble: 561 562 :guilabel:`Try it yourself` 563 564 Copy and paste the following set of commands to the CLI command line. 565 566 .. code:: bash 567 568 export CORE_PEER_ADDRESS=peer0.org1.example.com:7051 569 export CORE_PEER_LOCALMSPID=Org1MSP 570 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 571 export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 572 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 573 574 Invoke the marbles ``initMarble`` function which 575 creates a marble with private data --- name ``marble1`` owned by ``tom`` with a color 576 ``blue``, size ``35`` and price of ``99``. Recall that private data **price** 577 will be stored separately from the private data **name, owner, color, size**. 578 For this reason, the ``initMarble`` function calls the ``PutPrivateData()`` API 579 twice to persist the private data, once for each collection. Also note that 580 the private data is passed using the ``--transient`` flag. Inputs passed 581 as transient data will not be persisted in the transaction in order to keep 582 the data private. Transient data is passed as binary data and therefore when 583 using CLI it must be base64 encoded. We use an environment variable 584 to capture the base64 encoded value, and use ``tr`` command to strip off the 585 problematic newline characters that linux base64 command adds. 586 587 .. code:: bash 588 589 export MARBLE=$(echo -n "{\"name\":\"marble1\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64 | tr -d \\n) 590 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\"}" 591 592 You should see results similar to: 593 594 ``[chaincodeCmd] chaincodeInvokeOrQuery->INFO 001 Chaincode invoke successful. result: status:200`` 595 596 .. _pd-query-authorized: 597 598 Query the private data as an authorized peer 599 -------------------------------------------- 600 601 Our collection definition allows all members of Org1 and Org2 602 to have the ``name, color, size, owner`` private data in their side database, 603 but only peers in Org1 can have the ``price`` private data in their side 604 database. As an authorized peer in Org1, we will query both sets of private data. 605 606 The first ``query`` command calls the ``readMarble`` function which passes 607 ``collectionMarbles`` as an argument. 608 609 .. code-block:: GO 610 611 // =============================================== 612 // readMarble - read a marble from chaincode state 613 // =============================================== 614 615 func (t *SimpleChaincode) readMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response { 616 var name, jsonResp string 617 var err error 618 if len(args) != 1 { 619 return shim.Error("Incorrect number of arguments. Expecting name of the marble to query") 620 } 621 622 name = args[0] 623 valAsbytes, err := stub.GetPrivateData("collectionMarbles", name) //get the marble from chaincode state 624 625 if err != nil { 626 jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}" 627 return shim.Error(jsonResp) 628 } else if valAsbytes == nil { 629 jsonResp = "{\"Error\":\"Marble does not exist: " + name + "\"}" 630 return shim.Error(jsonResp) 631 } 632 633 return shim.Success(valAsbytes) 634 } 635 636 The second ``query`` command calls the ``readMarblePrivateDetails`` 637 function which passes ``collectionMarblePrivateDetails`` as an argument. 638 639 .. code-block:: GO 640 641 // =============================================== 642 // readMarblePrivateDetails - read a marble private details from chaincode state 643 // =============================================== 644 645 func (t *SimpleChaincode) readMarblePrivateDetails(stub shim.ChaincodeStubInterface, args []string) pb.Response { 646 var name, jsonResp string 647 var err error 648 649 if len(args) != 1 { 650 return shim.Error("Incorrect number of arguments. Expecting name of the marble to query") 651 } 652 653 name = args[0] 654 valAsbytes, err := stub.GetPrivateData("collectionMarblePrivateDetails", name) //get the marble private details from chaincode state 655 656 if err != nil { 657 jsonResp = "{\"Error\":\"Failed to get private details for " + name + ": " + err.Error() + "\"}" 658 return shim.Error(jsonResp) 659 } else if valAsbytes == nil { 660 jsonResp = "{\"Error\":\"Marble private details does not exist: " + name + "\"}" 661 return shim.Error(jsonResp) 662 } 663 return shim.Success(valAsbytes) 664 } 665 666 Now :guilabel:`Try it yourself` 667 668 Query for the ``name, color, size and owner`` private data of ``marble1`` as a member of Org1. 669 Note that since queries do not get recorded on the ledger, there is no need to pass 670 the marble name as a transient input. 671 672 .. code:: bash 673 674 peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarble","marble1"]}' 675 676 You should see the following result: 677 678 .. code:: bash 679 680 {"color":"blue","docType":"marble","name":"marble1","owner":"tom","size":35} 681 682 Query for the ``price`` private data of ``marble1`` as a member of Org1. 683 684 .. code:: bash 685 686 peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}' 687 688 You should see the following result: 689 690 .. code:: bash 691 692 {"docType":"marblePrivateDetails","name":"marble1","price":99} 693 694 .. _pd-query-unauthorized: 695 696 Query the private data as an unauthorized peer 697 ---------------------------------------------- 698 699 Now we will switch to a member of Org2 which has the marbles private data 700 ``name, color, size, owner`` in its side database, but does not have the 701 marbles ``price`` private data in its side database. We will query for both 702 sets of private data. 703 704 Switch to a peer in Org2 705 ~~~~~~~~~~~~~~~~~~~~~~~~ 706 707 From inside the docker container, run the following commands to switch to 708 the peer which is unauthorized to access the marbles ``price`` private data. 709 710 :guilabel:`Try it yourself` 711 712 .. code:: bash 713 714 export CORE_PEER_ADDRESS=peer0.org2.example.com:9051 715 export CORE_PEER_LOCALMSPID=Org2MSP 716 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 717 export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA 718 export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 719 720 Query private data Org2 is authorized to 721 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 722 723 Peers in Org2 should have the first set of marbles private data (``name, 724 color, size and owner``) in their side database and can access it using the 725 ``readMarble()`` function which is called with the ``collectionMarbles`` 726 argument. 727 728 :guilabel:`Try it yourself` 729 730 .. code:: bash 731 732 peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarble","marble1"]}' 733 734 You should see something similar to the following result: 735 736 .. code:: json 737 738 {"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"} 739 740 Query private data Org2 is not authorized to 741 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 742 743 Peers in Org2 do not have the marbles ``price`` private data in their side database. 744 When they try to query for this data, they get back a hash of the key matching 745 the public state but will not have the private state. 746 747 :guilabel:`Try it yourself` 748 749 .. code:: bash 750 751 peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}' 752 753 You should see a result similar to: 754 755 .. code:: json 756 757 {"Error":"Failed to get private details for marble1: GET_STATE failed: 758 transaction ID: b04adebbf165ddc90b4ab897171e1daa7d360079ac18e65fa15d84ddfebfae90: 759 Private data matching public hash version is not available. Public hash 760 version = &version.Height{BlockNum:0x6, TxNum:0x0}, Private data version = 761 (*version.Height)(nil)"} 762 763 Members of Org2 will only be able to see the public hash of the private data. 764 765 .. _pd-purge: 766 767 Purge Private Data 768 ------------------ 769 770 For use cases where private data only needs to be on the ledger until it can be 771 replicated into an off-chain database, it is possible to "purge" the data after 772 a certain set number of blocks, leaving behind only hash of the data that serves 773 as immutable evidence of the transaction. 774 775 There may be private data including personal or confidential 776 information, such as the pricing data in our example, that the transacting 777 parties don't want disclosed to other organizations on the channel. Thus, it 778 has a limited lifespan, and can be purged after existing unchanged on the 779 blockchain for a designated number of blocks using the ``blockToLive`` property 780 in the collection definition. 781 782 Our ``collectionMarblePrivateDetails`` definition has a ``blockToLive`` 783 property value of three meaning this data will live on the side database for 784 three blocks and then after that it will get purged. Tying all of the pieces 785 together, recall this collection definition ``collectionMarblePrivateDetails`` 786 is associated with the ``price`` private data in the ``initMarble()`` function 787 when it calls the ``PutPrivateData()`` API and passes the 788 ``collectionMarblePrivateDetails`` as an argument. 789 790 We will step through adding blocks to the chain, and then watch the price 791 information get purged by issuing four new transactions (Create a new marble, 792 followed by three marble transfers) which adds four new blocks to the chain. 793 After the fourth transaction (third marble transfer), we will verify that the 794 price private data is purged. 795 796 :guilabel:`Try it yourself` 797 798 Switch back to peer0 in Org1 using the following commands. Copy and paste the 799 following code block and run it inside your peer container: 800 801 .. code:: bash 802 803 export CORE_PEER_ADDRESS=peer0.org1.example.com:7051 804 export CORE_PEER_LOCALMSPID=Org1MSP 805 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 806 export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 807 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 808 809 Open a new terminal window and view the private data logs for this peer by 810 running the following command: 811 812 .. code:: bash 813 814 docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata' 815 816 You should see results similar to the following. Note the highest block number 817 in the list. In the example below, the highest block height is ``4``. 818 819 .. code:: bash 820 821 [pvtdatastorage] func1 -> INFO 023 Purger started: Purging expired private data till block number [0] 822 [pvtdatastorage] func1 -> INFO 024 Purger finished 823 [kvledger] CommitLegacy -> INFO 022 Channel [mychannel]: Committed block [0] with 1 transaction(s) 824 [kvledger] CommitLegacy -> INFO 02e Channel [mychannel]: Committed block [1] with 1 transaction(s) 825 [kvledger] CommitLegacy -> INFO 030 Channel [mychannel]: Committed block [2] with 1 transaction(s) 826 [kvledger] CommitLegacy -> INFO 036 Channel [mychannel]: Committed block [3] with 1 transaction(s) 827 [kvledger] CommitLegacy -> INFO 03e Channel [mychannel]: Committed block [4] with 1 transaction(s) 828 829 Back in the peer container, query for the **marble1** price data by running the 830 following command. (A Query does not create a new transaction on the ledger 831 since no data is transacted). 832 833 .. code:: bash 834 835 peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}' 836 837 You should see results similar to: 838 839 .. code:: bash 840 841 {"docType":"marblePrivateDetails","name":"marble1","price":99} 842 843 The ``price`` data is still in the private data ledger. 844 845 Create a new **marble2** by issuing the following command. This transaction 846 creates a new block on the chain. 847 848 .. code:: bash 849 850 export MARBLE=$(echo -n "{\"name\":\"marble2\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64 | tr -d \\n) 851 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\"}" 852 853 Switch back to the Terminal window and view the private data logs for this peer 854 again. You should see the block height increase by 1. 855 856 .. code:: bash 857 858 docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata' 859 860 Back in the peer container, query for the **marble1** price data again by 861 running the following command: 862 863 .. code:: bash 864 865 peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}' 866 867 The private data has not been purged, therefore the results are unchanged from 868 previous query: 869 870 .. code:: bash 871 872 {"docType":"marblePrivateDetails","name":"marble1","price":99} 873 874 Transfer marble2 to "joe" by running the following command. This transaction 875 will add a second new block on the chain. 876 877 .. code:: bash 878 879 export MARBLE_OWNER=$(echo -n "{\"name\":\"marble2\",\"owner\":\"joe\"}" | base64 | tr -d \\n) 880 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\"}" 881 882 Switch back to the Terminal window and view the private data logs for this peer 883 again. You should see the block height increase by 1. 884 885 .. code:: bash 886 887 docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata' 888 889 Back in the peer container, query for the marble1 price data by running the 890 following command: 891 892 .. code:: bash 893 894 peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}' 895 896 You should still be able to see the price private data. 897 898 .. code:: bash 899 900 {"docType":"marblePrivateDetails","name":"marble1","price":99} 901 902 Transfer marble2 to "tom" by running the following command. This transaction 903 will create a third new block on the chain. 904 905 .. code:: bash 906 907 export MARBLE_OWNER=$(echo -n "{\"name\":\"marble2\",\"owner\":\"tom\"}" | base64 | tr -d \\n) 908 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\"}" 909 910 Switch back to the Terminal window and view the private data logs for this peer 911 again. You should see the block height increase by 1. 912 913 .. code:: bash 914 915 docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata' 916 917 Back in the peer container, query for the marble1 price data by running the 918 following command: 919 920 .. code:: bash 921 922 peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}' 923 924 You should still be able to see the price data. 925 926 .. code:: bash 927 928 {"docType":"marblePrivateDetails","name":"marble1","price":99} 929 930 Finally, transfer marble2 to "jerry" by running the following command. This 931 transaction will create a fourth new block on the chain. The ``price`` private 932 data should be purged after this transaction. 933 934 .. code:: bash 935 936 export MARBLE_OWNER=$(echo -n "{\"name\":\"marble2\",\"owner\":\"jerry\"}" | base64 | tr -d \\n) 937 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\"}" 938 939 Switch back to the Terminal window and view the private data logs for this peer 940 again. You should see the block height increase by 1. 941 942 .. code:: bash 943 944 docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata' 945 946 Back in the peer container, query for the marble1 price data by running the following command: 947 948 .. code:: bash 949 950 peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}' 951 952 Because the price data has been purged, you should no longer be able to see it. 953 You should see something similar to: 954 955 .. code:: bash 956 957 Error: endorsement failure during query. response: status:500 958 message:"{\"Error\":\"Marble private details does not exist: marble1\"}" 959 960 .. _pd-indexes: 961 962 Using indexes with private data 963 ------------------------------- 964 965 Indexes can also be applied to private data collections, by packaging indexes in 966 the ``META-INF/statedb/couchdb/collections/<collection_name>/indexes`` directory 967 alongside the chaincode. An example index is available `here <https://github.com/hyperledger/fabric-samples/blob/master/chaincode/marbles02_private/go/META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexOwner.json>`__ . 968 969 For deployment of chaincode to production environments, it is recommended 970 to define any indexes alongside chaincode so that the chaincode and supporting 971 indexes are deployed automatically as a unit, once the chaincode has been 972 installed on a peer and instantiated on a channel. The associated indexes are 973 automatically deployed upon chaincode instantiation on the channel when 974 the ``--collections-config`` flag is specified pointing to the location of 975 the collection JSON file. 976 977 978 .. _pd-ref-material: 979 980 Additional resources 981 -------------------- 982 983 For additional private data education, a video tutorial has been created. 984 985 .. note:: The video uses the previous lifecycle model to install private data 986 collections with chaincode. 987 988 .. raw:: html 989 990 <br/><br/> 991 <iframe width="560" height="315" src="https://www.youtube.com/embed/qyjDi93URJE" frameborder="0" allowfullscreen></iframe> 992 <br/><br/> 993 994 .. Licensed under Creative Commons Attribution 4.0 International License 995 https://creativecommons.org/licenses/by/4.0/