本文介绍了 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.circom
circom Example.circom --sym --r1cs --wasm
编译它input.json
:echo '{"a": "3", "b": "4", "c":"2", "d":"24"}' > input.json
cd example_js
node generate_witness.js example.wasm ../input.json witness.wtns
snarkjs 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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!