github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/docs/source/private_data_tutorial.rst (about)

     1  Using Private Data in Fabric
     2  ============================
     3  
     4  This tutorial will demonstrate the use of Private Data Collections (PDC) to provide storage
     5  and retrieval of private data on the blockchain network for authorized peers
     6  of organizations. The collection is specified using a collection definition file containing the policies governing that collection.
     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-use-case`
    20  #. :ref:`pd-build-json`
    21  #. :ref:`pd-read-write-private-data`
    22  #. :ref:`pd-install-define_cc`
    23  #. :ref:`pd-register-identities`
    24  #. :ref:`pd-store-private-data`
    25  #. :ref:`pd-query-authorized`
    26  #. :ref:`pd-query-unauthorized`
    27  #. :ref:`pd-transfer-asset`
    28  #. :ref:`pd-purge`
    29  #. :ref:`pd-indexes`
    30  #. :ref:`pd-ref-material`
    31  
    32  This tutorial will deploy the `asset transfer private data sample <https://github.com/hyperledger/fabric-samples/tree/master/asset-transfer-private-data/chaincode-go>`__
    33  to the Fabric test network to demonstrate how to create, deploy, and use a collection of
    34  private data.
    35  You should have completed the task :doc:`install`.
    36  
    37  .. _pd-use-case:
    38  
    39  Asset transfer private data sample use case
    40  -------------------------------------------
    41  
    42  This sample demonstrates the use of three private data collections, ``assetCollection``, ``Org1MSPPrivateCollection`` & ``Org2MSPPrivateCollection`` to transfer an asset between Org1 and Org2, using following use case:
    43  
    44  A member of Org1 creates a new asset, henceforth referred as owner. The public details of the asset,
    45  including the identity of the owner, are stored in the private data collection named ``assetCollection``. The asset is also created with an appraised
    46  value supplied by the owner. The appraised value is used by each participant to agree to the transfer of the asset, and is only stored in owner organization's collection. In our case, the initial appraisal value agreed by the owner is stored in the ``Org1MSPPrivateCollection``.
    47  
    48  To purchase the asset, the buyer needs to agree to the same appraised value as
    49  the asset owner. In this step, the buyer (a member of Org2) creates an agreement
    50  to trade and agree to an appraisal value using smart contract function ``'AgreeToTransfer'``.
    51  This value is stored in ``Org2MSPPrivateCollection`` collection. Now, the asset
    52  owner can transfer the asset to the buyer using smart contract function ``'TransferAsset'``.
    53  The ``'TransferAsset'`` function uses the hash on the channel ledger to
    54  confirm that the owner and the buyer have agreed to the same appraised value
    55  before transferring the asset.
    56  
    57  Before we go through the transfer scenario, we will discuss how organizations can use private data collections in Fabric.
    58  
    59  .. _pd-build-json:
    60  
    61  Build a collection definition JSON file
    62  ---------------------------------------
    63  
    64  Before a set of organizations can transact using private data, all organizations
    65  on channel need to build a collection definition file that defines the private
    66  data collections associated with each chaincode. Data that is stored in a private
    67  data collection is only distributed to the peers of certain organizations instead
    68  of all members of the channel. The collection definition file describes all of the
    69  private data collections that organizations can read and write to from a chaincode.
    70  
    71  Each collection is defined by the following properties:
    72  
    73  .. _blockToLive:
    74  
    75  - ``name``: Name of the collection.
    76  
    77  - ``policy``: Defines the organization peers allowed to persist the collection data.
    78  
    79  - ``requiredPeerCount``: Number of peers required to disseminate the private data as
    80    a condition of the endorsement of the chaincode
    81  
    82  - ``maxPeerCount``: For data redundancy purposes, the number of other peers
    83    that the current endorsing peer will attempt to distribute the data to.
    84    If an endorsing peer goes down, these other peers are available at commit time
    85    if there are requests to pull the private data.
    86  
    87  - ``blockToLive``: For very sensitive information such as pricing or personal information,
    88    this value represents how long the data should live on the private database in terms
    89    of blocks. The data will live for this specified number of blocks on the private database
    90    and after that it will get purged, making this data obsolete from the network.
    91    To keep private data indefinitely, that is, to never purge private data, set
    92    the ``blockToLive`` property to ``0``.
    93  
    94  - ``memberOnlyRead``: a value of ``true`` indicates that peers automatically
    95    enforce that only clients belonging to one of the collection member organizations
    96    are allowed read access to private data.
    97  
    98  - ``memberOnlyWrite``: a value of ``true`` indicates that peers automatically
    99    enforce that only clients belonging to one of the collection member organizations
   100    are allowed write access to private data.
   101  
   102  - ``endorsementPolicy``: defines the endorsement policy that needs to be met in
   103    order to write to the private data collection. The collection level endorsement policy
   104    overrides to chaincode level policy. For more information on building a policy
   105    definition refer to the :doc:`endorsement-policies` topic.
   106  
   107  The same collection definition file needs to be deployed by all organizations that
   108  use the chaincode, even if the organization does not belong to any collections. In
   109  addition to the collections that are explicitly defined in a collection file,
   110  each organization has access to an implicit collection on their peers that can only
   111  be read by their organization. For an example that uses implicit data collections,
   112  see the :doc:`secured_asset_transfer/secured_private_asset_transfer_tutorial`.
   113  
   114  The asset transfer private data example contains a `collections_config.json` file
   115  that defines three private data collection definitions: ``assetCollection``, ``Org1MSPPrivateCollection``,
   116  and ``Org2MSPPrivateCollection``.
   117  
   118  .. code:: json
   119  
   120   // collections_config.json
   121  
   122   [
   123      {
   124      "name": "assetCollection",
   125      "policy": "OR('Org1MSP.member', 'Org2MSP.member')",
   126      "requiredPeerCount": 1,
   127      "maxPeerCount": 1,
   128      "blockToLive":1000000,
   129      "memberOnlyRead": true,
   130      "memberOnlyWrite": true
   131      },
   132      {
   133      "name": "Org1MSPPrivateCollection",
   134      "policy": "OR('Org1MSP.member')",
   135      "requiredPeerCount": 0,
   136      "maxPeerCount": 1,
   137      "blockToLive":3,
   138      "memberOnlyRead": true,
   139      "memberOnlyWrite": false,
   140      "endorsementPolicy": {
   141          "signaturePolicy": "OR('Org1MSP.member')"
   142      }
   143      },
   144      {
   145      "name": "Org2MSPPrivateCollection",
   146      "policy": "OR('Org2MSP.member')",
   147      "requiredPeerCount": 0,
   148      "maxPeerCount": 1,
   149      "blockToLive":3,
   150      "memberOnlyRead": true,
   151      "memberOnlyWrite": false,
   152      "endorsementPolicy": {
   153          "signaturePolicy": "OR('Org2MSP.member')"
   154      }
   155      }
   156   ]
   157  
   158  
   159  The ``policy`` property in the ``assetCollection`` definition specifies that both
   160  Org1 and Org2 can store the collection on their peers. The ``memberOnlyRead``
   161  and ``memberOnlyWrite`` parameters are used to specify that only Org1 and
   162  Org2 clients can read and write to this collection.
   163  
   164  The ``Org1MSPPrivateCollection`` collection allows only peers of Org1 to have
   165  the private data in their private database, while the ``Org2MSPPrivateCollection``
   166  collection can only be stored by the peers of Org2. The ``endorsementPolicy`` parameter
   167  is used to create a collection specific endorsement policy. Each update to
   168  ``Org1MSPPrivateCollection`` or ``Org2MSPPrivateCollection`` needs to be endorsed
   169  by the organization that stores the collection on their peers. We will see how
   170  these collections are used to transfer the asset in the course of the tutorial.
   171  
   172  This collection definition file is deployed when the chaincode definition is
   173  committed to the channel using the `peer lifecycle chaincode commit command <commands/peerlifecycle.html#peer-lifecycle-chaincode-commit>`__.
   174  More details on this process are provided in Section 3 below.
   175  
   176  .. _pd-read-write-private-data:
   177  
   178  Read and Write private data using chaincode APIs
   179  ------------------------------------------------
   180  
   181  The next step in understanding how to privatize data on a channel is to build
   182  the data definition in the chaincode. The asset transfer private data sample divides
   183  the private data into three separate data definitions according to how the data will
   184  be accessed.
   185  
   186  .. code-block:: GO
   187  
   188   // Peers in Org1 and Org2 will have this private data in a side database
   189   type Asset struct {
   190  	Type  string `json:"objectType"` //Type is used to distinguish the various types of objects in state database
   191  	ID    string `json:"assetID"`
   192  	Color string `json:"color"`
   193  	Size  int    `json:"size"`
   194  	Owner string `json:"owner"`
   195   }
   196  
   197   // AssetPrivateDetails describes details that are private to owners
   198  
   199   // Only peers in Org1 will have this private data in a side database
   200   type AssetPrivateDetails struct {
   201  	ID             string `json:"assetID"`
   202  	AppraisedValue int    `json:"appraisedValue"`
   203   }
   204  
   205   // Only peers in Org2 will have this private data in a side database
   206   type AssetPrivateDetails struct {
   207  	ID             string `json:"assetID"`
   208  	AppraisedValue int    `json:"appraisedValue"`
   209   }
   210  
   211  Specifically, access to the private data will be restricted as follows:
   212  
   213  - ``objectType, color, size, and owner`` are stored in ``assetCollection`` and hence will be visible to members of the channel per the definition in the collection policy (Org1 and Org2).
   214  - ``AppraisedValue`` of an asset is stored in collection ``Org1MSPPrivateCollection`` or ``Org2MSPPrivateCollection`` , depending on the owner of the asset. The value is only accessible to the users who belong to the organization that can store the collection.
   215  
   216  
   217  All of the data that is created by the asset transfer private data sample smart
   218  contract is stored in PDC. The smart contract uses the Fabric chaincode API
   219  to read and write private data to private data collections using the ``GetPrivateData()``
   220  and ``PutPrivateData()`` functions. You can find more information about those functions `here <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#ChaincodeStub>`_.
   221  This private data is stored in private state db on the peer (separate from public state db), and
   222  is disseminated between authorized peers via gossip protocol.
   223  
   224  The following diagram illustrates the private data model used by the private data
   225  sample. Note that Org3 is only shown in the diagram to illustrate that if
   226  there were any other organizations on the channel, they would not have access to *any*
   227  of the private data collections that were defined in the configuration.
   228  
   229  .. image:: images/SideDB-org1-org2.png
   230  
   231  Reading collection data
   232  ~~~~~~~~~~~~~~~~~~~~~~~~
   233  
   234  The smart contract uses the chaincode API ``GetPrivateData()`` to query private data in the
   235  database.  ``GetPrivateData()`` takes two arguments, the **collection name**
   236  and the data key. Recall the collection  ``assetCollection`` allows peers of
   237  Org1 and Org2 to have the private data in a side database, and the collection
   238  ``Org1MSPPrivateCollection`` allows only peers of Org1 to have their
   239  private data in a side database and ``Org2MSPPrivateCollection`` allows peers
   240  of Org2 to have their private data in a side database.
   241  For implementation details refer to the following two `asset transfer private data functions <https://github.com/hyperledger/fabric-samples/blob/{BRANCH}/asset-transfer-private-data/chaincode-go/chaincode/asset_queries.go>`__:
   242  
   243   * **ReadAsset** for querying the values of the ``assetID, color, size and owner`` attributes.
   244   * **ReadAssetPrivateDetails** for querying the values of the ``appraisedValue`` attribute.
   245  
   246  When we issue the database queries using the peer commands later in this tutorial,
   247  we will call these two functions.
   248  
   249  Writing private data
   250  ~~~~~~~~~~~~~~~~~~~~
   251  
   252  The smart contract uses the chaincode API ``PutPrivateData()`` to store the private data
   253  into the private database. The API also requires the name of the collection.
   254  Note that the asset transfer private data sample includes three different private data collections, but it is called
   255  twice in the chaincode (in this scenario acting as Org1).
   256  
   257  1. Write the private data ``assetID, color, size and owner`` using the
   258     collection named ``assetCollection``.
   259  2. Write the private data ``appraisedValue`` using the collection named
   260     ``Org1MSPPrivateCollection``.
   261  
   262  If we were acting as Org2, we would replace ``Org1MSPPrivateCollection`` with
   263  ````Org2MSPPrivateCollection``.
   264  
   265  For example, in the following snippet of the ``CreateAsset`` function,
   266  ``PutPrivateData()`` is called twice, once for each set of private data.
   267  
   268  .. code-block:: GO
   269  
   270      // CreateAsset creates a new asset by placing the main asset details in the assetCollection
   271      // that can be read by both organizations. The appraisal value is stored in the owners org specific collection.
   272      func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface) error {
   273  
   274          // Get new asset from transient map
   275          transientMap, err := ctx.GetStub().GetTransient()
   276          if err != nil {
   277              return fmt.Errorf("error getting transient: %v", err)
   278          }
   279  
   280          // Asset properties are private, therefore they get passed in transient field, instead of func args
   281          transientAssetJSON, ok := transientMap["asset_properties"]
   282          if !ok {
   283              //log error to stdout
   284              return fmt.Errorf("asset not found in the transient map input")
   285          }
   286  
   287          type assetTransientInput struct {
   288              Type           string `json:"objectType"` //Type is used to distinguish the various types of objects in state database
   289              ID             string `json:"assetID"`
   290              Color          string `json:"color"`
   291              Size           int    `json:"size"`
   292              AppraisedValue int    `json:"appraisedValue"`
   293          }
   294  
   295          var assetInput assetTransientInput
   296          err = json.Unmarshal(transientAssetJSON, &assetInput)
   297          if err != nil {
   298              return fmt.Errorf("failed to unmarshal JSON: %v", err)
   299          }
   300  
   301          if len(assetInput.Type) == 0 {
   302              return fmt.Errorf("objectType field must be a non-empty string")
   303          }
   304          if len(assetInput.ID) == 0 {
   305              return fmt.Errorf("assetID field must be a non-empty string")
   306          }
   307          if len(assetInput.Color) == 0 {
   308              return fmt.Errorf("color field must be a non-empty string")
   309          }
   310          if assetInput.Size <= 0 {
   311              return fmt.Errorf("size field must be a positive integer")
   312          }
   313          if assetInput.AppraisedValue <= 0 {
   314              return fmt.Errorf("appraisedValue field must be a positive integer")
   315          }
   316  
   317          // Check if asset already exists
   318          assetAsBytes, err := ctx.GetStub().GetPrivateData(assetCollection, assetInput.ID)
   319          if err != nil {
   320              return fmt.Errorf("failed to get asset: %v", err)
   321          } else if assetAsBytes != nil {
   322              fmt.Println("Asset already exists: " + assetInput.ID)
   323              return fmt.Errorf("this asset already exists: " + assetInput.ID)
   324          }
   325  
   326          // Get ID of submitting client identity
   327          clientID, err := submittingClientIdentity(ctx)
   328          if err != nil {
   329              return err
   330          }
   331  
   332          // Verify that the client is submitting request to peer in their organization
   333          // This is to ensure that a client from another org doesn't attempt to read or
   334          // write private data from this peer.
   335          err = verifyClientOrgMatchesPeerOrg(ctx)
   336          if err != nil {
   337              return fmt.Errorf("CreateAsset cannot be performed: Error %v", err)
   338          }
   339  
   340          // Make submitting client the owner
   341          asset := Asset{
   342              Type:  assetInput.Type,
   343              ID:    assetInput.ID,
   344              Color: assetInput.Color,
   345              Size:  assetInput.Size,
   346              Owner: clientID,
   347          }
   348          assetJSONasBytes, err := json.Marshal(asset)
   349          if err != nil {
   350              return fmt.Errorf("failed to marshal asset into JSON: %v", err)
   351          }
   352  
   353          // Save asset to private data collection
   354          // Typical logger, logs to stdout/file in the fabric managed docker container, running this chaincode
   355          // Look for container name like dev-peer0.org1.example.com-{chaincodename_version}-xyz
   356          log.Printf("CreateAsset Put: collection %v, ID %v, owner %v", assetCollection, assetInput.ID, clientID)
   357  
   358          err = ctx.GetStub().PutPrivateData(assetCollection, assetInput.ID, assetJSONasBytes)
   359          if err != nil {
   360              return fmt.Errorf("failed to put asset into private data collecton: %v", err)
   361          }
   362  
   363          // Save asset details to collection visible to owning organization
   364          assetPrivateDetails := AssetPrivateDetails{
   365              ID:             assetInput.ID,
   366              AppraisedValue: assetInput.AppraisedValue,
   367          }
   368  
   369          assetPrivateDetailsAsBytes, err := json.Marshal(assetPrivateDetails) // marshal asset details to JSON
   370          if err != nil {
   371              return fmt.Errorf("failed to marshal into JSON: %v", err)
   372          }
   373  
   374          // Get collection name for this organization.
   375          orgCollection, err := getCollectionName(ctx)
   376          if err != nil {
   377              return fmt.Errorf("failed to infer private collection name for the org: %v", err)
   378          }
   379  
   380          // Put asset appraised value into owners org specific private data collection
   381          log.Printf("Put: collection %v, ID %v", orgCollection, assetInput.ID)
   382          err = ctx.GetStub().PutPrivateData(orgCollection, assetInput.ID, assetPrivateDetailsAsBytes)
   383          if err != nil {
   384              return fmt.Errorf("failed to put asset private details: %v", err)
   385          }
   386          return nil
   387      }
   388  
   389  To summarize, the policy definition above for our ``collections_config.json``
   390  allows all peers in Org1 and Org2 to store and transact
   391  with the asset transfer private data ``assetID, color, size, owner`` in their
   392  private database. But only peers in Org1 can store and transact with
   393  the ``appraisedValue`` key data in the Org1 collection ``Org1MSPPrivateCollection`` and only peers
   394  in Org2 can store and transact with the ``appraisedValue`` key data in the Org2 collection ``Org2MSPPrivateCollection``.
   395  
   396  As an additional data privacy benefit, since a collection is being used,
   397  only the private data *hashes* go through orderer, not the private data itself,
   398  keeping private data confidential from orderer.
   399  
   400  Start the network
   401  -----------------
   402  
   403  Now we are ready to step through some commands which demonstrate how to use
   404  private data.
   405  
   406  :guilabel:`Try it yourself`
   407  
   408  Before installing, defining, and using the private data smart contract,
   409  we need to start the Fabric test network. For the sake of this tutorial, we want
   410  to operate from a known initial state. The following command will kill any active
   411  or stale Docker containers and remove previously generated artifacts.
   412  Therefore let's run the following command to clean up any previous
   413  environments:
   414  
   415  .. code:: bash
   416  
   417     cd fabric-samples/test-network
   418     ./network.sh down
   419  
   420  From the ``test-network`` directory, you can use the following command to start
   421  up the Fabric test network with Certificate Authorities and CouchDB:
   422  
   423  .. code:: bash
   424  
   425     ./network.sh up createChannel -ca -s couchdb
   426  
   427  This command will deploy a Fabric network consisting of a single channel named
   428  ``mychannel`` with two organizations (each maintaining one peer node), certificate authorities, and an
   429  ordering service while using CouchDB as the state database. Either LevelDB or
   430  CouchDB may be used with collections. CouchDB was chosen to demonstrate how to
   431  use indexes with private data.
   432  
   433  .. note:: For collections to work, it is important to have cross organizational
   434             gossip configured correctly. Refer to our documentation on :doc:`gossip`,
   435             paying particular attention to the section on "anchor peers". Our tutorial
   436             does not focus on gossip given it is already configured in the test network,
   437             but when configuring a channel, the gossip anchors peers are critical to
   438             configure for collections to work properly.
   439  
   440  .. _pd-install-define_cc:
   441  
   442  Deploy the private data smart contract to the channel
   443  -----------------------------------------------------
   444  
   445  We can now use the test network script to deploy the smart contract to the channel.
   446  Run the following command from the test network directory.
   447  
   448  .. code:: bash
   449  
   450     ./network.sh deployCC -ccn private -ccp ../asset-transfer-private-data/chaincode-go/ -ccl go -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -cccg ../asset-transfer-private-data/chaincode-go/collections_config.json
   451  
   452  Note that we need to pass the path to the private data collection definition file
   453  to the command. As part of deploying the chaincode to the channel, both organizations
   454  on the channel must pass identical private data collection definitions as part
   455  of the :doc:`chaincode_lifecycle`. We are also deploying the smart contract
   456  with a chaincode level endorsement policy of ``"OR('Org1MSP.peer','Org2MSP.peer')"``.
   457  This allows Org1 and Org2 to create an asset without receiving an endorsement from
   458  the other organization. You can see the steps required to deploy the chaincode
   459  printed in your logs after you issue the command above.
   460  
   461  When both organizations approve the chaincode defition using the
   462  `peer lifecycle chaincode approveformyorg <commands/peerlifecycle.html#peer-lifecycle-chaincode-approveformyorg>`__
   463  command, the chaincode definition includes the path to the private data collection
   464  definition using the ``--collections-config`` flag. You can see the following `approveformyorg`
   465  command printed in your terminal:
   466  
   467  .. code:: bash
   468  
   469      peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name private --version 1.0 --collections-config ../asset-transfer-private-data/chaincode-go/collections_config.json --signature-policy "OR('Org1MSP.member','Org2MSP.member')" --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile $ORDERER_CA
   470  
   471  After channel members agree to the private data collection as part of the chaincode
   472  definition, the data collection is committed to the channel using the `peer lifecycle chaincode commit <commands/peerlifecycle.html#peer-lifecycle-chaincode-commit>`__
   473  command. If you look for the commit command in your logs, you can see that it uses
   474  the same ``--collections-config`` flag to provide the path to the collection definition.
   475  
   476  .. code:: bash
   477  
   478      peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name private --version 1.0 --sequence 1 --collections-config ../asset-transfer-private-data/chaincode-go/collections_config.json --signature-policy "OR('Org1MSP.member','Org2MSP.member')" --tls --cafile $ORDERER_CA --peerAddresses localhost:7051 --tlsRootCertFiles $ORG1_CA --peerAddresses localhost:9051 --tlsRootCertFiles $ORG2_CA
   479  
   480  
   481  .. _pd-register-identities:
   482  
   483  Register identities
   484  -------------------
   485  The private data transfer smart contract supports ownership by individual identities that belong to the network. In our scenario, the owner of the asset will be a member of Org1, while the buyer will belong to Org2. To highlight the connection between the ``GetClientIdentity().GetID()`` API and the information within a user's certificate, we will register two new identities using the Org1 and Org2 Certificate Authorities (CA's), and then use the CA's to generate each identity's certificate and private key.
   486  
   487  First, we need to set the following environment variables to use the Fabric CA client:
   488  
   489  .. code :: bash
   490  
   491      export PATH=${PWD}/../bin:${PWD}:$PATH
   492      export FABRIC_CFG_PATH=$PWD/../config/
   493  
   494  We will use the Org1 CA to create the identity asset owner. Set the Fabric CA client home to the MSP of the Org1 CA admin (this identity was generated by the test network script):
   495  
   496  .. code:: bash
   497  
   498      export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org1.example.com/
   499  
   500  You can register a new owner client identity using the `fabric-ca-client` tool:
   501  
   502  .. code:: bash
   503  
   504      fabric-ca-client register --caname ca-org1 --id.name owner --id.secret ownerpw --id.type client --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
   505  
   506  
   507  You can now generate the identity certificates and MSP folder by providing the enroll name and secret to the enroll command:
   508  
   509  .. code:: bash
   510  
   511      fabric-ca-client enroll -u https://owner:ownerpw@localhost:7054 --caname ca-org1 -M ${PWD}/organizations/peerOrganizations/org1.example.com/users/owner@org1.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
   512  
   513  
   514  Run the command below to copy the Node OU configuration file into the owner identity MSP folder.
   515  
   516  .. code:: bash
   517  
   518      cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/users/owner@org1.example.com/msp/config.yaml
   519  
   520  
   521  We can now use the Org2 CA to create the buyer identity. Set the Fabric CA client home the Org2 CA admin:
   522  
   523  .. code:: bash
   524  
   525      export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org2.example.com/
   526  
   527  You can register a new owner client identity using the `fabric-ca-client` tool:
   528  
   529  .. code:: bash
   530  
   531      fabric-ca-client register --caname ca-org2 --id.name buyer --id.secret buyerpw --id.type client --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem
   532  
   533  
   534  We can now enroll to generate the identity MSP folder:
   535  
   536  .. code:: bash
   537  
   538      fabric-ca-client enroll -u https://buyer:buyerpw@localhost:8054 --caname ca-org2 -M ${PWD}/organizations/peerOrganizations/org2.example.com/users/buyer@org2.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem
   539  
   540  
   541  Run the command below to copy the Node OU configuration file into the buyer identity MSP folder.
   542  
   543  .. code:: bash
   544  
   545      cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org2.example.com/users/buyer@org2.example.com/msp/config.yaml
   546  
   547  .. _pd-store-private-data:
   548  
   549  Create an asset in private data
   550  -------------------------------
   551  
   552  Now that we have created the identity of the asset owner, we can invoke the
   553  private data smart contract to create a new asset. Copy and paste the following
   554  set of commands into your terminal in the `test-network` directory:
   555  
   556  :guilabel:`Try it yourself`
   557  
   558  
   559  .. code :: bash
   560  
   561      export PATH=${PWD}/../bin:$PATH
   562      export FABRIC_CFG_PATH=$PWD/../config/
   563      export CORE_PEER_TLS_ENABLED=true
   564      export CORE_PEER_LOCALMSPID="Org1MSP"
   565      export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
   566      export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/owner@org1.example.com/msp
   567      export CORE_PEER_ADDRESS=localhost:7051
   568  
   569  
   570  We will use the ``CreateAsset`` function to create an asset that is stored in private
   571  data ---  assetID ``asset1`` with a color ``green``, size ``20`` and appraisedValue of ``100``. Recall that private data **appraisedValue**
   572  will be stored separately from the private data **assetID, color, size**.
   573  For this reason, the ``CreateAsset`` function calls the ``PutPrivateData()`` API
   574  twice to persist the private data, once for each collection. Also note that
   575  the private data is passed using the ``--transient`` flag. Inputs passed
   576  as transient data will not be persisted in the transaction in order to keep
   577  the data private. Transient data is passed as binary data and therefore when
   578  using terminal it must be base64 encoded. We use an environment variable
   579  to capture the base64 encoded value, and use ``tr`` command to strip off the
   580  problematic newline characters that linux base64 command adds.
   581  
   582  Run the following command to create the asset:
   583  
   584  .. code:: bash
   585  
   586      export ASSET_PROPERTIES=$(echo -n "{\"objectType\":\"asset\",\"assetID\":\"asset1\",\"color\":\"green\",\"size\":20,\"appraisedValue\":100}" | base64 | tr -d \\n)
   587      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 private -c '{"function":"CreateAsset","Args":[]}' --transient "{\"asset_properties\":\"$ASSET_PROPERTIES\"}"
   588  
   589  You should see results similar to:
   590  
   591  .. code:: bash
   592  
   593      [chaincodeCmd] chaincodeInvokeOrQuery->INFO 001 Chaincode invoke successful. result: status:200
   594  
   595  Note that command above only targets the Org1 peer. The ``CreateAsset`` transaction writes to two collections, ``assetCollection`` and ``Org1MSPPrivateCollection``.
   596  The ``Org1MSPPrivateCollection`` requires an endorsement from the Org1 peer in order to write to the collection, while the ``assetCollection`` inherits the endorsement policy of the chaincode, ``"OR('Org1MSP.peer','Org2MSP.peer')"``.
   597  An endorsement from the Org1 peer can meet both endorsement policies and is able to create an asset without an endorsement from Org2.
   598  
   599  .. _pd-query-authorized:
   600  
   601  Query the private data as an authorized peer
   602  --------------------------------------------
   603  
   604  Our collection definition allows all peers of Org1 and Org2
   605  to have the ``assetID, color, size, and owner`` private data in their side database,
   606  but only peers in Org1 can have Org1's opinion of their ``appraisedValue`` private data in their side
   607  database. As an authorized peer in Org1, we will query both sets of private data.
   608  
   609  The first ``query`` command calls the ``ReadAsset`` function which passes
   610  ``assetCollection`` as an argument.
   611  
   612  .. code-block:: GO
   613  
   614     // ReadAsset reads the information from collection
   615     func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, assetID string) (*Asset, error) {
   616  
   617          log.Printf("ReadAsset: collection %v, ID %v", assetCollection, assetID)
   618          assetJSON, err := ctx.GetStub().GetPrivateData(assetCollection, assetID) //get the asset from chaincode state
   619          if err != nil {
   620              return nil, fmt.Errorf("failed to read asset: %v", err)
   621          }
   622  
   623          //No Asset found, return empty response
   624          if assetJSON == nil {
   625              log.Printf("%v does not exist in collection %v", assetID, assetCollection)
   626              return nil, nil
   627          }
   628  
   629          var asset *Asset
   630          err = json.Unmarshal(assetJSON, &asset)
   631          if err != nil {
   632              return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
   633          }
   634  
   635          return asset, nil
   636  
   637      }
   638  
   639  The second ``query`` command calls the ``ReadAssetPrivateDetails``
   640  function which passes ``Org1MSPPrivateDetails`` as an argument.
   641  
   642  .. code-block:: GO
   643  
   644     // ReadAssetPrivateDetails reads the asset private details in organization specific collection
   645     func (s *SmartContract) ReadAssetPrivateDetails(ctx contractapi.TransactionContextInterface, collection string, assetID string) (*AssetPrivateDetails, error) {
   646          log.Printf("ReadAssetPrivateDetails: collection %v, ID %v", collection, assetID)
   647          assetDetailsJSON, err := ctx.GetStub().GetPrivateData(collection, assetID) // Get the asset from chaincode state
   648          if err != nil {
   649              return nil, fmt.Errorf("failed to read asset details: %v", err)
   650          }
   651          if assetDetailsJSON == nil {
   652              log.Printf("AssetPrivateDetails for %v does not exist in collection %v", assetID, collection)
   653              return nil, nil
   654          }
   655  
   656          var assetDetails *AssetPrivateDetails
   657          err = json.Unmarshal(assetDetailsJSON, &assetDetails)
   658          if err != nil {
   659              return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
   660          }
   661  
   662          return assetDetails, nil
   663      }
   664  
   665  Now :guilabel:`Try it yourself`
   666  
   667  We can read the main details of the asset that was created by using the `ReadAsset` function
   668  to query the `assetCollection` collection as Org1:
   669  
   670  .. code:: bash
   671  
   672      peer chaincode query -C mychannel -n private -c '{"function":"ReadAsset","Args":["asset1"]}'
   673  
   674  When successful, the command will return the following result:
   675  
   676  .. code:: bash
   677  
   678      {"objectType":"asset","assetID":"asset1","color":"green","size":20,"owner":"x509::CN=appUser1,OU=admin,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org1.example.com,O=org1.example.com,L=Durham,ST=North Carolina,C=US"}
   679  
   680  The `"owner"` of the asset is the identity that created the asset by invoking the smart contract. The private data smart contract uses the ``GetClientIdentity().GetID()`` API to read the name and issuer of the identity certificate. You can see the name and issuer of the identity certificate, in the owner attribute.
   681  
   682  
   683  Query for the ``appraisedValue`` private data of ``asset1`` as a member of Org1.
   684  
   685  .. code:: bash
   686  
   687      peer chaincode query -C mychannel -n private -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}'
   688  
   689  You should see the following result:
   690  
   691  .. code:: bash
   692  
   693      {"assetID":"asset1","appraisedValue":100}
   694  
   695  .. _pd-query-unauthorized:
   696  
   697  Query the private data as an unauthorized peer
   698  ----------------------------------------------
   699  
   700  Now we will operate a user from Org2. Org2 has the asset transfer private data
   701  ``assetID, color, size, owner`` in its side database as defined in the ``assetCollection`` policy, but does not store the
   702  asset ``appraisedValue`` data for Org1. We will query for both sets of private data.
   703  
   704  Switch to a peer in Org2
   705  ~~~~~~~~~~~~~~~~~~~~~~~~
   706  
   707  Run the following commands to operate as an Org2 member and query the Org2 peer.
   708  
   709  :guilabel:`Try it yourself`
   710  
   711  .. code:: bash
   712  
   713      export CORE_PEER_LOCALMSPID="Org2MSP"
   714      export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
   715      export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/buyer@org2.example.com/msp
   716      export CORE_PEER_ADDRESS=localhost:9051
   717  
   718  Query private data Org2 is authorized to
   719  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   720  
   721  Peers in Org2 should have the first set of asset transfer private data (``assetID,
   722  color, size and owner``) in their side database and can access it using the
   723  ``ReadAsset()`` function which is called with the ``assetCollection``
   724  argument.
   725  
   726  :guilabel:`Try it yourself`
   727  
   728  .. code:: bash
   729  
   730      peer chaincode query -C mychannel -n private -c '{"function":"ReadAsset","Args":["asset1"]}'
   731  
   732  When successful, should see something similar to the following result:
   733  
   734  .. code:: json
   735  
   736      {"objectType":"asset","assetID":"asset1","color":"green","size":20,
   737      "owner":"x509::CN=appUser1,OU=admin,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org1.example.com,O=org1.example.com,L=Durham,ST=North Carolina,C=US" }
   738  
   739  Query private data Org2 is not authorized to
   740  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   741  
   742  Because the asset was created by Org1, the ``appraisedValue`` associated with
   743  ``asset1`` is stored in the ``Org1MSPPrivateCollection`` collection. The value is
   744  not stored by peers in Org2. Run the following command to demonstrate that the
   745  asset's ``appraisedValue`` is not stored in the ``Org2MSPPrivateCollection``
   746  on the Org2 peer:
   747  
   748  :guilabel:`Try it yourself`
   749  
   750  .. code:: bash
   751  
   752      peer chaincode query -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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org2MSPPrivateCollection","asset1"]}'
   753  
   754  The empty response shows that the asset1 private details do not exist in buyer
   755  (Org2) private collection.
   756  
   757  Nor can a user from Org2 read the Org1 private data collection:
   758  
   759  .. code:: bash
   760  
   761      peer chaincode query -C mychannel -n private -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}'
   762  
   763  By setting ``"memberOnlyRead": true`` in the collection configuration file, we
   764  specify that only clients from Org1 can read data from the collection. An Org2 client
   765  who tries to read the collection would only get the following response:
   766  
   767  .. code:: json
   768  
   769      Error: endorsement failure during query. response: status:500 message:"failed to
   770      read asset details: GET_STATE failed: transaction ID: d23e4bc0538c3abfb7a6bd4323fd5f52306e2723be56460fc6da0e5acaee6b23: tx
   771      creator does not have read access permission on privatedata in chaincodeName:private collectionName: Org1MSPPrivateCollection"
   772  
   773  Users from Org2 will only be able to see the public hash of the private data.
   774  
   775  .. _pd-transfer-asset:
   776  
   777  Transfer the Asset
   778  ------------------
   779  
   780  Let's see what it takes to transfer ``asset1`` to Org2. In this case, Org2 needs to agree
   781  to buy the asset from Org1, and they need to agree on the ``appraisedValue``. You may be wondering how they can
   782  agree if Org1 keeps their opinion of the ``appraisedValue`` in their private side database. For the answer
   783  to this, lets continue.
   784  
   785  :guilabel:`Try it yourself`
   786  
   787  Switch back to the terminal with our peer CLI.
   788  
   789  To transfer an asset, the buyer (recipient) needs to agree to the same ``appraisedValue`` as the asset owner, by calling chaincode function ``AgreeToTransfer``. The agreed value will be stored in the ``Org2MSPDetailsCollection`` collection on the Org2 peer. Run the following commands to agree to the appraised value of 100 as Org2:
   790  
   791  .. code:: bash
   792  
   793      export ASSET_VALUE=$(echo -n "{\"assetID\":\"asset1\",\"appraisedValue\":100}" | base64 | tr -d \\n)
   794      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 private -c '{"function":"AgreeToTransfer","Args":[]}' --transient "{\"asset_value\":\"$ASSET_VALUE\"}"
   795  
   796  
   797  The buyer can now query the value they agreed to in the Org2 private data collection:
   798  
   799  .. code:: bash
   800  
   801      peer chaincode query -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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org2MSPPrivateCollection","asset1"]}'
   802  
   803  The invoke will return the following value:
   804  
   805  .. code:: bash
   806  
   807      {"assetID":"asset1","appraisedValue":100}
   808  
   809  Now that buyer has agreed to buy the asset for the appraised value, the owner can transfer
   810  the asset to Org2. The asset needs to be transferred by the identity that owns the asset,
   811  so lets go acting as Org1:
   812  
   813  .. code:: bash
   814  
   815      export CORE_PEER_LOCALMSPID="Org1MSP"
   816      export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/owner@org1.example.com/msp
   817      export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
   818      export CORE_PEER_ADDRESS=localhost:7051
   819  
   820  The owner from Org1 can read the data added by the `AgreeToTransfer` transaction to view the buyer identity:
   821  
   822  .. code:: bash
   823  
   824      peer chaincode query -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 private -c '{"function":"ReadTransferAgreement","Args":["asset1"]}'
   825  
   826  .. code:: bash
   827  
   828      {"assetID":"asset1","buyerID":"eDUwOTo6Q049YnV5ZXIsT1U9Y2xpZW50LE89SHlwZXJsZWRnZXIsU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUzo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL"}
   829  
   830  We now have all we need to transfer the asset. The smart contract uses the
   831  ``GetPrivateDataHash()`` function to check that the hash of the asset appraisal
   832  value in ``Org1MSPPrivateCollection`` matches the hash of the appraisal value in the
   833  ``Org2MSPPrivateCollection``. If the hashes are the same, it confirms that the
   834  owner and the interested buyer have agreed to the same asset value. If the
   835  conditions are met, the transfer function will get the client ID of the buyer
   836  from the transfer agreement and make the buyer the new owner of the asset. The transfer
   837  function will also delete the asset appraisal value from the collection of the former owner,
   838  as well as remove the transfer agreement from the ``assetCollection``.
   839  
   840  Run the following commands to transfer the asset. The owner needs to provide the
   841  assetID and the organization MSP ID of the buyer to the transfer transaction:
   842  
   843  .. code:: bash
   844  
   845      export ASSET_OWNER=$(echo -n "{\"assetID\":\"asset1\",\"buyerMSP\":\"Org2MSP\"}" | base64 | tr -d \\n)
   846      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 private -c '{"function":"TransferAsset","Args":[]}' --transient "{\"asset_owner\":\"$ASSET_OWNER\"}" --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
   847  
   848  You can query ``asset1`` to see the results of the transfer:
   849  
   850  .. code:: bash
   851  
   852      peer chaincode query -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 private -c '{"function":"ReadAsset","Args":["asset1"]}'
   853  
   854  The results will show that the buyer identity now owns the asset:
   855  
   856  .. code:: bash
   857  
   858      {"objectType":"asset","assetID":"asset1","color":"green","size":20,"owner":"x509::CN=appUser2, OU=client + OU=org2 + OU=department1::CN=ca.org2.example.com, O=org2.example.com, L=Hursley, ST=Hampshire, C=UK"}
   859  
   860  The `"owner"` of the asset now has the buyer identity.
   861  
   862  You can also confirm that transfer removed the private details from the Org1 collection:
   863  
   864  .. code:: bash
   865  
   866      peer chaincode query -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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}'
   867  
   868  Your query will return empty result, since the asset private data is removed from the Org1 private data collection.
   869  
   870  
   871  .. _pd-purge:
   872  
   873  Purge Private Data
   874  ------------------
   875  
   876  For use cases where private data only needs to be persisted for a short period of time,
   877  it is possible to "purge" the data after a certain set number of blocks, leaving
   878  behind only a hash of the data that serves as immutable evidence of the transaction.
   879  An organization could decide to purge private data if the data contained sensitive
   880  information that was used by another transaction, but is not longer needed, or
   881  if the data is being replicated into an off-chain database.
   882  
   883  The ``appraisedValue`` data in our example contains a private agreement that
   884  the organization may want to expire after a certain period of time. Thus, it
   885  has a limited lifespan, and can be purged after existing unchanged on the
   886  blockchain for a designated number of blocks using the ``blockToLive`` property
   887  in the collection definition.
   888  
   889  The ``Org2MSPPrivateCollection`` definition has a ``blockToLive``
   890  property value of ``3``, meaning this data will live on the side database for
   891  three blocks and then after that it will get purged. If we create additional
   892  blocks on the channel, the ``appraisedValue`` agreed to by Org2 will eventually
   893  get purged. We can create 3 new blocks to demonstrate:
   894  
   895  :guilabel:`Try it yourself`
   896  
   897  Run the following commands in your terminal to switch back to operating as member
   898  of Org2 and target the Org2 peer:
   899  
   900  .. code:: bash
   901  
   902      export CORE_PEER_LOCALMSPID="Org2MSP"
   903      export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
   904      export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/buyer@org2.example.com/msp
   905      export CORE_PEER_ADDRESS=localhost:9051
   906  
   907  We can still query the ``appraisedValue`` in the ``Org2MSPPrivateCollection``:
   908  
   909  .. code:: bash
   910  
   911      peer chaincode query -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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org2MSPPrivateCollection","asset1"]}'
   912  
   913  You should see the value printed in your logs:
   914  
   915  .. code:: bash
   916  
   917      {"assetID":"asset1","appraisedValue":100}
   918  
   919  Since we need to keep track of how many blocks we are adding before the private data gets purged,
   920  open a new terminal window and run the following command to view the private data logs for
   921  the Org2 peer. Note the highest block number.
   922  
   923  .. code:: bash
   924  
   925      docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata'
   926  
   927  Now return to the terminal where we are acting as a member of Org2 and run the following
   928  commands to create three new assets. Each command will create a new block.
   929  
   930  .. code:: bash
   931  
   932      export ASSET_PROPERTIES=$(echo -n "{\"objectType\":\"asset\",\"assetID\":\"asset2\",\"color\":\"blue\",\"size\":30,\"appraisedValue\":100}" | base64 | tr -d \\n)
   933      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 private -c '{"function":"CreateAsset","Args":[]}' --transient "{\"asset_properties\":\"$ASSET_PROPERTIES\"}"
   934  
   935  .. code:: bash
   936  
   937      export ASSET_PROPERTIES=$(echo -n "{\"objectType\":\"asset\",\"assetID\":\"asset3\",\"color\":\"red\",\"size\":25,\"appraisedValue\":100}" | base64 | tr -d \\n)
   938      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 private -c '{"function":"CreateAsset","Args":[]}' --transient "{\"asset_properties\":\"$ASSET_PROPERTIES\"}"
   939  
   940  .. code:: bash
   941  
   942      export ASSET_PROPERTIES=$(echo -n "{\"objectType\":\"asset\",\"assetID\":\"asset4\",\"color\":\"orange\",\"size\":15,\"appraisedValue\":100}" | base64 | tr -d \\n)
   943      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 private -c '{"function":"CreateAsset","Args":[]}' --transient "{\"asset_properties\":\"$ASSET_PROPERTIES\"}"
   944  
   945  
   946  Return to the other terminal and run the following command to confirm that
   947  the new assets resulted in the creation of three new blocks:
   948  
   949  .. code:: bash
   950  
   951      docker logs peer0.org1.example.com 2>&1 | grep -i -a -E 'private|pvt|privdata'
   952  
   953  The ``appraisedValue`` has now been purged from the ``Org2MSPDetailsCollection``
   954  private data collection. Issue the query again from the Org2 terminal to see that
   955  the response is empty.
   956  
   957  .. code:: bash
   958  
   959      peer chaincode query -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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org2MSPPrivateCollection","asset1"]}'
   960  
   961  
   962  .. _pd-indexes:
   963  
   964  Using indexes with private data
   965  -------------------------------
   966  
   967  Indexes can also be applied to private data collections, by packaging indexes in
   968  the ``META-INF/statedb/couchdb/collections/<collection_name>/indexes`` directory
   969  alongside the chaincode. An example index is available `here <https://github.com/hyperledger/fabric-samples/blob/{BRANCH}//asset-transfer-private-data/chaincode-go/META-INF/statedb/couchdb/collections/assetCollection/indexes/indexOwner.json>`__ .
   970  
   971  For deployment of chaincode to production environments, it is recommended
   972  to define any indexes alongside chaincode so that the chaincode and supporting
   973  indexes are deployed automatically as a unit, once the chaincode has been
   974  installed on a peer and instantiated on a channel. The associated indexes are
   975  automatically deployed upon chaincode instantiation on the channel when
   976  the  ``--collections-config`` flag is specified pointing to the location of
   977  the collection JSON file.
   978  
   979  Clean up
   980  --------
   981  
   982  When you are finished using the private data smart contract, you can bring down the test
   983  network using ``network.sh`` script.
   984  
   985  
   986  .. code:: bash
   987  
   988    ./network.sh down
   989  
   990  This command will bring down the CAs, peers, and ordering node of the network
   991  that we created. Note that all of the data on the ledger will be lost.
   992  If you want to go through the tutorial again, you will start from a clean initial state.
   993  
   994  .. _pd-ref-material:
   995  
   996  Additional resources
   997  --------------------
   998  
   999  For additional private data education, a video tutorial has been created.
  1000  
  1001  .. note:: The video uses the previous lifecycle model to install private data
  1002            collections with chaincode.
  1003  
  1004  .. raw:: html
  1005  
  1006     <br/><br/>
  1007     <iframe width="560" height="315" src="https://www.youtube.com/embed/qyjDi93URJE" frameborder="0" allowfullscreen></iframe>
  1008     <br/><br/>
  1009  
  1010  
  1011  
  1012  .. Licensed under Creative Commons Attribution 4.0 International License
  1013     https://creativecommons.org/licenses/by/4.0/