
Solana ZK Token Proof 原生程序基于 Curve25519 的零知识证明验证指令全解析【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solanaSolana ZK Token Proof 原生程序Program IDZkTokenProof1111111111111111111111111111111是一组面向 Pedersen 承诺与 ElGamal 加密的零知识证明验证指令集合最初为 SPL Token 2022 机密扩展Confidential Extension设计也可在更通用的场景下独立使用。本文以 docs/src/runtime/zk-token-proof.md 为主线结合 programs/zk-token-proof/src/lib.rs 与 zk-token-sdk 的源码实现系统梳理其密码学基础、指令编码格式、Context Data 存储机制以及各证明指令的语义与开销帮助读者理解并直接上手使用该程序。程序概览与定位ZK Token proof 程序是 Solana 运行时内置的原生程序Native Program之一其声明位于 zk-token-sdk/src/zk_token_proof_program.rs// Program Id of the ZkToken Proof program solana_program::declare_id!(ZkTokenProof1111111111111111111111111111111);程序的核心职责是验证零知识证明而不是生成证明。证明的生成发生在链下客户端如 SDK 层链上程序仅负责以确定性的方式校验证明的正确性从而让链上逻辑可以安全地信任证明所声明的结论。程序支持的所有指令枚举定义在 zk-token-sdk/src/zk_token_proof_instruction.rs 的ProofInstruction中。程序最初是为 SPL Token 2022 的机密扩展Confidential Extension服务的当用户进行机密转账时转账方需要在链下生成一系列零知识证明交给本程序验证通过后SPL Token 2022 程序才会接受相应的机密余额更新。不过这些证明指令本身是通用的任何需要验证 Pedersen 承诺或 ElGamal 密文性质的应用都可以复用。密码学基础Pedersen 承诺与扭曲ElGamal 加密程序的全部证明都建立在两种密码学原语之上并且统一使用椭圆曲线curve25519Ristretto 群作为底层实现。ElGamal 加密ElGamal 加密是公钥加密体制的经典实例化。一个 ElGamal 密钥对由一个 ElGamal 公钥与一个 ElGamal 私钥组成消息可以在公钥下被加密产生一个密文ciphertext密文可以用对应的 ElGamal 私钥解密还原消息。程序中使用的是 curve25519 上的扭曲 ElGamal 加密twisted ElGamal encryption变体相关数学说明见仓库内的 twisted_elgamal.pdf。SDK 中对应的实现位于 zk-token-sdk/src/encryption/elgamal.rs基准测试中可见其典型用法verify_proofs.rslet keypair ElGamalKeypair::new_rand(); let ciphertext keypair.pubkey().encrypt(0_u64);Pedersen 承诺Pedersen 承诺方案是密码学承诺方案的经典实例化。承诺允许用户把一个消息“包装”成承诺值并留待以后揭示与密文类似承诺值不会泄露其所包含消息的任何信息隐藏性同时承诺是绑定的binding用户无法事后更改承诺中包裹的原始值。SDK 中的 Pedersen 承诺实现位于 zk-token-sdk/src/encryption/pedersen.rs基准测试中的典型用法如下verify_proofs.rslet amount std::u64::MAX; let (commitment, opening) Pedersen::new(amount); let proof_data RangeProofU64Data::new(commitment, amount, opening).unwrap();进一步阅读材料原文档推荐了以下资料用于深入了解这两类方案的细节twisted_elgamal.pdf关于扭曲 ElGamal 加密的数学笔记位于本仓库 docs/src/runtime/zk-docs 目录下SPL Token 2022 机密扩展的技术概述对应token/zk-token-protocol-paper/part1.pdf属于 SPL 仓库内容Pretty Good Confidentiality 研究论文ePrint 2019/319即扭曲 ElGamal 方案的原始出处。ZK Token proof 程序针对 Pedersen 承诺与 ElGamal 加密提供了多种证明验证指令。例如VerifyRangeProofU64验证一个零知识证明证明某 Pedersen 承诺中包含的是一条无符号 64 位整数消息VerifyPubkeyValidity验证一个零知识证明证明某 ElGamal 公钥是格式正确的公钥且证明者知道对应的私钥。Context Data上下文与证明数据的二分结构与每个 ZK Token proof 指令关联的证明数据在逻辑上分为两个部分context 组件零知识证明所声称的数据。例如VerifyRangeProofU64指令数据的 context 是承载无符号 64 位整数的 Pedersen 承诺VerifyPubkeyValidity指令数据的 context 是格式正确的 ElGamal 公钥。proof 组件真正用于证明 context 数据各项性质的数学片段。程序处理一条证明指令分为两步实现见 programs/zk-token-proof/src/lib.rs验证与该证明指令关联的零知识证明数据如果指令中指定了则将 context 数据写入专用的 context state 账户。用法一不产生 context state 账户内联验证使用证明指令最简单的方式就是执行而不产生 context state 账户。此时证明指令可以作为更大的 Solana 交易的一部分与其他程序的指令混合编排。其他程序需要直接从证明指令的指令数据中读取 context 数据并用于自身逻辑。这种方式适用于大多数场景证明数据以指令判别字节 证明数据的形式直接编码在指令数据中。编码逻辑见 zk_token_proof_instruction.rslet mut data vec![ToPrimitive::to_u8(self).unwrap()]; data.extend_from_slice(bytes_of(proof_data));即第一个字节是指令判别符对应ProofInstruction枚举的变体序号随后紧跟经过bytemuck序列化的Pod证明数据。用法二产生 context state 账户持久化存储另一种方式是执行证明指令并产生 context state 账户。此时与证明指令关联的 context 数据在包含该指令的交易执行结束后依然持久存在。这在以下场景中尤其有用需要从PDAProgram Derived Address发起零知识证明时证明数据太大无法塞进单笔交易时——此时可将证明预先写入账户指令数据中只携带账户内偏移量。注意context state 账户必须预先分配为与特定证明类型所期望的 context 数据完全一致的大小之后才能被包含进证明验证指令中参见 zk_token_proof_instruction.rs 的模块注释。证明指令的两种数据携带方式从源码实现看lib.rs程序通过检查指令数据长度来判断证明数据以何种方式提供指令数据恰好为 5 字节1 字节判别符 4 字节无符号小端u32偏移量时程序假定指令携带的第一个账户中存有零知识证明数据并校验该账户数据在指定偏移处的T字节序列此时还要求功能开关enable_zk_proof_from_account已激活见 lib.rs其他长度时程序假定证明数据直接编码在指令数据中ProofInstruction::proof_data::T, U(instruction_data)从第 1 字节起反序列化。对应地SDK 提供两个编码入口zk_token_proof_instruction.rsencode_verify_proof证明作为指令数据内联encode_verify_proof_from_account证明预先写入账户指令数据为判别符 u32 偏移量并在账户列表中追加证明账户。Context State 账户的结构与生命周期账户数据布局context state 账户的数据结构定义在 zk-token-sdk/src/zk_token_proof_state.rspub struct ProofContextStateT: Pod { /// The proof context authority that can close the account pub context_state_authority: Pubkey, // 32 字节可关闭该账户的权限 /// The proof type for the context data pub proof_type: PodProofType, // 1 字节证明类型判别 /// The proof context data pub proof_context: T, // 各证明类型对应的 context 数据 }即账户数据 Pubkey(32B) proof_type(1B) proof_context(可变长)。写入逻辑位于 lib.rs当指令携带的账户数超过验证所需的最小账户数时程序会校验 context state 账户的所有者必须是本程序InvalidAccountOwner否则报错通过ProofContextStateMeta::try_from_bytes解析账户头部的元数据校验proof_type为Uninitialized否则报AccountAlreadyInitialized用ProofContextState::encode编码context_state_authority proof_type context_data并写入账户同时要求账户长度与编码后数据长度一致。CloseContextState 指令程序还提供了CloseContextState指令用于关闭 context state 账户让账户所有者取回存储押金lamports。其账户约定见 zk_token_proof_instruction.rs账户 0[writable]要关闭的 proof context 账户账户 1[writable]lamports 的目的账户账户 2[signer]context 账户的所有者context state authority。实现逻辑见 lib.rs要求 owner 必须是签名者且与账户中记录的context_state_authority一致随后将 lamports 转移到目的账户、把账户数据长度清零并归还给系统程序system_program。注意proof_context_account_pubkey destination_account_pubkey时会被拒绝InvalidInstructionData。Proof Instructions 全览ZK Token proof 程序支持的零知识证明指令可分为几大类。以下语义说明来自原文档与 ProofInstruction 枚举注释。关于 ElGamal 加密的证明VerifyPubkeyValidity公钥有效性证明证明一个 ElGamal 公钥是格式正确的公钥且证明者知道对应的私钥数学描述与安全性证明见 pubkey_proof.pdf。VerifyZeroBalance零余额证明证明一个 ElGamal 密文加密的数值是零数学描述与安全性证明见 zero_proof.pdf。相等性证明Equality proofsVerifyCiphertextCommitmentEquality密文-承诺相等性证明证明一个 ElGamal 密文与一个 Pedersen 承诺编码的是同一条消息数学描述见 ciphertext_commitment_equality.pdf。VerifyCiphertextCiphertextEquality密文-密文相等性证明证明两个 ElGamal 密文加密的是同一条消息数学描述见 ciphertext_ciphertext_equality.pdf。范围证明Range proofs范围证明针对 Pedersen 承诺定义用于证明承诺中的数值落在指定范围内、不存在负数溢出等问题VerifyRangeProofU64证明单个 Pedersen 承诺持有无符号 64 位整数VerifyBatchedRangeProofU64针对承诺序列[C_1, ..., C_N]与位长序列[n_1, ..., n_N]的批量范围证明证明每个C_i都是位长为n_i的正数。批量验证比逐条验证更高效。位长总和为 64 位例如可证明两个承诺各自持有 32 位正数VerifyBatchedRangeProofU128批量位长总和 128 位例如可证明两个承诺各自持有 64 位正数VerifyBatchedRangeProofU256批量位长总和 256 位例如可证明四个承诺[C_1, C_2, C_3, C_4]各自持有 64 位正数。该指令与VerifyTransferWithFee、VerifyFeeSigma一样依赖功能开关enable_zk_transfer_with_fee激活后才可用见 lib.rs。面向 SPL Token 2022 的组合型证明以下证明是若干子证明的集合服务于 SPL Token 2022 机密扩展的具体指令VerifyWithdrawSPL Token 2022 机密扩展Withdraw指令所需的证明集合VerifyTransfer不带转账费用的Transfer指令所需的证明集合VerifyTransferWithFee带转账费用的Transfer指令所需的证明集合依赖enable_zk_transfer_with_fee功能开关VerifyGroupedCiphertext2HandlesValidity分组 ElGamal 密文有效性证明证明该密文可以被其解密句柄对应的私钥解密例如目标方与审计方共享一个密文VerifyBatchedGroupedCiphertext2HandlesValidity两个使用同一组 ElGamal 公钥加密的分组密文有效性的批量证明比两个独立证明更短更高效VerifyFeeSigma证明 SPL Token 2022 编码转账费用的 Pedersen 承诺是良构的依赖enable_zk_transfer_with_fee功能开关。这些证明指令的账户约定高度一致见 ProofInstruction 枚举注释账户 0[]可选用于读取证明的账户账户 1[writable]可选proof context 账户账户 2[]可选proof context 账户所有者。指令数据要么是i. 具体的 ProofData 结构证明内联要么是ii. u32 字节偏移证明存于账户。指令的计算单元CU开销在链上使用证明指令时需要为程序分配足够的计算单元预算。程序在进入每个分支时都会调用consume_checked预扣固定 CU见 lib.rs 与指令分发表。以下常量全部来自 lib.rs可直接作为预算参考指令固定计算单元CUCloseContextState3,300VerifyPubkeyValidity2,600VerifyZeroBalance6,000VerifyCiphertextCommitmentEquality6,400VerifyGroupedCiphertext2HandlesValidity6,400VerifyFeeSigma6,500VerifyCiphertextCiphertextEquality8,000VerifyBatchedGroupedCiphertext2HandlesValidity13,000VerifyRangeProofU64105,000VerifyWithdraw110,000VerifyBatchedRangeProofU64111,000VerifyBatchedRangeProofU128200,000VerifyTransfer219,000VerifyBatchedRangeProofU256368,000VerifyTransferWithFee407,000可以看到范围证明与组合型转账证明的验证开销显著高于简单的公钥有效性、零余额等证明这符合其底层数学复杂度的差异。程序入口指令分发与限制程序入口通过declare_process_instruction!宏定义lib.rs整体流程为读取指令数据并解析ProofInstruction判别符ProofInstruction::instruction_type限制内部调用如果调用栈高度不是交易顶层TRANSACTION_LEVEL_STACK_HEIGHT且指令不是CloseContextState则返回UnsupportedProgramId——即证明验证指令不允许作为其他程序内部调用的 inner instruction只有CloseContextState例外见 lib.rs依据判别符分发到对应的process_verify_proof::T, U泛型处理函数或process_close_proof_context。其中process_verify_proof的泛型约束为T: Pod ZkProofDataU、U: PodT即各证明类型的 ProofData 结构如ZeroBalanceProofData、RangeProofU64DataU为对应的 Context 结构如ZeroBalanceProofContext、RangeProofContext二者间的绑定关系由 SDK 中的ZkProofDatatrait 定义。性能基准如何在链下验证证明programs/zk-token-proof/benches/verify_proofs.rs 使用 Criterion 提供了完整的证明构造与验证基准覆盖上表全部 14 个验证场景criterion_group!(benches, ...)中列出。每个基准均演示了标准用法可直接作为 SDK 调用的示例参考生成随机 ElGamal 密钥对ElGamalKeypair::new_rand()用公钥加密消息keypair.pubkey().encrypt(amount)构造 Pedersen 承诺let (commitment, opening) Pedersen::new(amount)构造证明数据并验证ProofData::new(...).unwrap()后调用proof_data.verify_proof().unwrap()。例如WithdrawData的构造需要传入提现金额、当前余额与当前密文verify_proofs.rsFeeSigmaProofData则需要手续费率、最大手续费与增量费等多个参数verify_proofs.rs可以帮助理解每个组合型证明的输入要素。运行该基准在仓库根目录执行cargo bench -p solana-zk-token-proof-program使用流程小结综合原文档与源码在 Solana 上使用 ZK Token proof 程序的完整路径为链下构造证明在客户端使用solana-zk-token-sdkzk-token-sdk/src 下的encryption、range_proof、sigma_proofs模块构造承诺/密文与相应的证明数据选择携带方式证明较小则通过encode_verify_proof内联进指令数据证明较大如批量范围证明则先写入账户通过encode_verify_proof_from_account携带账户与偏移量选择是否持久化 context无需持久化时指令不附带 context 账户其他程序从指令数据中读取 context 使用需要持久化如 PDA 场景时预先按精确大小分配 context state 账户并附带在指令账户列表中提交交易为证明指令预留上表对应的 CU 预算注意证明验证指令只能位于交易顶层回收押金可选context 使用完毕后由context_state_authority签名执行CloseContextState取回 lamports。无论是接入 SPL Token 2022 机密转账还是在自定义协议中复用 Pedersen/ElGamal 证明ZK Token proof 程序都提供了一套通用、可组合的链上验证基础设施。【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考