libsnark库代码层次非常清晰。libsnark也给出了SNARK相关算法的全貌,各种Relation,Language,Proof System。为了更好的生成R1CS电路,libsnark抽象出protoboard和gadget,方便开发者快速搭建电路。在阅读该示例代码前,请仔细阅读libsnark的源代码分析:零知识证明 - libsnark源代码分析 唯一有点遗憾的,libsnark没有给个完整的电路构造实例,入门者想搭建自己的电路,刚开始有点摸不着头脑。 为了方便入门者编写自己的电路,同事写了个基于libsnark构造电路,并生成并验证电路的实例: https://github.com/StarLI-Trapdoor/libsnark_sample 入门者,可以基于这个示例开发自己的电路。选择默克尔树作为电路的示例,因为在零知识证明的应用中,大量的使用默克尔树数据结构。 1 代码结构 该示例构造了一条merkle路径的验证电路,生成并验证证明。merkle树的深度为3,并且merkle树的计算采用sha256散列函数。代码结构比较清晰,merkle目录中的main.cpp是主函数。circuit目录下的merklecircuit.h是电路的实现。整个项目用cmake进行编译。 2 电路实现 电路名为MerkleCircuit,主要依赖两个gadget:merkle_authentication_path_variable和merkle_tree_check_read_gadget。merkle_authentication_path_variable提供了merkle树的一条路径。merkle_tree_check_read_gadget检查给定一个叶子节点,是否能计算出正确的root。 实现一个电路,主要实现两个接口函数: generate_r1cs_constraints - 生成R1CS,该电路比较简单,只要让依赖的两个gadget,生成R1CS即可。 generate_r1cs_witness - 给所有的变量进行赋值。该电路,需要赋值的变量有root,leaf(叶子节点),和叶子节点配套的默克尔路径,以及默克尔路径对应的地址信息(也就是每一层的节点的位置,左边还是右边)。 整个电路最复杂的就是电路的构造函数,申请变量,创建gadget。其中重点讲一讲,set_input_sizes函数。libsnark的框架中,使用简单的区分public和private变量的模型。通过set_input_sizes函数,设置前几个变量为public变量。 pb.set_input_sizes(root_digest->digest_size); 也就是说,该电路的公开变量为root的bit个数。 3 生成和验证证明 确定了电路的实现,看看main函数,如何生成和验证证明。 在main函数中定义了merkle树计算需要的一些类型: typedef?libff::default_ec_pp?ppzksnark_ppT; typedef?libff::Fr<ppzksnark_ppT>?FieldT; typedef?sha256_two_to_one_hash_gadget<FieldT>?HashT; FieldT默认是bn256椭圆曲线的的Fr,默克尔树计算采用是sha256算法。 3.1 setup 实现了generate_read_keypair函数,生成pk/vk。仔细看一下generate_read_keypair函数,逻辑简单清晰:构造MerkleCircuit,在生成R1CS后,调用r1cs_gg_ppzksnark_generator生成pk/vk。 protoboard<FieldT>?pb; ????sample::MerkleCircuit<FieldT,?HashT>?mc(pb,?tree_depth); ????mc.generate_r1cs_constraints(); ????r1cs_constraint_system<FieldT>?cs?=?pb.get_constraint_system(); ????return?r1cs_gg_ppzksnark_generator<ppzksnark_ppT>(cs); pk存放在merkle_pk.raw文件中,vk存放在merkle_vk.raw中。 3.2 prove prove逻辑,首先从输入参数构造一个完整的merkle树,并根据输入选定了默克尔路径。通过generate_read_proof函数生成证明。该函数逻辑也比较清晰: protoboard<FieldT>?pb; ????sample::MerkleCircuit<FieldT,?HashT>?mc(pb,?tree_depth); ????mc.generate_r1cs_constraints(); ????mc.generate_r1cs_witness(pb,?leaf,?root,?path,?address,?address_bits); ????return?r1cs_gg_ppzksnark_prover<ppzksnark_ppT>(proving_key,?pb.primary_input(),?pb.auxiliary_input()); 构造MerkleCircuit,在生成R1CS后,设置各个变量的值。接着通过r1cs_gg_ppzksnark_prover生成证明。 3.3 verify 在获知vk,证明以及公开信息(root)的基础上,调用r1cs_gg_ppzksnark_verifier_strong_IC的接口完成验证。这也就是verify_read_proof函数的逻辑。 4 编译和运行 在编译之前,同步该项目依赖的libsnark库: git?submodule?update?--init?--recursive 4.1 编译 mkdir?build;?cd?build;?cmake?.. 编译完成,merkle目录下会生成merkle的可执行文件。 4.2 可信设置(trusted setup) ./merkle?setup 4.3 生成证明 ./merkle?prove?[data1]?[data2]?[data3]?[data4]?[data5]?[data6]?[data7]?[data8]?[index] prove命令,需要提供原始的3层merkle树的8个叶子节点,并指定需要证明的第几个叶子节点对应的路径(index指明)。 4.4 验证 ./merkle?verify?[root] 其中,root信息是在prove中生成过程中打印出来的root信息(也是公开信息)。如果验证通过,就说明存在一条能生成root的merkle路径,虽然没有公开路径的具体信息。 总结: libsnark库代码层次非常清晰,并抽象出protoboard和gadget,方便开发者快速搭建电路。本文给出了一个基于libsnark库开发的完整电路示例。示例实现了3层默克尔树的一条默克尔路径的验证。其中默克尔树采用sha256的散列函数。 —- 编译者/作者:星想法 玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。 |
零知识证明——基于libsnark的电路构造及证明示例
2020-03-01 星想法 来源:区块链网络
LOADING...
相关阅读:
- 美联储政策声明对比:重申动用所有工具,但经济路径取决于疫情;美2020-07-30
- 成都链安智能合约安全实践(二)| 练就“火眼金睛”,真假构造函数一2020-07-28
- 一文读懂什么是哈希函数 | 比特币中的密码学原理2020-07-15
- 漏洞分析 | 一千种死法之 智能合约函数调用错误2020-06-29
- 零知识证明研发机构 StarkWare 启动基于 STARK 的可验证延迟函数服务「V2020-06-24