From 0ecfdd39a384c51a36cf15ab74cd48980dfdce6f Mon Sep 17 00:00:00 2001 From: Bret Harrison Date: Wed, 14 Nov 2018 18:08:18 -0500 Subject: [PATCH] FABN-972 NodeSDK Add private data doc Update the private data JSDoc and tutorial. Modify the test case to be clearer about how to call. Change-Id: I7cc30e451363db344238ce12599514d0abd5c597 Signed-off-by: Bret Harrison --- docs/tutorials/private-data.md | 32 ++++++++++++--- fabric-client/lib/Channel.js | 12 +++--- test/integration/e2e/e2eUtils.js | 41 +++++++------------- test/integration/e2e/getCollectionsConfig.js | 9 +++-- 4 files changed, 53 insertions(+), 41 deletions(-) diff --git a/docs/tutorials/private-data.md b/docs/tutorials/private-data.md index 923f244373..3ca7d6167f 100755 --- a/docs/tutorials/private-data.md +++ b/docs/tutorials/private-data.md @@ -2,20 +2,21 @@ This tutorial illustrates how to use the Node.js SDK APIs to store and retrieve private data in the Hyperledger Fabric network. Starting in v1.2, Fabric offers the ability to create private data collections, which allows a subset of organizations on -a channel to endorse, commit, or query private data without having to create a separate channel.For more information, +a channel to endorse, commit, or query private data without having to create a separate channel. For more information, refer to [Private Data Concept]( http://hyperledger-fabric.readthedocs.io/en/latest/private-data/private-data.html), [Private Data Architecture]( http://hyperledger-fabric.readthedocs.io/en/latest/private-data-arch.html), and [Using Private Data in Fabric]( http://hyperledger-fabric.readthedocs.io/en/latest/private_data_tutorial.html). ### Overview -The following are the steps to use private data with the Node.js SDK. Check out below sections for the details of these steps. +The following are the steps to use private data with the Node.js SDK (fabric-client). Check out the sections below for details on these steps. 1. Create a collection definition json file 2. Implement chaincode to store and query private data 3. Install and instantiate chaincode with a collection definition 4. Invoke chaincode to store and query private data 5. Purge private data +6. Query for a collection definition ### Create a collection definition json file A collection definition describes who can persist data, how many peers the data is distributed to, how many peers are @@ -96,7 +97,7 @@ The policy property in the collectionMarbles definition allows all members of th the private data in a private database. The collectionMarblesPrivateDetails collection allows only members of `Org1` to have the private data in their private database. -For Node.js SDK, you must define policies in the same format as shown in the above. +For Node.js SDK, you must define policies in the same format as shown above. ### Implement chaincode to store and query private data Fabric provides chaincode APIs to store and query private data. As an example, check out [marbles private data example]( @@ -113,7 +114,8 @@ This example implements the following functions to manage private data. ### Install and instantiate chaincode with a collection definition Client applications interact with the blockchain ledger through chaincode. As such we need to install and instantiate the chaincode on every peer that will execute and -endorse transactions. +endorse transactions. When instantiated a chaincode on a channel the collection will +be associated with that chaincode. * Install chaincode. No specific parameter needed to support private data. * Instantiate chaincode. To support private data, the request must include the `collections-config` attribute. @@ -150,7 +152,27 @@ https://github.com/hyperledger/fabric-samples/blob/master/chaincode/marbles02_pr ### Purge private data The Hyperledger Fabric allows client applications to optionally purge the private data in a collection by setting the `blockToLive` property. This option may be needed when private data include personal or confidential information -and transacting parties want to have a limited lifespan for these data. +and transacting parties want to have a limited lifespan for the data. When `blockToLive` is set to a non-zero value in the collection definition file, Fabric will automatically purge the related private data after the specified number of blocks are committed. Client applications do not need to call any API. + +### Query for a collection definition +The Hyperledger Fabric allows client applications to query a peer for collection definitions. +The Node.js SDK (fabric-client) has an API that will query a Hyperledger Fabric Peer for a +collection definition associated with a chaincode running on the specified channel. +See {@link Channel#queryCollectionsConfig} for detailed information. +``` + const request = { + chaincodeId: chaincodeId, + target: peer + }; + + try { + const response = await channel.queryCollectionsConfig(request); + // response contains an array of collection definitions + return response; + } catch (error) { + throw error; + } +``` diff --git a/fabric-client/lib/Channel.js b/fabric-client/lib/Channel.js index aa5e0fbe4b..8b251e7e2a 100755 --- a/fabric-client/lib/Channel.js +++ b/fabric-client/lib/Channel.js @@ -2317,17 +2317,19 @@ const Channel = class { * required_peer_count. * @property {number} block_to_live - The number of blocks after which the collection * data expires. For instance if the value is set to 10, a key last modified by block - * number 100 will be purged at block number 111. A zero value is treated same as MaxUint64 + * number 100 will be purged at block number 111. A zero value is treated same as MaxUint64, + * where the data will not be purged. * @property {Policy} policy - The */ /** - * query for the collections config for a chaincode. + * Query for the collection definitions associated with a chaincode. * * @param {CollectionQueryOptions} options - Required. The options to query the collections config. - * @property {boolean} useAdmin - Optional. To indicate that the admin identity - * should be used to make the discovery request - * @returns {Promise} returns a promise for a {@link CollectionQueryResponse} object. + * @param {boolean} useAdmin - Optional. To indicate that the admin identity + * should be used to make the query request + * @returns {Promise} returns a promise for an array of + * {@link CollectionQueryResponse} objects. */ async queryCollectionsConfig(options, useAdmin) { const method = 'queryCollectionsConfig'; diff --git a/test/integration/e2e/e2eUtils.js b/test/integration/e2e/e2eUtils.js index ed449149b2..f3aa9b5826 100644 --- a/test/integration/e2e/e2eUtils.js +++ b/test/integration/e2e/e2eUtils.js @@ -922,9 +922,8 @@ function getTargetPeers(channel, targets) { return targetPeers; } -async function getCollectionsConfig(t, org, chaincodeId, targets) { +async function getCollectionsConfig(t, org, chaincodeId, channel_name) { init(); - const channel_name = Client.getConfigSetting('E2E_CONFIGTX_CHANNEL_NAME', testUtil.END2END.channel); // this is a transaction, will just use org's identity to // submit the request. intentionally we are using a different org @@ -951,35 +950,23 @@ async function getCollectionsConfig(t, org, chaincodeId, targets) { await client.setUserContext(admin); t.pass('Successfully enrolled user \'admin\''); - // set up the channel to use each org's 'peer1' for - // both requests and events - for (const key in ORGS) { - if (ORGS.hasOwnProperty(key) && typeof ORGS[key].peer1 !== 'undefined') { - const data = fs.readFileSync(path.join(__dirname, ORGS[key].peer1.tls_cacerts)); - const peer = client.newPeer( - ORGS[key].peer1.requests, - { - pem: Buffer.from(data).toString(), - 'ssl-target-name-override': ORGS[key].peer1['server-hostname'] - }); - channel.addPeer(peer); + const caRootsPath = ORGS[org].peer1.tls_cacerts; + const data = fs.readFileSync(path.join(__dirname, '../e2e', caRootsPath)); + const caroots = Buffer.from(data).toString(); + + const peer = client.newPeer( + ORGS[org].peer1.requests, + { + 'pem': caroots, + 'ssl-target-name-override': ORGS[org].peer1['server-hostname'] } - } - // send query + ); + const request = { - chaincodeId, - txId: client.newTransactionID(), + chaincodeId: chaincodeId, + target: peer }; - // find the peers that match the targets - if (targets && targets.length !== 0) { - const targetPeers = getTargetPeers(channel, targets); - if (targetPeers.length < targets.length) { - t.fail('Failed to get all peers for targets: ' + targets); - } else { - request.targets = targetPeers; - } - } try { const resp = await channel.queryCollectionsConfig(request); t.pass('Successfully retrieved collections config from peer'); diff --git a/test/integration/e2e/getCollectionsConfig.js b/test/integration/e2e/getCollectionsConfig.js index 904b168ac2..00037b5c2a 100644 --- a/test/integration/e2e/getCollectionsConfig.js +++ b/test/integration/e2e/getCollectionsConfig.js @@ -11,12 +11,13 @@ const test = _test(tape); const e2eUtils = require('./e2eUtils.js'); const testUtil = require('../../unit/util.js'); -const chaincodeId = testUtil.END2END.chaincodeIdPrivateData; - test('getCollectionsConfig from peer', async (t) => { - const targets = []; // empty array, meaning client will discover the peers + const chaincodeId = testUtil.END2END.chaincodeIdPrivateData; + const channel_name = testUtil.END2END.channel; + t.comment(' looking at chaincodeId:' + chaincodeId); + t.comment(' looking at channel_name:' + channel_name); try { - const results = await e2eUtils.getCollectionsConfig(t, 'org1', chaincodeId, targets); + const results = await e2eUtils.getCollectionsConfig(t, 'org1', chaincodeId, channel_name); if (results) { t.pass('Successfully query collections config'); } else {