基于零知识证明的PoR原理与实现
- 资产审计
电路(circuit)定义及说明
BatchCreateUser电路是用于操作账户树和对每个账户的数据进行集合,电路的定义如下:
1.输入:
(1).公共输入:
group_commitment:总承诺哈希值。
(2)秘密输入:
preSmt_root:当前这个批次的默克树的树根值。
nextSmt_root:将当前这个批次的用户进行操作之后的默克树的树根值。
pre_cex_commitment:当前这个批次的用户,交易所的所有资产的承诺值(哈希)。
next_cex_commitment:在当前这个批次的用户进行操作之后,交易所的所有资产的承诺值(哈希)。
Pre_cex_asset:当前这个批次的用户,交易所的所有的币种资产列表。
totalCex_Asset:所有币种折u相加得到的总的交易所资产。
Uesr_Instructions:一组用户的信息表。
△asset:单个用户的资产列表
△totalEquity:用户的折U净资产
△totalDebt:用户的折U总负债
△account_index:用户的索引
△account_idhash:用户的叶子结点的哈希值
△account_proof:用户叶子结点的证明路径
2.约束
(1) group_commitment与preSmt_root,nextSmt_root,pre_cex_commitment,next_cex_commitment计算出的哈希相等。
(2)通过Pre_cex_asset来计算出pre_cex_commitment,两个哈希值相等。
(3)preSmt_root等于Uesr_Instructions中第一个元素的preSmt_root的值。
(4)nextSmt_root等于Uesr_Instructions中最后一个元素的nextSmt_root值。
(5)通过totalCex_Asset计算出totalCexEquity和totalCexDebt,范围属于「0,2^64」,且equit大于Debt。
(6)通过after_asset_list算出next_cex_commitment,这两个next_cex_commitment相等。
(7)每个用户的资产大于其负债,保证每个用户的净资产折U大于0。
(8)用户叶子结点哈希结合默克尔路径计算出的根哈希和nextSmt_root相等
(9)nextSmt_root等于下一组用户的preSmt_root。
当所有的用户都被操作完成后,我们将得到一系列的证明:[proof_0, proof_1, ..., proof_n],同样也有一组承诺:[group_commitment_0, group_commitment_1, ..., group_commitment_n]和[cex_commitment_0, cex_commitment_1, ..., cex_commitment_n],同时还有一组根哈希值:[SMT_root_0, SMT_root_1, ..., SMT_root_n]。所以这些信息都会发布出来,任何用户都可以进行下载,并进行验证。
如何计算witness
Witness 服务用于为 Prover 服务产生生成 witness,作为 Prover 生成证明的输入,最终生成的 witness 输入数据保存在 mysql 中。在生成 witness 时,考虑到庞大用户数和资产数量,我们对用户数据进行了分批计算,对单个用户的资产数据以稀疏向量的形式进行存储以节省存储空间,在从 mysql 表中读取当前批次的 witness 数据时再进行向量稠密化处理,对齐数据维度,以便于进行后续计算。
用于计算 witness 的数据举例如下所示(该数据为每月定期某时刻取出的用户资产快照数据):
Index |
UID |
Btc_balance |
Eth_balance |
… |
Total_Equity |
Total_Debt |
0 |
Id1 |
0.00000032 |
0 |
|
0.008672 |
0 |
1 |
Id2 |
0.00000031 |
0 |
|
0.009716 |
0 |
2 |
Id3 |
0 |
0 |
|
0.00108 |
0 |
其中用户的所有资产的 balance 列表示用户对应币种的净资产数量,total_equity 表示用户所有币种资产的折U总价格,total_debt 表示用户所有币种债务的折U总价格。在计算时,交易所单个币种的总资产数量由用户对应币种资产数量累加得到,在后续设计证明电路时通过相应的约束保证这一计算过程的正确性来防止作弊。由于储备金证明采用的零知识证明算法只能在有限域上进行计算,对于浮点数的处理我们统一采用了乘法因子来进行扩大并对余下精度予以截断处理来进行数据的规范化。
在管理所有用户账户数据时,采用了稀疏默克尔树的树形数据结构来对账户数据进行管理(如上图所示)。稀疏默克尔树和标准默克尔树的区别是其中的数据是有序的(对应于前表中的Index),比如Index为0,3的用户A、D一定在上图中最下方对应的第0个(最左)和第3个叶子节点(最右),如果Index为1、2的用户B、C在交易所中没有资产数据(仅注册账户没有资产),其在稀疏默克尔树中的位置依旧是固定的。这样的设计便于快速校验某个用户是否存在或不存在于证明过程中,使得证明方案更为严谨。
在 witness 数据的计算中需要频繁进行hash运算而hash算法的复杂性会在零知识证明的电路中引入大量约束。为了在保证安全的前提下加速证明计算过程,我们在数字承诺的计算中采取了零知识证明友好的hash函poseidon hash 来用于hash计算,其结构如下图所示。通过hash函数的优化可以在保证证明安全的前提下将计算复杂度减少到原来的1/8。
如何进行Prover 及verifier
火币HTX通过使用keygen程序生成的r1cs电路以及pk,vk文件,生成所需要的proof文件并将其保存在数据库中,让用户能够下载并进行verify。
运行方式:保证当前工作目录在zkmerkleverify下面,也就是src的上层目录下面。
(1)当只使用一台主机开启prover服务的时候,使用如下指令来使用prover服务:
当prover 正常运行后,最后输出显示:
(2)当使用多台主机开启prover服务的时候,所有主机都保证在上面所说的工作目录下,然后使用同样的指令:
当所有主机的这个指令结束之后,都会有下面的输出:
使用如下命令:
来检测是否还有没有运行完的prover程序,如果有就将其运行完成,当该程序正常运行后,最后输出显示:
Verifier工具使用说明
火币HTX使用提供verifier服务为用户提供自行验证PoR验证服务以及userproof验证服务。客户使用prover服务生成的proof表格以及vk进行PoR验证和userproof验证。其中,PoR验证是我们的零知识资产证明验证,userproof是我们的零知识默克尔树验证,用户可以通过如下位置进行下载:
如何使用:
对于用户验证,只需点击下载审计数据(公开),将public-data.zip下载的公共文件解压成一个文件夹保存在一个目录下,该文件夹其中包含三个公共文件(config.json,proof0.csv,zkpor500.vk.save),然后在https://github.com/huobiapi/Tool-Go-MerkleVerify/releases/tag/2.0.0下载不同操作系统对应的运行程序(这里以mac为例)。将运行程序zkverifier-macos-x64.zip解压也保存在同一目录下,结构如下图所示:
用户通过使用如下命令来进行PoR验证:
当mac第一次运行的时候,可能会有如下报错:
只需要在“系统偏好设置”中的安全性与隐私按照如下方式打开“APPstore和被认可的开发者”即可。
或者打开终端输入指令:
sudo spctl --master-disable
即可。
运行时,程序会检查当前目录下含有public-data名字的目录,并选择一个进行读取,对于选取的目录,程序会输出其路径:(如果不能读取到最新下载的目录,可以删除以前下载的目录)
当验证成功 s会显示:
在使用merkle验证之前用户需要下载审计数据(个人):user_config.json:点击下图按钮进行下载:
下载之后,将其放在上面进行PoR验证的目录下面如下图所示:
用户使用如下命令来进行merkle验证:
当验证成功会显示:
如果用户想从头使用全流程内容,可以通过github上的程序使用README的步骤进行操作。
具体的数学原理可以参考《为什么要使用零知识证明来做储备金证明?》。