midnight-dapp:proof-handlingSkill midnight-dapp:proof-handling

Midnight DApp零知识证明处理技能,专注于在区块链隐私应用中构建、生成和管理零知识证明。该技能涵盖见证数据构建、客户端证明生成、隐私披露用户同意流程、证明超时重试处理以及向用户解释隐私保证等核心功能。适用于开发者在构建基于零知识证明的隐私保护DApp时,确保用户数据安全、提供流畅证明生成体验并实现合规的隐私披露机制。关键词:零知识证明、ZK证明、Midnight DApp、隐私保护、见证构建、证明生成、区块链隐私、客户端证明、披露同意、DApp开发。

DApp开发 0 次安装 1 次浏览 更新于 2/26/2026

name: midnight-dapp:proof-handling description: 在构建ZK证明的见证数据、向用户展示证明生成进度、实现披露同意流程、处理证明超时和重试、或解释隐私保证时使用。

证明处理

在您的Midnight DApp中构建和呈现零知识证明,从构建见证数据到展示证明生成进度以及处理隐私披露。

何时使用

  • 为合约交易构建见证数据
  • 向用户展示证明生成进度
  • 实现披露同意流程
  • 处理证明超时和重试
  • 向用户解释隐私保证

核心概念

见证构建

见证是TypeScript函数,为ZK电路提供私有数据。数据保留在本地——只有加密证明上链。

// 见证提供私有输入 - 电路证明您知道它
const witnesses = {
  get_secret: ({ privateState }: WitnessContext<PrivateState>): bigint => {
    return privateState.secret; // 永远不会离开客户端
  }
};

客户端证明

所有证明生成都在用户设备本地进行:

  1. 绝不在远程服务器上 - 私有数据永远不会离开浏览器
  2. 证明服务器本地运行 - 端口6300上的Docker容器
  3. 需要数秒,而非毫秒 - 向用户展示进度UI
  4. 可能超时 - 为实现可靠性实施重试逻辑

披露用户体验

当电路使用 disclose() 时,用户必须理解他们正在披露什么:

// 这将在链上公开确切值
const disclosedAge = disclose(userAge);

// 用户应该看到:“这将公开您的年龄(25)”

参考文档

文档 描述
witness-fundamentals.md 见证模式和类型映射
client-side-proofs.md 证明服务器设置和性能
disclosure-ux.md 隐私披露UI模式
web3-comparison.md 以太坊签名与Midnight证明对比

示例

示例 描述
witness-builder/ 构建见证对象的函数
proof-progress/ 证明生成进度组件
disclosure-modal/ 隐私披露同意模态框

快速开始

1. 定义见证类型

interface PrivateState {
  secretKey: Uint8Array;
  credentials: Map<string, Credential>;
  balance: bigint;
}

2. 实现见证

import type { WitnessContext } from "@midnight-ntwrk/midnight-js-types";

const witnesses = {
  get_balance: ({ privateState }: WitnessContext<PrivateState>): bigint => {
    return privateState.balance;
  },

  get_credential: (
    { privateState }: WitnessContext<PrivateState>,
    credentialId: Uint8Array
  ): Credential => {
    const id = bytesToHex(credentialId);
    const credential = privateState.credentials.get(id);
    if (!credential) {
      throw new WitnessError("凭证未找到", "NOT_FOUND");
    }
    return credential;
  }
};

3. 展示证明进度

function TransferButton({ onTransfer }) {
  const { status, progress, error, generateProof } = useProofStatus();

  const handleClick = async () => {
    const result = await generateProof(async () => {
      return contract.callTx.transfer(recipient, amount, witnesses);
    });

    if (result.success) {
      onTransfer(result.transaction);
    }
  };

  return (
    <div>
      <button onClick={handleClick} disabled={status === "generating"}>
        {status === "generating" ? "正在生成证明..." : "转账"}
      </button>
      {status === "generating" && <ProgressBar value={progress} />}
      {error && <ErrorMessage error={error} />}
    </div>
  );
}

4. 处理披露同意

function DisclosureFlow({ disclosures, onConfirm, onCancel }) {
  return (
    <DisclosureModal
      disclosures={disclosures}
      onConfirm={onConfirm}
      onCancel={onCancel}
    >
      <p>此交易将披露以下信息:</p>
      <ul>
        {disclosures.map((d) => (
          <li key={d.field}>
            <strong>{d.label}</strong>: {d.value}
          </li>
        ))}
      </ul>
    </DisclosureModal>
  );
}

常见模式

带验证的见证

const witnesses = {
  get_credential: (
    { privateState }: WitnessContext<PrivateState>,
    credentialId: Uint8Array
  ): Credential => {
    const credential = privateState.credentials.get(bytesToHex(credentialId));

    if (!credential) {
      throw new WitnessError("凭证未找到", "NOT_FOUND");
    }

    // 返回前验证
    if (credential.expiry < BigInt(Date.now())) {
      throw new WitnessError("凭证已过期", "EXPIRED");
    }

    return credential;
  }
};

用于外部数据的异步见证

const witnesses = {
  get_oracle_price: async (
    { privateState }: WitnessContext<PrivateState>,
    tokenId: Uint8Array
  ): Promise<bigint> => {
    const response = await fetch(`/api/price/${bytesToHex(tokenId)}`);
    if (!response.ok) {
      throw new WitnessError("价格不可用", "ORACLE_ERROR");
    }
    const { price } = await response.json();
    return BigInt(price);
  }
};

证明重试逻辑

async function submitWithRetry(
  buildTx: () => Promise<Transaction>,
  maxRetries = 3
): Promise<string> {
  let lastError: Error | null = null;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const tx = await buildTx();
      const provenTx = await walletAPI.balanceAndProveTransaction(tx, newCoins);
      return await walletAPI.submitTransaction(provenTx);
    } catch (error) {
      lastError = error as Error;

      // 用户拒绝时不重试
      if (error.message.includes("rejected")) {
        throw error;
      }

      // 重试前等待,使用指数退避
      if (attempt < maxRetries) {
        await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, attempt)));
      }
    }
  }

  throw lastError ?? new Error("证明生成失败");
}

安全考虑

  1. 绝不持久化见证数据 - 仅将私有状态保留在内存中
  2. 清除敏感数据 - 使用后尽可能将数组清零
  3. 验证所有输入 - 在见证中检查参数有效性
  4. 优雅处理错误 - 不要在错误消息中泄露信息
  5. 披露需用户同意 - 始终展示将要披露的内容

相关技能

  • wallet-integration - 证明生成前需要钱包连接
  • state-management - 管理见证的私有状态
  • transaction-flows - 提交已证明的交易
  • error-handling - 证明错误消息和恢复

相关命令

  • /dapp-check - 验证证明服务器配置
  • /dapp-debug proofs - 诊断证明生成问题