noname 邂逅以太坊:与 SnarkJS 集成 - ZKSECURITY

zkSecurity 推出了一种名为 noname 的编程语言,用于编写 ZK 电路,该语言现在支持 R1CS。

我们很高兴地宣布,nonamezkSecurity 用于编写 ZK 电路的编程语言,现在支持 R1CS 了! 此次更新允许开发者使用受 Rust 和 Golang 启发的语言编写 ZK 电路,并使用 SnarkJS 将其部署到以太坊。 这为以太坊上 zk-SNARK 证明提供了一种替代广泛使用的 Circom 语言的方案。 在本文中,你将看到第一个部署在以太坊上的 noname 电路,并且我们将向你展示如何自己部署一个。

noname & snarkjs

零知识领域正在快速发展,新的证明系统和框架层出不穷。 开发者面临着一个主要的挑战:碎片化。 每个证明系统都有其自己的语言和怪癖,通常针对特定的项目,并且在其他平台上不可用。 例如,Noir 用于 Aztec Network,Leo 用于 Aleo,Cairo 用于 Starknet,而 O1js 用于 Mina。

如果有一种方法可以弥合这一差距呢? 一种可以统一 zk 生态系统的语言,从而允许开发者编写可在不同后端上运行的电路。

这就是我们正在做的事情! 虽然 noname 最初是为了编译程序以与 Mina 的 kimchi 证明系统 一起使用而创建的,但我们现在正在更新它以与以太坊的 SnarkJS 一起使用。

noname 与 R1CS 和 SnarkJS 的集成是该语言发展历程中的一个关键步骤,这得益于 以太坊基金会的隐私与扩展探索团队 的资助。

将数独作为 noname 程序

以数独为例,可以展示零知识证明的两个优点:

  1. 在不泄露解决方案本身的情况下,证明数独解决方案的正确性。
  2. 降低链上计算成本。 链上验证成本是恒定的,与链下计算的复杂性无关。

数独电路的完整代码可在此处获取:这里。 让我们看看我们的main函数:

fn main(pub grid: Sudoku, solution: Sudoku) {
    solution.matches(grid);
    solution.verify();
}

它接收两个参数:数独问题作为公共输入,解决方案作为秘密。

从较高的层次来看,此数独电路检查:

  1. 解决方案网格与数独问题(编码在公共输入中)匹配
  2. 验证解决方案确实遵循数独规则

在 Noname 中,grid: Sudoku 自动将输入解释为 Sudoku 类型:

struct Sudoku {
    inner: [Field; 81],
}

这是它如何检查解决方案是否与解决方案问题的网格匹配。

// return the value in a given cell
// 返回给定单元格中的值
fn Sudoku.cell(self, const row: Field, const col: Field) -> Field {
    return self.inner[(row * 9) + col];
}

// verifies that self matches the grid in places where the grid has numbers
// 验证 self 在网格具有数字的地方与网格匹配
fn Sudoku.matches(self, grid: Sudoku) {
    // for each cell
    // 对于每个单元格
    for row in 0..9 {
        for col in 0..9 {
            // either the solution matches the grid
            // 或者解决方案与网格匹配
            // or the grid is zero
            // 或者网格为零
            let matches = self.cell(row, col) == grid.cell(row, col);
            let is_empty = grid.cell(row, col) == empty;
            assert(matches || is_empty);
        }
    }
}

接下来,程序根据数独规则验证解决方案:

fn Sudoku.verify(self) {
    self.verify_rows();
    self.verify_cols();
    self.verify_diagonals();
}

fn Sudoku.verify_rows(self) {
    for row in 0..9 {
        for num in 1..10 {
            let mut found = false;
            for col in 0..9 {
                let found_one = self.cell(row, col) == num;
                found = found || found_one;
            }
            assert(found);
        }
    }
}

fn Sudoku.verify_cols(self) {
    for col in 0..9 {
        for num in 1..10 {
            let mut found = false;
            for row in 0..9 {
                let found_one = self.cell(row, col) == num;
                found = found || found_one;
            }
            assert(found);
        }
    }
}

fn Sudoku.verify_diagonals(self) {
    for num in 1..10 {

        // first diagonal
        // 第一条对角线
        let mut found1 = false;
        for row1 in 0..9 {
            let temp1 = self.cell(row1, row1) == num;
            found1 = found1 || temp1;
        }
        assert(found1);

        // second diagonal
        // 第二条对角线
        let mut found2 = false;
        for row2 in 0..9 {
            let temp2 = self.cell(8 - row2, row2) == num;
            found2 = found2 || temp2;
        }
        assert(found2);
    }
}

如上所示,该语言背后的类型支持使电路代码简单明了,类似于用传统语言编写。

接入 Snarkjs

现在我们有了数独代码,我们可以使用 noname CLI 来生成和运行与 Snarkjs 兼容的电路。

  1. 安装 noname
cargo install --git https://www.github.com/zksecurity/noname
  1. 命令指南

noname-help

  1. 生成与 snarkjs 兼容的 r1cs 电路和 witness 文件
noname run --backend r1cs-bn254 --path ../test/noname-sudoku \
--private-inputs '{"solution": { "inner": ["9", "5", "3", "6", "2", "1", "7", "8", "4", "1", "4", "8", "7", "5", "9", "2", "6", "3", "2", "7", "6", "8", "3", "4", "9", "5", "1", "3", "6", "9", "2", "7", "5", "4", "1", "8", "4", "8", "5", "9", "1", "6", "3", "7", "2", "7", "1", "2", "3", "4", "8", "6", "9", "5", "6", "3", "7", "1", "8", "2", "5", "4", "9", "5", "2", "1", "4", "9", "7", "8", "3", "6", "8", "9", "4", "5", "6", "3", "1", "2", "7"] }}' \
--public-inputs '{"grid": { "inner": ["0", "5", "3", "6", "2", "1", "7", "8", "4", "0", "4", "8", "7", "5", "9", "2", "6", "3", "2", "7", "6", "8", "3", "4", "9", "5", "1", "3", "6", "9", "2", "7", "0", "4", "1", "8", "4", "8", "5", "9", "1", "6", "3", "7", "2", "0", "1", "2", "3", "4", "8", "6", "9", "5", "6", "3", "0", "1", "8", "2", "5", "4", "9", "5", "2", "1", "4", "9", "0", "8", "3", "6", "8", "9", "4", "5", "6", "3", "1", "2", "7"] }}'

然后,它应该生成 .r1cs.wtns 文件,并告诉你这些文件的位置。

R1CS file generated at: ../test/noname-sudoku/output.r1cs
Witness file generated at: ../test/noname-sudoku/output.wtns
  1. 测试生成的 r1cs 电路和 witness 文件

有一个 脚本 可以快速测试这些输出与 SnarkJS 的兼容性。 该脚本自动生成证明,并通过 SnarkJS 在链下对其进行验证。

在以太坊上验证证明

snarkjs 提供了一个命令来导出 solidity 验证器。 我们在以太坊上部署了一个实例,位于 0x7c686a33ac3f2c911b03b9aa80e5175d3ab4152a

要在链上测试解决方案,你可以使用此 SnarkJS 命令来生成calldata。

直观地说,calldata 包含合约调用参数以表示数独问题(电路中的公共输入)和相应解决方案的证明(不泄露它)。

Screenshot 2024-05-30 at 16.23.34

如屏幕截图所示(来自 remix),如果 proof(_pA, _pB, _pC) 和数独问题匹配 (_pubSignals),则对 verifyProof 的合约调用应返回 true

接下来是什么?

noname 是一种正在开发的语言,并且缺少许多功能! 我们正在寻求帮助和贡献。 如果你有兴趣,请查看 the issues

要了解有关 Noname 的更多信息,请查看 repo,或以下博客文章和视频:

  • 原文链接: blog.zksecurity.xyz/post...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
zksecurity
zksecurity
Security audits, development, and research for ZKP, FHE, and MPC applications, and more generally advanced cryptography.