2
votes

I have a Hyperledger Fabric network with two organisations: Org1 and Org2. Discovery is enabled. Private data collections are used to protect sensitive data. In particular there is one private data collection accessible only to members of Org2. When I try to submit transaction which requires access to Org2-only private data, I observe that it is sent to peers of Org1 for endorsement as well:

const gateway = new Gateway();
await gateway.connect(ccp, { wallet, identity: userName, discovery: { enabled: true, asLocalhost: false } });
const network = await gateway.getNetwork(channelName);
const contract = await network.getContract(contractName);
await contract.createTransaction(command).setTransient(transient).submit();

Connection profile used does not list peers from Org2, but my guess is that the Node SDK finds peers from Org1 through discovery and sends transaction proposal to them. Peer logs in Org1 show that it cannot access private data, which is expected:

2019-11-21T12:03:03.684Z ERROR [contracts-spi/chaincodefromcontract.js]  
    {"message":"GET_STATE failed: transaction ID: 25f22c0abd0318b2ec1da06ae28b90a8e6af55e6d0ea825461938cce8b2d0801: private data matching public hash version is not available. Public hash version = {BlockNum: 951, TxNum: 4}, Private data version = <nil>","stack":"Error: GET_STATE failed: transaction ID: 25f22c0abd0318b2ec1da06ae28b90a8e6af55e6d0ea825461938cce8b2d0801: private data matching public hash version is not available. Public hash version = {BlockNum: 951, TxNum: 4}, Private data version = <nil>\n    at parseResponse (/usr/local/src/node_modules/fabric-shim/lib/handler.js:751:15)\n    at MsgQueueHandler.handleMsgResponse (/usr/local/src/node_modules/fabric-shim/lib/handler.js:136:40)\n    at ClientDuplexStream.<anonymous> (/usr/local/src/node_modules/fabric-shim/lib/handler.js:290:46)\n    at emitOne (events.js:116:13)\n    at ClientDuplexStream.emit (events.js:211:7)\n    at addChunk (_stream_readable.js:263:12)\n    at readableAddChunk (_stream_readable.js:250:11)\n    at ClientDuplexStream.Readable.push (_stream_readable.js:208:10)\n    at Object.onReceiveMessage (/usr/local/src/node_modules/grpc/src/client_interceptors.js:1292:19)\n    at InterceptingListener.recvMessageWithContext (/usr/local/src/node_modules/grpc/src/client_interceptors.js:607:19)"}
2019-11-21T12:03:03.684Z ERROR [lib/handler.js] [channel123-25f22c0a]Calling chaincode Invoke() returned error response [Error: GET_STATE failed: transaction ID: 25f22c0abd0318b2ec1da06ae28b90a8e6af55e6d0ea825461938cce8b2d0801: private data matching public hash version is not available. Public hash version = {BlockNum: 951, TxNum: 4}, Private data version = <nil>]. Sending ERROR message back to peer

Similar output is also displayed at the client side. Only in client application, it is a Warning instead of an Error.

2019-11-21T15:15:53.165Z - warn: [DiscoveryEndorsementHandler]: _build_endorse_group_member >> G2:0 - endorsement failed - Error: transaction returned with failure: Error: GET_STATE failed: transaction ID: 0b3e90c745535af7520ffab7b82b041394d409850cb5efff96071c24f5f75817: private data matching public hash version is not available. Public hash version = {BlockNum: 957, TxNum: 0}, Private data version = <nil>

Despite the above errors/warnings, the transaction is successful. Expected updates to the ledger and private data collections are performed.

The question is: is it possible to control from the client side, which peers are used for endorsement of particular transaction, taking into account private data?

I found Channel.getEndorsementPlan(endorsement_hint) which correctly recognises which peers have access to specific chaincode and private data collections. Is it possible to use output from this function to control the behaviour of Transaction.submit()?

1
i know it doesn't help much, but - for the record, this is supported in the Go SDK. The peer side allows such queries. You can pass a hint to the discovery query to the peer that you want to use a collection and it will filter out peers not in the collection. See hyperledger-fabric.readthedocs.io/en/release-1.4/… - yacovm
This capability of using discovery to query collections is available in the node sdk but not via the gateway interface (ie the high level api). You could switch to using the low level api (Client/Channel instances) and do it from there. The Gateway implementation (when a new version is released) doesn't use the discovery facility but rather just allows you to specify the peers yourself which isn't as useful but I suppose does allow you to achieve what you need. - david_k

1 Answers

1
votes

The code to allow you to target specific peers is in the gateway (high-level api) code base but unfortunately it is not in the current release of the node sdk which currently is 1.4.4. Hopefully at some point a 1.4.5 version will be released that will have this in. There are probably newer snapshot releases of the fabric-node-sdk on npm that you could try out for now. For reference if you look at the reference documentation here https://fabric-sdk-node.github.io/release-1.4/module-fabric-network.Transaction.html you should see a method called setEndorsingPeers. This should allow you to perform peer targeting for your transaction. An example used in the tests can be found here https://github.com/hyperledger/fabric-sdk-node/blob/bf8c663fbbb9adeeb872b27eb8ccec60c03af6de/test/typescript/integration/network-e2e/invoke.ts#L954

The node-sdk low-level api (Client/Channel interface) does have the capability to discover and determine collections, but that is not available through the gateway/network/contract interface (and no code for it exists at the moment). Here is a reference to a how to use it https://fabric-sdk-node.github.io/release-1.4/tutorial-discovery.html but the Client/Channel apis don't provide support for wallets or handle events for you so you need to do identity handling and event handling yourself.