You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hyperledger Fabric外部应用写入多隐式私有集合失败排查

问题概述

外部应用可正常提交写入单个隐式私有数据集合的交易,但写入2个隐式私有集合的交易会执行失败;相同合约逻辑通过VS Code区块链扩展提交时运行完全正常。

环境与场景说明
  • VS Code区块链扩展版本:v2.0.8
  • 扩展自动部署的Fabric运行环境:microfab 0.0.11
  • 网络拓扑:基于扩展2组织模板搭建的2组织Fabric网络
  • 业务逻辑:开发的智能合约需要同时向Org1、Org2对应的两个隐式私有数据集合写入数据
相关代码

智能合约(TypeScript)

@Transaction()
public async createMyPrivateAsset(ctx: Context, myPrivateAssetId: string): Promise<void> {
    const exists: boolean = await this.myPrivateAssetExists(ctx, myPrivateAssetId);
    if (exists) {
        throw new Error(`The asset my private asset ${myPrivateAssetId} already exists`);
    }

    const privateAsset: MyPrivateAsset = new MyPrivateAsset();

    const transientData: Map<string, Uint8Array> = ctx.stub.getTransient();
    if (transientData.size === 0 || !transientData.has('privateValue')) {
        throw new Error('The privateValue key was not specified in transient data. Please try again.');
    }
    privateAsset.privateValue = transientData.get('privateValue').toString();

    const collectionName: string = await getCollectionName(ctx, ctx.clientIdentity.getMSPID());
    await ctx.stub.putPrivateData(collectionName, myPrivateAssetId, Buffer.from(JSON.stringify(privateAsset)));
}

@Transaction()
public async createMyPrivateAssetMultiple(ctx: Context, myPrivateAssetId: string): Promise<void> {
    const exists: boolean = await this.myPrivateAssetExists(ctx, myPrivateAssetId);
    if (exists) {
        throw new Error(`The asset my private asset ${myPrivateAssetId} already exists`);
    }

    const privateAsset: MyPrivateAsset = new MyPrivateAsset();

    const transientData: Map<string, Uint8Array> = ctx.stub.getTransient();
    if (transientData.size === 0 || !transientData.has('privateValue')) {
        throw new Error('The privateValue key was not specified in transient data. Please try again.');
    }
    privateAsset.privateValue = transientData.get('privateValue').toString();

    for (const mspid of ['Org1MSP', 'Org2MSP']) {
        var collectionName: string = await getCollectionName(ctx, mspid);
        await ctx.stub.putPrivateData(collectionName, myPrivateAssetId, Buffer.from(JSON.stringify(privateAsset)));
    }
}

@Transaction(false)
@Returns('MyPrivateAsset')
public async readMyPrivateAsset(ctx: Context, myPrivateAssetId: string): Promise<string> {
    const exists: boolean = await this.myPrivateAssetExists(ctx, myPrivateAssetId);
    if (!exists) {
        throw new Error(`The asset my private asset ${myPrivateAssetId} does not exist`);
    }

    let privateDataString: string;

    const collectionName: string = await getCollectionName(ctx, ctx.clientIdentity.getMSPID());
    const privateData: Uint8Array = await ctx.stub.getPrivateData(collectionName, myPrivateAssetId);

    privateDataString = JSON.parse(privateData.toString());
    return privateDataString;
}

方法说明:

  • createMyPrivateAsset:向调用者所属组织的单个隐式集合写入数据,全场景运行正常
  • createMyPrivateAssetMultiple:同时向Org1、Org2两个组织的隐式集合写入数据,仅在外部应用调用时失败
  • readMyPrivateAsset:读取调用者所属组织隐式集合内的私有数据

测试结果

VS Code扩展环境测试

通过VS Code交易视图提交上述两个交易均运行正常;使用Org1网关提交createMyPrivateAssetMultiple交易后,分别通过Org1、Org2网关调用readMyPrivateAsset均可正确返回私有数据。

外部应用测试

外部应用基于fabric-network ~2.1.0版本开发,其中createMyPrivateAsset交易可正常执行,createMyPrivateAssetMultiple交易执行失败。

网络连接代码

// connection
const gateway: Gateway = new Gateway();
const connectionProfilePath: string = path.resolve(__dirname, '..', connectionFile);
const connectionProfile = JSON.parse(fs.readFileSync(connectionProfilePath, 'utf8'));
const connectionOptions: GatewayOptions = { wallet, identity: identity, discovery: { enabled: true, asLocalhost: true } };
await gateway.connect(connectionProfile, connectionOptions);

// Get the network (channel) our contract is deployed to.
const network = await gateway.getNetwork('mychannel');

// Get the contract from the network.
const contract = network.getContract('private-contract');

单集合写入代码(运行正常)

let transientData = {
  'privateValue': Buffer.from(`Private value for asset ${assetId}`)
};
const trans:Transaction = contract.createTransaction('createMyPrivateAsset');
const buffer: Buffer = await trans.setTransient(transientData).submit(assetId);

多集合写入代码(运行失败)

let transientData = {
  'privateValue': Buffer.from(`Private value for asset ${assetId}`)
};
const trans:Transaction = contract.createTransaction('createMyPrivateAssetMultiple');
const buffer: Buffer = await trans.setTransient(transientData).submit(assetId);
报错现象

使用Org1网关提交多集合写入交易时,应用抛出ENDORSEMENT_POLICY_FAILURE错误,microfab docker容器日志显示无法从远程节点拉取_implicit_org_Org2MSP集合的私有数据,存在空成员资格告警,提交区块时缺失对应私有写集。

根因分析

问题核心是两端的背书节点选择逻辑存在差异:

  1. VS Code区块链扩展提交交易时,默认会将交易发送给通道内所有组织的对等节点执行背书。交易写入两个隐式私有集合时,Org1、Org2的节点均在本地执行合约,各自生成本组织隐式集合的私有写集,不需要跨节点拉取私有数据,因此交易可以正常提交。
  2. 外部应用使用fabric-network 2.1默认配置时,SDK会通过服务发现自动选择满足链码背书策略的节点发送请求。对于默认的“多数组织背书即可”规则,SDK仅会选中满足数量要求的最少节点集合。使用Org1身份提交交易时,SDK默认只选中Org1的对等节点做背书,该节点执行合约时需要写入Org2的隐式私有集合,但节点本身不属于Org2,尝试从Org2节点拉取私有数据时,受microfab环境默认的gossip跨组织通信配置、成员资格校验配置限制拉取失败,最终导致Org2隐式集合的私有写集缺失,触发背书策略失败。
解决方法

优先选择显式指定背书组织的方案,稳定性最高,不需要修改底层网络配置:
在提交多集合写入交易时,调用setEndorsingOrganizations方法显式要求交易必须发送给Org1、Org2的对等节点共同背书,修改后的代码如下:

let transientData = {
  'privateValue': Buffer.from(`Private value for asset ${assetId}`)
};
const trans:Transaction = contract.createTransaction('createMyPrivateAssetMultiple');
const buffer: Buffer = await trans.setTransient(transientData)
  .setEndorsingOrganizations('Org1MSP', 'Org2MSP')
  .submit(assetId);

该配置下两个组织的节点都会在本地执行合约,各自生成本组织隐式集合的私有写集,不需要跨节点拉取私有数据,即可正常完成交易提交。

如果需要使用默认服务发现自动选择节点,需要逐一修正microfab环境的gossip跨组织通信配置、TLS信任配置、成员资格配置,确保Org1节点可以正常从Org2节点拉取授权范围内的私有数据,该方案配置复杂度高,不推荐在开发测试环境使用。


内容的提问来源于stack exchange,提问作者Jacques Sauvé

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 20:01:06