本实例,将实现一个在区块链用例中非常典型的操作:证明给定哈希摘要的原像知识。 特别是,假设证明者 (Peggy )向验证者 (Victor )毫无疑问地证明她知道摘要的哈希原像 ,但没有透露原像是什么。
本实例以zokrates为ZKP工具(iden3亦可)。
1. 计算hash 我们将通过使用 ZoKrates 计算任意选择的原像的哈希值来开始本教程,在本例中,原像内容为数字 5 。
首先,我们创建一个名为 hashexample.zok 的新文件,内容如下:
1 2 3 4 5 6 import "hashes/sha256/512bitPacked" as sha256packed; def main(private field a, private field b, private field c, private field d) -> field[2 ] { field[2 ] h = sha256packed([a, b, c, d]); return h; }
第一行从 ZoKrates 标准库导入 sha256packed 函数。
sha256packed 是一个 SHA256 的实现。它是这样工作的:我们想将 512 位的输入 传递给 SHA256。 但是,由于其背后使用的基础字段的原因,Zokrates中字段值只能容纳 254 位,无法容纳256位的输入。因此,我们使用四个字段元素,每个元素编码 128 位,来表示我们的输入。然后这四个元素在 ZoKrates 中连接起来并传递给 SHA256。 鉴于生成的散列长度为 256 位,我们将其一分为二并将每个值作为 128 位数字返回。
如上,代码实际上只是使用 sha256packed,返回计算出的哈希值。
1.1 首先,我们使用编译命令将程序编译成一个运算电路。 1 zokrates compile -i hashexample.zok
1.2 第二步,我们可以使用以下命令创建见证文件 1 zokrates compute-witness -a 0 0 0 5
使用标志 -a 我们将参数传递给程序。 回想一下,我们的目标是计算数字 5 的哈希值。因此我们将 a、b 和 c 设置为 0 ,将 d 设置为 5 。
此时,我们可以检查 witness 文件中的返回值:
1 2 3 grep '~out' witness ~out_0 263561599766550617289250058199814760685 ~out_1 65303172752238645975888084098459749904
因此,通过将输出连接为 128 位数字,我们得到以下值作为我们选择的原像的哈希值:
1 2 3 4 5 6 7 8 9 10 11 // 计算5 的sha256 import hashlib preimage = bytes .fromhex('00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05' ) bin (int (preimage.hex (), 16 )) //binary representation of pre-image //output is '0b101' hashlib.sha256(preimage).hexdigest() //compute hash //output is //'c6481e22c5ff4164af680b8cfaa5e8ed3120eeff89c4f307c4a6faaae059ce10'
此处,愿像的hash值‘c6481e22c5ff4164af680b8cfaa5e8ed3120eeff89c4f307c4a6faaae059ce10’ 与 witness中out_0 out_1拼接后的hash内容是一致的。
2. 证明原像知识 现在,我们已经看到我们可以使用 ZoKrates 计算哈希值。
让我们回顾一下我们的目标:Peggy 想证明她知道 Victor 选择的摘要的原像,但不透露原像是什么。 现在让我们假设 Victor 选择摘要作为我们在上面的示例中找到的摘要。
为了让它发挥作用,双方必须遵循他们在协议中的角色:
2.1 Victor 必须指定他感兴趣的哈希 因此,我们必须调整由 ZoKrates 编译的 zkSNARK 电路,这样除了计算摘要外,它还根据 Victor 提供的感兴趣的摘要对其进行验证。 这导致 hashexample.zok 的以下更新:
1 2 3 4 5 6 7 8 import "hashes/sha256/512bitPacked" as sha256packed; def main(private field a, private field b, private field c, private field d) { field[2 ] h = sha256packed([a, b, c, d]); assert(h[0 ] == 263561599766550617289250058199814760685 ); assert(h[1 ] == 65303172752238645975888084098459749904 ); return ; }
请注意,现在将 sha256packed 的结果与 Victor 定义的硬编码正确解决方案进行比较。 我们添加的行被视为断言:验证者不会接受不满足这些约束的证明。 显然,如果所有计算的位都相等,则此程序仅返回 1。
2.2 Victor 现在可以编译代码 1 2 3 4 5 zokrates compile -i hashexample.zok zokrates setup zokrates export-verifier
setup 创建一个 verification.key 文件和一个 proving.key 文件。 Victor 把证明钥匙给了Peggy 。
2.3 Peggy 提供正确的原像作为程序的参数 1 zokrates compute-witness -a 0 0 0 5
2.4 Peggy 可以运行命令来构建证明
由于输入在程序中被声明为私有,因此由于协议的零知识属性,它们不会出现在证明中。
ZoKrates 创建一个文件 proof.json,由构成 zkSNARKs 证明的三个椭圆曲线点组成。 Victor 部署的智能合约中的 verifyTx 函数接受这三个值以及一系列公共输入。公共输入数组包括:
main 函数的任何公共输入,声明时没有使用 private 关键字
ZoKrates 函数的返回值
在本示例中,所有输入都是私有的,并且只有一个返回值 1。
2.5 然后 Peggy 可以通过调用 verifyTx 提交她的证明 2.6 Victor 监控验证智能合约以获取 Peggy 交易的返回值 一旦Victor 观察到来自 Peggy 的公共地址的具有真实返回值的交易,Victor就可以确定Peggy拥有他在智能合约中设置的哈希值的有效原像。
3.延伸 在此示例中只涉及两方。这种特殊情况使得处理 zkSNARKs 的信任假设变得容易:只有 Victor 对验证 Peggy 的声明感兴趣,因此他可以信任他对设置阶段的执行。
一般来说,多方可能有兴趣验证Peggy声明的正确性。例如,在基于零知识的加密货币 Zcash 中,每个节点都需要能够验证交易的正确性。为了将设置阶段推广到这些多方用例,需要执行一个通常称为“可信设置”或“仪式”的过程。
即可以实现 - https://willzhuang.github.io/2020/06/23/%E4%BD%93%E9%AA%8Czksync-2/
4.结论 这可能算做一个知识证明,但是题目中的应用实例,是什么呢?
参考链接 - circom - https://willzhuang.github.io/2021/05/06/circom%E8%AF%95%E7%94%A8/