本文提出了一种在以太坊上实现私有智能合约的方法,通过在 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 是我们添加的一个 EVM 操作码。它类似于 sload。当 sload 在 zkEVM 中执行时,zkEVM 会做一个 Merkle 证明,证明某个值在树中的某个位置。
类似地,对于 pload,我们对树中的该叶子进行成员资格证明,但我们也证明该叶子尚未被零知识化。
假设我们要 pload 一个值 x。所以基本上我们要做两个 Merkle 证明
只有知道该叶子秘密值的用户才能计算其零知识值,并且只有这样的用户才能证明它尚未被零知识化。
注意:sload 隐含地具有非包含证明,因为它使用有序的 Merkle 树。我们不能对 pload 和 pstore 使用有序的 Merkle 树,因此我们需要某种编码来确保给定的叶子尚未被创建。这种编码可以是 hash (contract, slot, value, nullifier)
注意:我认为如果你 sload 一个空的地址,你会得到 0x0,这也需要考虑。可能需要考虑一种在 ZkEVM 中处理这个问题的方法,使得存在相同的开发体验。但是很难证明一个存储槽没有被填充。
pstore 将执行与 sstore 相同的操作。但它的工作方式略有不同。
在 zkEVM 中,每次执行 sstore 时,它实际上会执行两个 Merkle 证明。第一个证明证明叶子的当前值为 x,第二个 Merkle 证明计算的 Merkle 根是将值 x 替换为 y 后的结果。因此,你可以将第一个证明视为获得对树中所有叶子的摘要证明,第二个证明视为仅将单个叶子 (x) 替换为 y。
所以 sstore
pstore 可以做同样的事情,但略有不同
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 和其他元数据泄漏。我们可以通过以下几种方式做到这一点
注意:tx.origin 可能需要在 reth 更改中进行清理。但它可能只是一个 bundler,所以我觉得不会太糟糕。
仅仅为了制作 Aztec Connect,这一切似乎有点复杂。但这里的力量在于能够重用我们目前的大部分基础设施,从而能够实现更强大的应用程序。
我们谈论了很多关于私有全局状态以及如何不可能实现 Uniswap 之类的东西。但假设我想为我和我的朋友们制作一个智能合约。我想公开地保持源代码的私有,但让我和我的朋友们执行它。这也是可能的,我们只需要放宽智能合约的数据可用性保证,使得智能合约代码不需要发布。
关于卡特尔内部的数据可用性保证有一些细微的差别。我想我们可以实施某种强制日志记录,其中所有数据更新都被加密和发布,以便只有卡特尔成员才能看到。
似乎这个想法在以下两个方面可以立即发挥作用
看起来它既有用又易于实现。不需要任何 zk 知识,将 zk 视为商品。
我们需要更多地思考
- 原文链接: ethresear.ch/t/zkzkevm-p...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!