如何在提交使用私有数据的交易时 select 为节点背书

How to select endorsing peers when submitting transaction which uses private data

我有一个包含两个组织的 Hyperledger Fabric 网络:Org1 和 Org2。发现已启用。私有数据集合用于保护敏感数据。特别是,只有 Org2 的成员可以访问一个私有数据集合。当我尝试提交需要访问仅 Org2 私有数据的交易时,我观察到它也被发送给 Org1 的对等点以供背书:

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();

使用的连接配置文件未列出来自 Org2 的对等点,但我的猜测是 Node SDK 通过发现从 Org1 中找到对等点并向它们发送交易建议。 Org1 中的对等日志显示它无法访问私有数据,这是预期的:

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

类似的输出也显示在客户端。仅在客户端应用程序中,它是警告而不是错误。

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>

尽管如上errors/warnings,交易成功。执行分类帐和私人数据收集的预期更新。

问题是:考虑到私有数据,是否可以从客户端控制哪些节点用于背书特定交易?

我发现 Channel.getEndorsementPlan(endorsement_hint) 可以正确识别哪些节点可以访问特定的链代码和私有数据集合。是否可以使用此函数的输出来控制 Transaction.submit() 的行为?

允许您针对特定对等点的代码位于网关(高级 api)代码库中,但不幸的是,它不在当前版本为 1.4.4 的节点 sdk 中。希望在某个时候会发布一个包含此功能的 1.4.5 版本。您现在可以在 npm 上试用 fabric-node-sdk 的较新快照版本。如果您在此处查看参考文档,以供参考 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

node-sdk 低级 api(Client/Channel 接口)确实具有发现和确定集合的能力,但无法通过 gateway/network/contract 接口(和目前不存在它的代码)。 Here is a reference to a how to use it https://fabric-sdk-node.github.io/release-1.4/tutorial-discovery.html 但 Client/Channel api 不提供对钱包的支持或为您处理事件,因此您需要进行身份处理和事件处理你自己。

我认为更好的方法是创建一个特定的查询处理程序 (FABRIC 2):

import { QueryHandler, QueryHandlerFactory, Query, QueryResults } from 'fabric-network';
import { Endorser } from 'fabric-common';
import * as util from 'util';

/**
 * Query handler implementation
 */
class SameOrgQueryHandler implements QueryHandler {
  private readonly peers: Endorser[];

  constructor(peers: Endorser[]) {
    this.peers = peers;
  }

  public async evaluate(query: Query): Promise<Buffer> {
    const errorMessages: string[] = [];

    for (const peer of this.peers) {
      const results: QueryResults = await query.evaluate([peer]);
      const result = results[peer.name];
      if (result instanceof Error) {
        errorMessages.push(result.toString());
      } else {
        if (result.isEndorsed) {
          console.log(`QueryHandler: ${result.payload}`);
          return result.payload;
        }
        throw new Error(result.message);
      }
    }

    const message = util.format('Query failed. Errors: %j', errorMessages);
    throw new Error(message);
  }
}

/**
 * Factory function for creating sample query handlers.
 * @param {Network} network The network where transactions are to be evaluated.
 * @returns {QueryHandler} A query handler implementation.
 */
export const createQueryHandler: QueryHandlerFactory = network => {
  const mspId = network.getGateway().getIdentity().mspId;
  //const mspIdOrg2 = 'Org2MSP';
  const channel = network.getChannel();
  const orgPeers = channel.getEndorsers(mspId);
  const otherPeers = channel.getEndorsers().filter(peer => !orgPeers.includes(peer));
  const allPeers = orgPeers.concat(otherPeers);
  return new SameOrgQueryHandler(allPeers);
};