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