Zkzkevm:私有 EVM - 隐私性

本文提出了一种在以太坊上实现私有智能合约的方法,通过在 Reth 中添加两个新的操作码 pstore 和 pload,并结合零知识 EVM (zkEVM),开发者可以使用 Solidity 编写私有合约,实现私有用户状态但非私有全局状态。文章还讨论了该方案的权衡,例如调用链中私有和公共部分的结合,以及一些需要进一步考虑的问题,例如账户隐私和移动端可行性。

介绍

随着 zkEVM 的商品化,出现了提供私有智能合约基础设施同时保持 EVM 兼容性的有趣机会。开发者可以使用 Solidity 编译器的一个版本或一些后处理工具来编写 Solidity 代码并将其编译为私有智能合约。

在私有全局状态和隐私之间存在重要的权衡,这源于你需要知道你要证明什么才能证明它的观点。因此,你不能拥有一个带有你不知道的全局公共状态的私有智能合约。由此可见,你也不能拥有一个带有全局私有状态的私有智能合约。例如,Uniswap 是不可能的,因为证明者需要知道两个池的余额才能证明交换已正确完成。更多信息请参考 为什么你不能用 ZKP 构建一个私有 Uniswap

因此,在我们拥有 IO 之前,我们所知道和喜爱的一些东西无法以私有的方式实现,这就是为什么 IO 如此重要。它让我们能够创建一个完全私有的以太坊,并具有完全相同的信任假设。

无论如何,这篇文章是关于我们如何向 reth 添加两个操作码 pstore 和 pload,将其编译成 zkEVM,并拥有具有私有用户状态但没有私有全局状态的私有智能合约。

如何实现

你有一些合约调用是私有的。我们通过利用现有的 zkEVM 代码来证明这些调用已正确执行,但不泄露关于合约实际做了什么的任何信息来实现这一点。除了满足一些要求,例如,已批准从另一个合约中使用一定数量的代币,但不知道是谁的代币。我们实现两个新的操作码 pstore 和 pload,类似于 sload 和 sstore,但这些值是私有的。

工具箱

我们以 zkEVM 作为我们的工具链。我们不会对 zkEVM 本身进行任何更改。我们会将其视为黑盒。相反,我们会对 reth 进行更改。我们要求 reth 添加两个新的树。私有存储树 (PST) 和私有零知识树 (PNT)。

PST 和 PNT 中的每个叶子都会随着每次更新一起发布。因此,任何人都可以证明任何叶子的成员资格。但是,这些叶子包含的值仅对创建它们的用户已知。

pload

pload 是我们添加的一个 EVM 操作码。它类似于 sload。当 sload 在 zkEVM 中执行时,zkEVM 会做一个 Merkle 证明,证明某个值在树中的某个位置。

类似地,对于 pload,我们对树中的该叶子进行成员资格证明,但我们也证明该叶子尚未被零知识化。

假设我们要 pload 一个值 x。所以基本上我们要做两个 Merkle 证明

  1. 证明 PST 中存在 x
  2. 证明 PNT 中不存在 x.nullifier

只有知道该叶子秘密值的用户才能计算其零知识值,并且只有这样的用户才能证明它尚未被零知识化。

注意:sload 隐含地具有非包含证明,因为它使用有序的 Merkle 树。我们不能对 pload 和 pstore 使用有序的 Merkle 树,因此我们需要某种编码来确保给定的叶子尚未被创建。这种编码可以是 hash (contract, slot, value, nullifier)

注意:我认为如果你 sload 一个空的地址,你会得到 0x0,这也需要考虑。可能需要考虑一种在 ZkEVM 中处理这个问题的方法,使得存在相同的开发体验。但是很难证明一个存储槽没有被填充。

pstore

pstore 将执行与 sstore 相同的操作。但它的工作方式略有不同。

在 zkEVM 中,每次执行 sstore 时,它实际上会执行两个 Merkle 证明。第一个证明证明叶子的当前值为 x,第二个 Merkle 证明计算的 Merkle 根是将值 x 替换为 y 后的结果。因此,你可以将第一个证明视为获得对树中所有叶子的摘要证明,第二个证明视为仅将单个叶子 (x) 替换为 y。

所以 sstore

  1. 证明树中存在一个值 x
  2. 将其替换为 y

pstore 可以做同样的事情,但略有不同

  1. 它通过获取 x.nullifier 并将其添加到零知识树中来删除 x。
  2. 它通过将 y 添加到 PST 来替换 x。

Solidity

Solidity 编译为 EVM 操作码。

假设我们有以下智能合约


def transfer(sender, reciver, amount) private:
    bal[sender]= bal[sender] - amount
    bal[reciver] = bal[reciver] + amount
    # 这不是直接添加到用户的余额中。相反,这有点像输入输出,用户需要获得收到的资金才能添加到他们的总余额中。这种细微的差异现在被封装在接收者地址抽象中。但需要更多的工作来弄清楚 zkEVM 端需要什么。
    return(1)

Solidity 编译器(或一些后处理器)会看到这个,并将字节码中所有的 sload/sstore 替换为 pload/pstore。它只会对具有私有修饰符或标签的函数执行此操作。

带有部分私有和部分公共调用链

可以把它想象成一个更可编程版本的 Aztec Connect。假设我们有一个私有钱包,让这个钱包调用 Uniswap。这是可以做到的,但我们必须小心地清理 message.sender、tx.origin、nonce、gas_price、gas_limit 和其他元数据泄漏。我们可以通过以下几种方式做到这一点

  1. 为每个调用创建代理合约,然后在另一个交易或当前交易调用堆栈的末尾重新平衡。
  2. 使用全局代理合约

注意:tx.origin 可能需要在 reth 更改中进行清理。但它可能只是一个 bundler,所以我觉得不会太糟糕。

权衡

仅仅为了制作 Aztec Connect,这一切似乎有点复杂。但这里的力量在于能够重用我们目前的大部分基础设施,从而能够实现更强大的应用程序。

卡特尔合约

我们谈论了很多关于私有全局状态以及如何不可能实现 Uniswap 之类的东西。但假设我想为我和我的朋友们制作一个智能合约。我想公开地保持源代码的私有,但让我和我的朋友们执行它。这也是可能的,我们只需要放宽智能合约的数据可用性保证,使得智能合约代码不需要发布。

关于卡特尔内部的数据可用性保证有一些细微的差别。我想我们可以实施某种强制日志记录,其中所有数据更新都被加密和发布,以便只有卡特尔成员才能看到。

总结

似乎这个想法在以下两个方面可以立即发挥作用

  1. 制作一个私有rollup,其中一台巨型服务器进行所有的证明,用户将其数据提供给这台服务器,而不是其他人
  2. 一个私有rollup,用户进行一些证明,因此他们的存储访问对巨型服务器隐藏。

看起来它既有用又易于实现。不需要任何 zk 知识,将 zk 视为商品。

待办事项

我们需要更多地思考

  1. 我们是否应该默认将 EOA 设置为私有,这似乎可以通过一些零知识技巧来实现,例如让他们签署“nullifier”,然后该随机字符串成为他们的 nullifier,或者像 nullifier_0 = hash (sign(“nullfiier”) , 0 ) nullifier_1 = hash(sign(“nullifier”,1)) 等等。但要做到这一点,我们必须编译所有 ERC20 合约以对 ERC20 使用 pstore 和 pload。看起来这可能会破坏其他东西。但是手动 EOA 隐私似乎不包括状态更改,因为大多数人关心的是 ERC20 而不是 ETH。
  2. 在移动设备上对一组简化的事物(几个 pload)进行 zkEVM 证明是否可行?
  3. 如果你要 pstore 的值是动态生成的,那么最好有一个巨型服务器可以用来为你存储该叶子的零知识值,而不是你创建完整的叶子,因为存在竞争条件。
  4. 如何给出我的地址,以便我可以在不将所有收据连接在一起的情况下私下接收资金
  5. 如何使用日志记录或其他结构,以便我知道我是否收到了资金。可能就像返回一个包含某种加密“标志”的日志一样简单
  • 原文链接: ethresear.ch/t/zkzkevm-p...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
以太坊中文
以太坊中文
以太坊中文, 用中文传播以太坊的最新进展