{"content":{"title":"使用 SnarkJS 和 Circom 进行零知识证明","body":">- 原文链接：https://betterprogramming.pub/zero-knowledge-proofs-using-snarkjs-and-circom-fac6c4d63202 \r\n>- 译文出自：[登链翻译计划](https://github.com/lbc-team/Pioneer)\r\n>- 译者：[张小风](https://learnblockchain.cn/people/74) ，校对：[Tiny 熊](https://learnblockchain.cn/people/15)\r\n>- 本文永久链接：[learnblockchain.cn/article…](https://learnblockchain.cn/article/7403)\r\n\r\n\r\n太长不看版：本文介绍了如何使用 SnarkJS 和 Circom 在 JavaScript 项目中进行零知识证明。零知识证明技术的重要性在于可以证明拥有信息而无需透露，适用于匿名投票等场景。文章介绍了如何使用 Circom 编写电路，生成证明和验证密钥。此外，还讨论了 Circomlib 的使用以及在 JavaScript 中计算 poseidon hash 的方法。 \r\n\r\n\r\n\r\n![img](https://img.learnblockchain.cn/attachments/migrate/1708049803041)\r\n\r\n> [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral) 上的 [Maria Cappelli](https://unsplash.com/@rikku72?utm_source=medium&utm_medium=referral) 拍摄的照片\r\n\r\n##  简介\r\n\r\n[零知识证明](https://learnblockchain.cn/tags/%E9%9B%B6%E7%9F%A5%E8%AF%86%E8%AF%81%E6%98%8E) 技术，尤其是 [zk-SNARK](https://en.wikipedia.org/wiki/Non-interactive_zero-knowledge_proof) ，是加密领域中最令人兴奋的技术之一，原因如下：\r\n\r\n- 你可以证明自己拥有信息，而无需透露它（例如，你可以用它进行匿名投票）。\r\n- 证明很小，易于在区块链上验证，因此可以用于 Rollup。\r\n\r\n> [Rollup](https://learnblockchain.cn/article/1977) 是一种区块链扩展解决方案，其中计算是在链下完成的，并且在一定数量的交易之后，状态会同步回区块链。这种解决方案为你提供了区块链的安全性（在同步之后），但证明所需的空间（和 gas）远小于原始交易。因此，zk-rollup 是区块链的理想扩展解决方案。\r\n\r\n我之前有一篇[文章](https://learnblockchain.cn/article/7402) ，在其中展示了零知识证明是如何通过 Tornado Cash 代币混合器的源代码工作的。如果你对这项技术不熟悉，强烈建议在阅读本文之前阅读该文章。\r\n\r\n在本文中，我将向你展示如何在 JavaScript 项目中使用 zk-SNARK。\r\n\r\n## Circom\r\n\r\n如果你已经阅读了我的[之前的文章](https://learnblockchain.cn/article/7402) ，你就会知道，你需要一个电路来生成零知识证明。电路是系统用来计算输出和证明的巨大数学表达式。零知识证明本身就是证明你已成功进行了计算。\r\n\r\n电路可能非常复杂，但幸运的是，有电路编程语言和库，可以轻松编写自己的电路。我们将使用 [Circom](https://iden3.io/circom)。Circom 是用 Rust 编写的。要安装它，你必须使用以下命令安装 Rust 环境：\r\n\r\n```\r\ncurl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh\r\n```\r\n\r\nRust 安装完成后，克隆 Circom 存储库并构建编译器：\r\n\r\n```\r\ngit clone https://github.com/iden3/circom.git\r\ncd circom\r\ncargo build --release\r\ncargo install --path circom\r\n```\r\n\r\n如果一切顺利，现在你已经安装了 Circom 编译器。\r\n\r\n我们需要的另一样东西是 [circomlib](https://github.com/iden3/circomlib)。Circomlib 是一个带有许多有用的预定义电路的编程库。因此，创建一个空项目，并使用以下代码安装 circomlib：\r\n\r\n```\r\nnpm init\r\nnpm i circomlib\r\n```\r\n\r\n现在，一切都准备好创建我们的电路了。以下是它的样子：\r\n\r\n```\r\npragma circom 2.0.0;\r\n\r\ninclude \"node_modules/circomlib/circuits/poseidon.circom\";\r\n\r\ntemplate PoseidonHasher() {\r\n    signal input in;\r\n    signal output out;\r\n\r\n    component poseidon = Poseidon(1);\r\n    poseidon.inputs[0] <== in;\r\n    out <== poseidon.out;\r\n}\r\n\r\ncomponent main = PoseidonHasher();\r\n```\r\n\r\n这个简单的电路有一个私有输入和输出[信号](https://docs.circom.io/circom-language/signals/) 。我们使用 circomlib 中的 poseidon hasher 生成输入 `hash`。使用这个电路，我们可以证明我们知道给定 `hash` 的原始数据，而无需透露它。\r\n\r\n首先，我们通过 circom 编译器编译电路，它将生成一个 wasm 文件和一个 r1cs 文件。\r\n\r\n```\r\ncircom poseidon_hasher.circom --wasm --r1cs -o ./build\r\n```\r\n\r\n生成的 wasm 和 r1cs 文件可在 build 文件夹中找到。要生成证明，我们需要一个 proving key 文件，要生成此文件，我们需要一个 ptau 文件。可以使用 [snarkjs](https://github.com/iden3/snarkjs) 生成此 ptau 文件，或者你可以下载一个预生成的文件（你可以在 snarkjs 存储库中找到链接）。对于测试，生成的文件对我们来说就足够了，但在你的生产应用中，建议进行仪式并生成自己的 ptau 文件（你可以在我的[之前的文章](https://learnblockchain.cn/article/7402)中了解更多）。\r\n\r\n```\r\nwget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_12.ptau\r\n```\r\n\r\n现在，我们可以使用电路和 ptau 文件生成 proving key（zkey 文件）：\r\n\r\n```\r\nnpx snarkjs groth16 setup build/poseidon_hasher.r1cs powersOfTau28_hez_final_12.ptau circuit_0000.zkey\r\n```\r\n\r\n不建议在生产中使用此 zkey 文件，但对于测试，对我们来说就足够了（有关更多信息，请查看 [snarkjs 文档](https://github.com/iden3/snarkjs)）。\r\n\r\n## snarkjs\r\n\r\n现在，一切都准备好生成证明了。我们将使用 snarkjs，因此使用以下命令安装它：\r\n\r\n```\r\nnpm i snarkjs\r\n```\r\n\r\n生成证明的过程如下：\r\n\r\n```js\r\nconst { proof, publicSignals } = await snarkjs.groth16.fullProve(\r\n  { in: 10 }, \r\n  \"build/poseidon_hasher_js/poseidon_hasher.wasm\", \r\n  \"circuit_0000.zkey\");\r\nconsole.log(publicSignals);\r\nconsole.log(proof);\r\n```\r\n\r\n输入信号作为 `fullProve` 函数的第一个参数传递。第二个参数是编译后的电路，最后一个参数是生成的 proving key。该函数返回电路的输出和证明。\r\n\r\n我们需要一个验证密钥，可以从 proving key 生成以验证证明。以下是如何获取它：\r\n\r\n```\r\nnpx snarkjs zkey export verificationkey circuit_0000.zkey verification_key.json\r\n```\r\n\r\n验证代码如下：\r\n\r\n```\r\nconst vKey = JSON.parse(fs.readFileSync(\"verification_key.json\"));\r\nconst res = await snarkjs.groth16.verify(vKey, publicSignals, proof);\r\n\r\nif (res === true) {\r\n  console.log(\"Verification OK\");\r\n} else {\r\n  console.log(\"Invalid proof\");\r\n}\r\n```\r\n\r\n验证密钥是 `verify` 函数的第一个参数，输出和证明是第二个和第三个参数。函数的结果是一个简单的布尔值。\r\n\r\n在此示例中，我们使用电路计算了 `hash`，但这并不总是可能的，因为 `hash` 可能是部分结果，或者我们的电路看起来像这样：\r\n\r\n```\r\npragma circom 2.0.0;\r\n\r\ninclude \"node_modules/circomlib/circuits/poseidon.circom\";\r\n\r\ntemplate PoseidonHasher() {\r\n    signal input in;\r\n    signal input hash;\r\n\r\n    component poseidon = Poseidon(1);\r\n    poseidon.inputs[0] <== in;\r\n    hash === poseidon.out;\r\n}\r\n\r\ncomponent main {public [hash]} = PoseidonHasher();\r\n```\r\n\r\n这个电路没有输出，只有两个输入。第一个输入是数据，第二个是它的 hash。在模板的最后一行，我们检查了 `hash`。只有在给定的 `hash` 是给定数据的 poseidon `hash` 时，电路才会成功运行。但是我们如何在 JS 中计算 poseidon `hash` 呢？\r\n\r\nCircomlib 有一个 [JS 实现](https://github.com/iden3/circomlibjs) ，可以用于此目的。让我们安装它：\r\n\r\n```\r\nnpm i circomlibjs\r\n```\r\n\r\n现在，我们可以使用以下代码计算 `hash`：\r\n\r\n```js\r\n const poseidon = await circomlibjs.buildPoseidon();\r\n const hash = poseidon.F.toString(poseidon([10]));\r\n console.log(hash);\r\n```\r\n\r\n`poseidon` 函数的结果是一个 `Buffer`，我们必须将其转换为数字。在 zk-SNARK 中，每个计算都是在[有限域](https://en.wikipedia.org/wiki/Finite_field)中进行的，因此我们必须使用 `poseidon.F.toString` 进行转换。\r\n\r\nCircomlibjs 和 snarkjs 在 Node.js 和浏览器中都能很好地工作，因此你可以在客户端生成或验证证明。还可以生成用于验证的智能合约，你可以将其用于 Solidity 代码中验证证明（有关更多信息，请查看 [snarkjs 文档](https://github.com/iden3/snarkjs)）。\r\n\r\nCircomlibjs 还具有智能合约生成器。例如，如果你想在链上生成 poseidon `hash`，可以通过生成的代码实现。\r\n\r\n这是我关于在 JavaScript 中使用 zk-SNARK 的非常简短的教程。这不是一个完整的课程，你可能有很多问题，但我希望我能帮助你开始你的旅程。[Circom](https://docs.circom.io/) 和 [snarkjs](https://github.com/iden3/snarkjs/blob/master/README.md) 都有很好的文档，你还可以从像 [Tornado Cash](https://github.com/tornadocash/tornado-core) 这样的现有项目中学到很多。 \r\n\r\n这个教程的源代码可以在[这个 GitHub 仓库](https://github.com/TheBojda/zksnark-tutorial)中找到。\r\n\r\n---\r\n\r\n> 本翻译由 [DeCert.me](https://decert.me/) 协助支持， 在 DeCert 构建可信履历，为自己码一个未来。"},"author":{"user":"https://learnblockchain.cn/people/74","address":"0xD305E3e4364DfB6eb1b07ec9aeA66709d836609D"},"history":null,"timestamp":1708055297,"version":1}