本文介绍了 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 是公开的,并且被约束为 a 和 b 的乘积。因此,底层的 R1CS 是相同的。但是,第二个版本更 “方便”,因为我们不必显式地提供 c。在使用 component main {public [c]} 的第一个电路中,如果我们提供的 c 不遵守约束,则不会生成 witness。但是,在使用 c 作为输出的第二个电路中,witness 生成器会自动计算出 c 的正确值,从而消除了手动输入。
由于 c 完全由 a 和 b 决定,因此没有理由显式地为 c 提供一个值,因此应该首选 output 表示法。
请注意,输出是公开的。
在输入的情况下,如果我们想公开一些输入,那么这意味着我们有一个信号,其值不完全由其他信号值决定。在这种情况下,我们必须使用 public 修饰符方法。例如,如果我们声明 “我将 a、b 和 c 相乘得到 d,a 和 d 是公开的,但 b 和 c 是私有的”,我们将把该电路构造为:
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 生成器,它们被分别使用。
主组件中的输出信号可以被认为是对验证者公开的中间信号。
Circom 按以下方式排列 witness 向量:
[constant, public signals, private signals]
让我们以 “我将隐藏值 a、b 与公共值 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 是如何构建的:
Example.circomcircom Example.circom --sym --r1cs --wasm 编译它input.json:echo '{"a": "3", "b": "4", "c":"2", "d":"24"}' > input.jsoncd example_jsnode generate_witness.js example.wasm ../input.json witness.wtnssnarkjs wej witness.wtns && cat witness.json我们应该得到以下结果。请注意,这与我们为 input.json 提供的值相匹配:
[
"1", // 常数
"2", // c (公共信号)
"24", // d (公共信号)
"3", // a
"4", // b
"12" // s
]
因此,我们可以看到 witness 布局总是:
c, d)a, b)s)。component main {public [in1, in2]} = Main(); 语法将输入设置为公开
- 原文链接: rareskills.io/post/circo...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!