零知识证明:ZoKrates 使用和案例说明

ZoKrates 是一个针对以太坊的 zk-SNARKs 工具类项目, 利用它可以比较容易地实现链下生成零知识证明, 再提交到以太坊链上用智能合约进行验证的功能.
ZoKrates 本身主要用 rust 实现, 底层 zkp 方案依赖于 bellman, libsnark 实现.
本文简要介绍 ZoKrates 的使用以及 V 神 zkSNARKs 相关文章里的一个案例用 ZoKrates 实现说明.
关于 zkSNARKs, ZoKrates 更详细的介绍和说明可以参考文末参考资料.
命令行说明
compile
编译源码: 将 zok 源码压平成逻辑条件语句形式, 生成两个文件(默认 out, out.ztf), 其中 .ztf 文件是可读版.
命令样例: zokrates compile [-o out] -i sample.zok
zok 源码文件 sample.zok:
def main(private field a, field b) -> (field):
  field result = if a * a == b then 1 else 0 fi
  return result
生成的 ztf 文件:
def main(_0, _1) -> (1):
  (1 * _0) * (1 * _0) == 1 * _4
  # _2, _3 = Rust::ConditionEq((-1) * _1 + 1 * _4)
  ((-1) * _1 + 1 * _4) * (1 * _3) == 1 * _2
  (1 * ~one + (-1) * _2) * ((-1) * _1 + 1 * _4) == 0
  (1 * ~one) * (1 * ~one + (-1) * _2) == 1 * ~out_0
   return ~out_0
setup
可信启动: 执行可信启动(trusted setup)操作, 生成 CRS(Common Reference String).
命令样例: zokrates setup [-i out] [-s G16] [-p proving.key] [-v verification.key] 
setup 输入为 compile 生成的 out, 在生成 CRS 前会会先生成 R1CS 等操作, 最终输出两个文件: proving.key 和 verification.key.
可以用参数 -s 指定采用的零知识证明方案, 目前支持 G16、PGHR13、GM17, 默认为 G16.
生成的 verification.key:
vk.alpha = 0x0570e3c9d82099fa7387140f1c16a521c600c62109cdc023a7dafd45698f22de, 0x04ad4ecb02c73f674c264eab35f2ef763f0fc7b758fa554385cc419c72fa4b8c
vk.beta = [0x0c81950e8454719fcdae412f554bac62071f0dcfbc0df5a10dbfe3530b5b980c, 0x1cd6933716f38d239eb6fd2416c22f1e7ba3bbd367b007e9d94a29c243486858], [0x1bdcbd9b7306f051de4ff26979fddac6e376a94416521f30233451ef03d59b75, 0x1165ef2b53fe7172d3bada68df3200db2931c991b4602299127f0195983575b8]
vk.gamma = [0x0fa0718df95c498bc1f50ec2a5f4f7b1214bf0b5c3f28d439740588a4c42ece6, 0x013f7042472ce1b5e8c546d18761c3a27ae786b1258050cc486d0258cc401e24], [0x0cf0f1d42a320b73e579d7828712c08b663ecec595bb3d893f10acebbb5d8658, 0x296b67405655ee6d0edd69fbfcab20f400da0ad9b87cbca98a4fa96e133773e0]
vk.delta = [0x063fa2f6cd8ad00b35af4348fd7e627dcf8bb3530d0e50e23046abe054900d06, 0x06f008e36d2c0c05978785e4cce3dfc169fbb078aa891dc83a14f96d56572d52], [0x1347ce64162496c450dc725aef975e2a0744712a487106739883df311af91057, 0x234e180f25487b444d0f1f8ac0c02b45ceeffc0ce171e2507e270d1aea44d67f]
vk.gamma_abc.len() = 3
vk.gamma_abc[0] = 0x18149018b7c0ee29906ef20c544efb732a822d4a562100c20aef1a301bb1dc20, 0x18a913b2026e7fb7fccef60c0b577dbd7a80b95c7575de8a5314ed88f0a1ae1b
vk.gamma_abc[1] = 0x17b95318ccf8382281b3f6811a44f107d3df1984bb8ce3d25bda15966e4ac243, 0x05c08e28b1d45692a3bb7e51a7a784553695ee373047a095560d9702f02f46bc
vk.gamma_abc[2] = 0x2c820e3b6ca96c8a98a98085fe4d29b237a23dac00abb0cc5dcb7a96d45fa042, 0x00937b10d0c409ab576d9739f895b4e8a4f0b3f0daa8bde00d83239a691e3471
compute-witness
计算 witness: 这是生成证明的一个步骤, 根据原计算问题的输入生成 witness.
命令样例: zokrates compute-witness [-i out] [-o witness] -a 4 16
命令的输入为 compile 生成的 out, 以及计算问题(函数)的输入参数; 输出一个文件, 默认文件名为 witness.
generate-proof
生成证明: 基于约束系统(计算问题)以及 witness 生成对应的零知识证明.
命令样例:zokrates generateproof [-i out] [-p proving.key] [-w witness] [-s G16] [-j proof.json]
命令的输入为 compile 和 setup 生成的(out, proving.key) 以及 compute-witness 生成的(witness); 输出证明文件, 默认为 proof.json.
proof.json 样例:
{
  “proof”: {
    “a”: [“0x00079e628b230588e245fda5edf8866ce711c7de8d5cc37cf54b80f51aa37c37”, “0x167b3260cf4af269c4914dfbe180477097a988f6bb778705e76e489a42e0bfd0”],
    “b”: [[“0x1afb08d6f40254ef449b504d1f6530879ddb3effc7b729620a736701dec6d6dc”, “0x1e89599001bc3572bfefaf041c624d72be516ca3cb6be479fb4daf0442566c4d”], [“0x24f83adc32a54235ad72c7e61e93990c1d97c3ef72417892b7bb01f64a53c42e”, “0x0adc5cb25f7de45483b027f9afa12eff92a910d277ccc28bf39e6d79f7c5569f”]],
    “c”: [“0x2019bdafca45c38f26a5e3ab23764e2650d13f0b64c2b4dee3ad705fbacbbafd”, “0x26aa019ae205ae1b91bf8dab69d16e24395b3e17983377f25d2f3730e53ae17d”]
  },
  “inputs”: [“0x0000000000000000000000000000000000000000000000000000000000000010”, “0x0000000000000000000000000000000000000000000000000000000000000001”]
}
export-verifier
导出验证合约: 根据 verification.key 生成 verifier 合约.
命令样例: zokrates export-verifier [-i verification.key] [-s G16] [-o verifier.sol]
证明方案
docs: Proving Schemes
当前 ZoKrates 支持三个证明方案:
1. PGHR13: pghr13, 依赖于 libsnark
2. G16: groth16, 默认方案, 不依赖 libsnark, 依赖于 bellman
3. GM17: gm17, 依赖于 libsnark
setup, export-verifier, generate-proof 三个命令可以通过 -s 指定证明方案, 但须保持一致.
要使用 PGHR13 或者 GM17, 需要在编译生成 zokrates 命令时增加额外的参数来加入 libsnark: cargo +nightly -Z package-features build –release –package zokrates_cli –features=”libsnark”
说明: G16 存在延展性(malleability)问题, 推荐使用 gm17.
验证方法
验证方法就是调用 export-verifier 命令生成的 verifier.sol 合约的 verifyTx 函数.
function verifyTx(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[2] memory input) public returns (bool r)
函数的参数来自 generte-proof 命令的输出结果.
案例: x^3+x+3=35
本案例来自 vitalik 的 QAP 文章:
· Quadratic Arithmetic Programs: from Zero to Hero
· 白话零知识证明 (二)
执行过程:
1. 使用 DSL 来对问题编码:
· main.zok
def main(private field x) -> (bool):
  x*x*x + x + 5 == 35
  return true
2. 编译:
· 命令: zokrates compile -i main.zok
· 结果:
Compiling main.zok
Compiled program:
def main(_0) -> (1):
    (1 * _0) * (1 * _0) == 1 * _1
    (1 * _1) * (1 * _0) == 1 * _2
    (1 * ~one) * (35 * ~one) == 5 * ~one + 1 * _0 + 1 * _2
    (1 * ~one) * (1 * ~one) == 1 * ~out_0
     return ~out_0
Compiled code written to ‘out’
Human readable code to ‘out.ztf’
Number of constraints: 4
· 根据结果可以知道:
~one = 1
~out_0=1
_0 = x
_1 = _0*_1 = x^3
3. setup
命令: zokrates setup
输出结果:
Performing setup…
def main(_0) -> (1):
    (1 * _0) * (1 * _0) == 1 * _1
    (1 * _1) * (1 * _0) == 1 * _2
    (1 * ~one) * (35 * ~one) == 5 * ~one + 1 * _0 + 1 * _2
    (1 * ~one) * (1 * ~one) == 1 * ~out_0
    return ~out_0
WARNING: You are using the G16 scheme which is subject to malleability. See zokrates.github.io/reference/proving_schemes.html#g16-malleability for implications.
Has generated 5 points
4. 生成验证合约
命令: zokrates export-verifier
5. 编译并发布合约
· 使用 remix 和 Metamask 来编译和发布.
· Rinkby 上发布并源码认证的合约: 0x53577a6d35da004d1c76397959c594d0426ecd1a
https://rinkeby.etherscan.io/address/0x53577a6d35da004d1c76397959c594d0426ecd1a
6. 生成正确的 witness 和 proof
· 命令: zokrates compute-witness -a 3 -o witness.good
Computing witness…
Witness:
[true]
· witness.good:
~out_0 1
~one 1
_0 3
_1 9
_2 27
7. 生成正确的 proof
· 命令: zokrates generate-proof -w witness.good -j proof-good.json
Generating proof…
WARNING: You are using the G16 scheme which is subject to malleability. See zokrates.github.io/reference/proving_schemes.html#g16-malleability for implications.
generate-proof successful: true
· proof-good.json
{
    “proof”: {
        “a”: [“0x110332d0c8e1d05ce9404fd93105c3fe4584d80ccb5ac717acadc7ebd0fc980e”, “0x030f5b5816274abb8eef5be4fd24991d0de600916d02338be72374e7b9bfdf6c”],
        “b”: [[“0x047db95379f2de8e6753fc26dfd0254d6f634526062ae70e3545bd50bf8be5df”, “0x187c8851eae58a5713dd46f18dc9598b67598a248edb0cee6b68f5d080f01e9b”], [“0x05ae13857c3b68ea1728fcdf4f41883c78be1fda50b4a54f0aab8c27aa63fdf2”, “0x2402254c268795bb9c1ef973e4fb4a5eacc8e230793a1d6b8208666b436da00a”]],
        “c”: [“0x141b43d522d0cf6912c12efbb5c5bb783a21a1392573d9073db93f284ba6b008”, “0x2aa86a426ca6b2deeeaff97cbeef7299082cc9753635bb8dee1c8d87e9ef53c2”]
    },
    “inputs”: [“0x0000000000000000000000000000000000000000000000000000000000000001”]
}%
8. 调用 verifier 合约进行验证
交易: 0xf84aa9f7cc7b7ef7896f77b295bcce657c18c6053fe33eba0905f86131c6851f
https://rinkeby.etherscan.io/tx/0xf84aa9f7cc7b7ef7896f77b295bcce657c18c6053fe33eba0905f86131c6851f
根据 verifier.sol, 交易有抛出事件就说明验证通过.
9. 生成错误的 witness 和 proof
命令: zokrates compute-witness -a 4 -o witness.bad
Computing witness…
Execution failed: Expected 35 to equal 73
witness.bad 无法生成.
10. 伪造错误的 proof, 基于上面的 proof-good.json 的数据进行任意修改.
11. 调用 verifier 合约进行验证
· 交易: 0x669936c392f39a10bc0ee594e1472b87a93b0370ed329ab03892e5f702fb1ea3
https://rinkeby.etherscan.io/tx/0x669936c392f39a10bc0ee594e1472b87a93b0370ed329ab03892e5f702fb1ea3
· 交易: 0x89689b344f88751995641f8f682768aaf99b770f70f404bd003fedc6ecb50393
https://rinkeby.etherscan.io/tx/0x89689b344f88751995641f8f682768aaf99b770f70f404bd003fedc6ecb50393
· 有些交易会失败, 有些交易会成功, 但是成功的交易也是没有事件的(说明验证没通过, 函数返回 false)
参考资料
· ZoKrates – github
https://github.com/Zokrates/ZoKrates
· zokrates docs
https://zokrates.github.io/
· bellman
https://github.com/zkcrypto/bellman
· libsnark
https://github.com/scipr-lab/libsnark
· VitalikButerin – Quadratic Arithmetic Programs: from Zero to Hero
https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649
· 零知识证明 – 深入理解 ZoKrates
https://learnblockchain.cn/2019/07/24/zokrates/