SnarkJS:错误Witness仍通过PLONK验证的问题求助
问题:错误输入仍能通过ZK-SNARK验证?
我是ZK-SNARK新手,正在构建一个验证输入数字是否匹配预定义数值的简单应用,已经按照SnarkJS文档完成了全部步骤。
我的Circuit代码如下:
pragma circom 2.0.0; template IsZero() { signal input in; signal output out; signal inv; inv <-- in!=0 ? 1/in : 0; out <== -in*inv +1; in*out === 0; } template verifier() { signal input a; signal output b; signal c <== 9429723347216097781814234269377894115729242880998740711935743712243189718113; component isz = IsZero(); a - c ==> isz.in; isz.out ==> b; } component main = verifier();
但当我使用错误Witness(比如输入a=5)生成证明,再通过verifier.sol合约验证时,验证居然能通过。
我的操作流程:
- 生成calldata参数:
proof = await snarkjs.plonk.fullProve( { a: 5}, "circuit_js/circuit.wasm", "circuit_final.zkey"); await snarkjs.plonk.exportSolidityCallData(proof["proof"], proof["publicSignals"]);
- 用
verifier.sol验证该calldata,结果验证通过。
请问哪里操作有误?
问题原因与修复方案
核心问题:未约束输出信号的公开性与取值要求
你的电路里虽然定义了输出b,但没有强制要求b必须等于1,也没有将b标记为公开信号。ZK-SNARK验证的是电路内部约束是否满足,当前电路只保证:
- 若
a - c = 0,则b = 1 - 若
a - c ≠ 0,则b = 0
但验证合约并不知道需要检查b的取值——既没让b对验证者可见,也没在电路里加约束强制b必须为1。这就导致a≠c时,只要电路内部逻辑自洽,验证就会通过。
修复步骤
1. 修改Circuit代码,添加约束与公开信号
把输出b设为公开信号,并强制b必须等于1,这样只有当a=c时才能满足所有约束:
pragma circom 2.0.0; template IsZero() { signal input in; signal output out; signal inv; inv <-- in!=0 ? 1/in : 0; out <== -in*inv +1; in*out === 0; } template verifier() { signal input a; // 将b设为公开信号 signal output b; b === 1; // 强制b必须等于1,确保a=c的条件成立 signal c <== 9429723347216097781814234269377894115729242880998740711935743712243189718113; component isz = IsZero(); a - c ==> isz.in; isz.out ==> b; } component main = verifier();
2. 重新生成验证所需文件
修改电路后,需要重新执行以下步骤:
- 编译电路:
circom circuit.circom --r1cs --wasm - 重新生成
circuit_final.zkey(遵循SnarkJS的PLONK流程) - 导出新的
verifier.sol合约
3. 验证逻辑自动生效
修改后,当a≠c时,isz.out会输出0,无法满足b===1的约束,此时snarkjs.plonk.fullProve会直接生成证明失败;若强行提交,verifier.sol也会因为公开信号不符合约束而验证不通过。
内容的提问来源于stack exchange,提问作者Razor Sharp
相关产品推荐
相关产品推荐

