Circom 公开和私有输入

本文介绍了 Circom 中 public input 和 output 的概念,以及如何在 Circom 电路中定义和使用它们。

在 Circom 中,公共输入是 witness 中的一个信号,它将被透露给验证者。

例如,假设我们想要创建一个 ZK 证明,声明:“我们知道生成 0x492c…9254 的哈希的输入。”为了使这个声明有意义,值 0x492c…9254(目标哈希输出)必须是公开的。否则,我们在语义上声称 “我们哈希了一些东西”,这没有那么有用。

以下电路声明,“我将两个数字相乘得到第三个数字:”

template Main() {
  signal input a;
  signal input b;
  signal input c;

  a * b === c;
}

component main = Main();

下一个circut做了一个类似的声明,但是把结果改成了公开的:“我将两个数字相乘得到第三个数字,它的值是公开已知的:”

template Main() {
  signal input a;
  signal input b;
  signal input c;

  a * b === c;
}

component main {public [c]} = Main();
  • 所有输入信号默认都是私有的,除非使用 component main {public [c]} 语法明确地将它们公开。主组件是唯一可以定义哪些输入是公开的地方。
  • 列表 [c] 是要公开的信号的列表。如果我们也想公开 a,它可以包含更多的信号,例如 [a,c]
  • 只有输入信号可以被指定为公开的,中间信号不能。

上面的模板编译成一个秩-1 约束系统 (R1CS),与以下内容相同,我们在主组件中引入 output 关键字:

template Main() {
  signal input a;
  signal input b;
  signal output c;

  a * b ==> c;
}

component main = Main();

在上面的两个模板中,c 是公开的,并且被约束为 ab 的乘积。因此,底层的 R1CS 是相同的。但是,第二个版本更 “方便”,因为我们不必显式地提供 c。在使用 component main {public [c]} 的第一个电路中,如果我们提供的 c 不遵守约束,则不会生成 witness。但是,在使用 c 作为输出的第二个电路中,witness 生成器会自动计算出 c 的正确值,从而消除了手动输入。

由于 c 完全由 ab 决定,因此没有理由显式地为 c 提供一个值,因此应该首选 output 表示法。

请注意,输出是公开的。

在输入的情况下,如果我们想公开一些输入,那么这意味着我们有一个信号,其值完全由其他信号值决定。在这种情况下,我们必须使用 public 修饰符方法。例如,如果我们声明 “我将 abc 相乘得到 dad 是公开的,但 bc 是私有的”,我们将把该电路构造为:

template Main() {
  signal input a; // 显式公开
  signal input b;
  signal input c;
  signal output d; // 隐式公开

  signal s <== a * b; // 中间信号
  d <== c * s;
}

component main{public [a]} = Main();

以下是如何理解 output 信号:

  • 对于一个子组件,output 是一个信号,它将被赋予来自其他输入的值,并且可能会被实例化该子组件的组件稍后使用。
  • 对于主组件,output 是 witness 中的一个公共信号,其值应完全由其他输入信号决定。声明一个输出信号而不给它赋值可能会产生漏洞,因为证明者可以给它赋任何他们想要的值。我们将在接下来的章节中展示这种漏洞的机制。

尽管名称是 “输出”,但没有从主组件获取 “输出” 的机制 —— Circom 无法返回任何内容。没有办法让其他代码库读取 “输出” 的值。

它只生成一个 R1CS,帮助计算 R1CS 的 witness。然后,Snarkjs 使用 Circom 代码生成一个 ZK 证明,证明 witness 满足 R1CS。

Circom 没有被 “执行”,这就是为什么它不 “返回” 任何东西。你不是在 “运行” Circom,你只是在描述一个抽象电路,它被转换成两个部分:R1CS 和一个 witness 生成器,它们被分别使用。

主组件中的输出信号可以被认为是对验证者公开的中间信号。

带有公共信号的 Witness 布局

Circom 按以下方式排列 witness 向量:

[constant, public signals, private signals]

让我们以 “我将隐藏值 ab 与公共值 c 相乘,得到公共值 d” 为例:

// 断言 a*b === c*d
template Example() {
  signal input a;
  signal input b;
  signal input c;
  signal input d;

  signal s;

  s <== a * b;
  d === s * c;
}

component main {public [c, d]} = Example();

请注意,我们可以通过将 d 设置为输出来节省一些代码,但我们这里不这样做,以使即将到来的演示更清楚。

要查看 witness 是如何构建的:

  1. 将上面的文件保存为 Example.circom
  2. 使用 circom Example.circom --sym --r1cs --wasm 编译它
  3. 创建 input.jsonecho '{"a": "3", "b": "4", "c":"2", "d":"24"}' > input.json
  4. cd example_js
  5. 计算 witness:node generate_witness.js example.wasm ../input.json witness.wtns
  6. 将 witness 转换为 json 并 cat 它:snarkjs wej witness.wtns && cat witness.json

我们应该得到以下结果。请注意,这与我们为 input.json 提供的值相匹配:

[
 "1", // 常数
 "2", // c (公共信号)
 "24", // d (公共信号)
 "3", // a
 "4", // b
 "12" // s
]

因此,我们可以看到 witness 布局总是:

  • witness 中的常数项(总是 1)
  • 公共信号 (c, d)
  • 输入信号 (a, b)
  • 中间信号 (s)。

总结

  • 输入默认为私有
  • 我们可以使用 component main {public [in1, in2]} = Main(); 语法将输入设置为公开
  • 输出是公共信号
  • 输出是基于其他输入为用户计算的信号
  • 原文链接: rareskills.io/post/circo...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/