特别感谢 Yoav Weiss、Dan Finlay、Martin Koppelmann 以及 Arbitrum、Optimism、Polygon、Scroll 和 SoulWallet 团队的反馈和审查。
在这篇关于三个转变的文章,我概述了一些关键原因,为什么开始明确考虑 L1 + 跨 L2 支持、钱包安全和隐私作为生态系统堆栈的必要基本特征,而不是将这些东西构建为可以单独设计的插件个人钱包。
这篇文章将更直接地关注一个特定子问题的技术方面:如何更容易地从 L2 读取 L1、从 L1 读取 L2 或从另一个 L2 读取一个 L2。解决这个问题对于实现资产/密钥库分离架构至关重要,但它在其他领域也有有价值的用例,最显着的是优化可靠的跨 L2 调用,包括在 L1 和 L2 之间移动资产等用例。
一旦 L2 变得更加主流,用户将拥有跨多个 L2 的资产,也可能拥有跨 L1 的资产。一旦智能合约钱包(多重签名、社交恢复或其他)成为主流,访问某些帐户所需的密钥会随着时间的推移而改变,旧的密钥将不再有效。一次两个都 当发生这些事情时,用户将需要有一种方法来更改有权访问位于许多不同地方的许多帐户的密钥,而无需进行大量交易。
特别是,我们需要一种方法来处理反事实地址:尚未以任何方式在链上“注册”的地址,但仍然需要接收和安全持有资金。我们都依赖于反事实地址:当你第一次使用以太坊时,你能够生成一个 ETH 地址,有人可以用它来向你付款,而无需在链上“注册”该地址(这需要支付交易费用,因此已经持有一些 ETH)。
和环氧乙烷,所有地址都以反事实地址开始。使用智能合约钱包,反事实地址仍然是可能的,这很大程度上要归功于创建2,它允许您拥有一个 ETH 地址,该地址只能由具有与特定哈希匹配的代码的智能合约填充。
EIP-1014(创建2) 地址计算算法。
然而,智能合约钱包带来了一个新的挑战:访问密钥的可能性改变。地址,它是哈希值初始化代码,只能包含钱包的最初的 验证密钥。这当前的 验证密钥将存储在钱包的存储中,但该存储记录不会神奇地传播到其他 L2。
如果用户在许多 L2 上拥有许多地址,包括他们所在的 L2 不知道的地址(因为它们是反事实的),那么似乎只有一种方法允许用户更改其密钥:资产/密钥库分离架构。每个用户都有(i) “密钥库合同” (在 L1 或一个特定的 L2 上),它存储验证密钥全部 钱包以及更改密钥的规则,以及(二) “钱包合约” 在 L1 和许多 L2 上,读取跨链以获取验证密钥。
有两种方法可以实现此目的:
为了展示完整的复杂性,我们将探讨最困难的情况:密钥库位于一个 L2 上,而钱包位于另一个 L2 上。如果密钥库或钱包位于 L1,则只需要此设计的一半。
假设密钥库已打开线,并且钱包已打开划伤的。钱包密钥的完整证明包括:
这里有两个主要棘手的实施问题:
主要有五个选项:
就所需的基础设施工作和用户成本而言,我大致排名如下:
”聚合“是指将每个区块中用户提供的所有证明聚合成一个将所有这些证明组合在一起的大元证明的想法。这对于 SNARK 和 KZG 是可能的,但对于 Merkle 分支则不行(你能 稍微合并 Merkle 分支,但它只会拯救你log(每个块的交易数)/ log(密钥库总数),实际上可能是 15-30%,所以可能不值得这个成本)。
只有当该方案拥有大量用户时,聚合才变得值得,因此实际上,版本 1 实现可以忽略聚合,并在版本 2 中实现聚合。
这个很简单:遵循上一节中的图表 直接地。更准确地说,每个“证明”(假设将一个 L2 证明为另一个 L2 的最大难度情况)将包含:
不幸的是,以太坊状态证明很复杂,但存在用于验证它们的库,并且如果你使用这些库,这个机制实现起来并不太复杂。
更大的问题是成本。 Merkle 证明很长,不幸的是,Patricia 树比所需的长度长约 3.9 倍(准确地说:理想的 Merkle 证明包含在树中)氮 对象是32 log2(N) 字节长,并且因为以太坊的 Patricia 树每个子节点有 16 个叶子,所以这些树的证明是32 15 log16(N) ~= 125 log2(N) 字节长)。大致处于一个状态2.5 亿 (~2²⁸) 个账户,这使得每个证明125 * 28 = 3500 字节,或大约 56,000 个 Gas,加上解码和验证哈希值的额外成本。
两个证明加在一起最终将花费大约 100,000 到 150,000 Gas(如果每笔交易使用签名验证,则不包括签名验证)——明显高于当前每笔交易 21,000 Gas 的基础。但如果在 L2 上验证证明,差距会变得更糟。 L2 内部的计算成本低廉,因为计算是在链下完成的,并且是在节点数比 L1 少得多的生态系统中完成的。另一方面,数据必须发布到 L1。因此,比较不是 21000 Gas 与 150,000 Gas;而是 21000 Gas 与 150,000 Gas 的比较。这是 21,000 L2 气体 vs 100,000 L1 气体。
我们可以通过比较 L1 Gas 成本和 L2 Gas 成本来计算这意味着什么:
目前,对于简单发送,L1 比 L2 贵约 15-25 倍,对于代币交换,L1 比 L2 贵 20-50 倍。简单发送的数据量相对较大,但交换的计算量要大得多。因此,交换是近似 L1 计算与 L2 计算成本的更好基准。考虑到所有这些,如果我们假设 L1 计算成本和 L2 计算成本之间的成本比为 30 倍,这似乎意味着在 L2 上放置 Merkle 证明的成本可能相当于 50 个常规交易。
当然,使用二叉 Merkle 树可以将成本降低约 4 倍,但即便如此,在大多数情况下成本还是太高了——如果我们愿意做出牺牲,不再与以太坊当前的十六进制兼容状态树,我们不妨寻求更好的选择。
从概念上讲,ZK-SNARK 的使用也很容易理解:您只需将 Merkle 证明替换为上图 用 ZK-SNARK 证明这些 Merkle 证明存在。一个 ZK-SNARK 的计算成本约为 400,000 Gas,大约 400 字节(比较:未来一笔基本交易需要 21,000 Gas 和 100 字节)通过压缩可减少至约 25 字节)。因此,从一个计算性的 从角度来看,ZK-SNARK 的成本是当今基本交易成本的 19 倍,并且从数据 从角度来看,ZK-SNARK 的成本是目前基本交易的 4 倍,未来可能是基本交易的 16 倍。
这些数字比 Merkle 证明有了巨大的进步,但它们仍然相当昂贵。有两种方法可以对此进行改进:(i)特殊用途的 KZG 证明,或(ii)聚合,类似于ERC-4337聚合 但使用更奇特的数学。我们可以对两者进行研究。
警告,这一部分比其他部分更加数学化。这是因为我们超越了通用工具,并构建了一些更便宜的专用工具,因此我们必须更多地深入“幕后”。如果您不喜欢深入的数学,请直接跳至下一节。
首先,回顾一下 KZG 承诺的运作方式:
需要理解的一些重要属性是:
因此,我们有一个结构,我们可以不断地将值添加到不断增长的列表的末尾,尽管有一定的大小限制(实际上,数亿个值是可行的)。然后我们使用那 作为我们的数据结构,用于管理(i)对每个 L2 上的密钥列表的承诺,存储在该 L2 上并镜像到 L1,以及(ii)对 L2 密钥承诺列表的承诺,存储在以太坊 L1 上,镜像到每个 L2。
保持承诺更新可以成为核心 L2 逻辑的一部分,也可以通过存款和取款桥在不更改 L2 核心协议的情况下实现。
因此,完整的证明需要:
实际上可以将两个 KZG 证明合并为一个,因此我们得到的总大小只有 100 字节。
请注意一个微妙之处:因为键列表是一个列表,而不是像状态那样的键/值映射,所以键列表必须按顺序分配位置。密钥承诺合约将包含其自己的内部注册表,将每个密钥库映射到一个 ID,并且对于它将存储的每个密钥hash(密钥,密钥库的地址) 而不是仅仅钥匙,明确地与其他 L2 进行通信哪个 特定条目正在谈论的密钥库。
该技术的优点是它执行L2 非常好。数据为 100 字节,比 ZK-SNARK 短约 4 倍,比 Merkle 证明短得多。计算成本主要是一次 size-2 配对检查,或者约 119,000 天然气。在 L1 上,数据不如计算重要,因此不幸的是 KZG 比 Merkle 证明更昂贵。
Verkle 树本质上涉及堆叠 KZG 承诺(或《近期行动计划》承诺,这可以更有效并使用更简单的加密技术)相互叠加:要存储 2⁴⁸ 值,您可以对 2²⁴ 值列表做出 KZG 承诺,其中每个值本身都是对 2²⁴ 值的 KZG 承诺。 Verkle 树正在@vbuterin/verkle_tree_eip">强烈考虑以太坊状态树,因为 Verkle 树可用于保存键值映射,而不仅仅是列表(基本上,您可以创建一个大小为 2²⁵⁶ 的树,但一开始它是空的,只有在实际需要填充时才填充树的特定部分)。
Verkle 树是什么样子的?实际上,您可以为基于 IPA 的树指定每个节点的宽度 256 == 2⁸,或者为基于 KZG 的树指定 2²⁴。
Verkle 树中的证明比 KZG 稍长;它们可能有几百字节长。它们也很难验证,尤其是当您尝试将许多证据聚合为一个时。
实际上,Verkle 树应该被认为与 Merkle 树类似,但在没有 SNARKing 的情况下更可行(因为数据成本更低),并且在使用 SNARKing 的情况下更便宜(因为证明者成本更低)。
Verkle 树的最大优点是协调数据结构的可能性:Verkle 证明可以直接在 L1 或 L2 状态上使用,无需覆盖结构,并且对 L1 和 L2 使用完全相同的机制。一旦量子计算机成为一个问题,或者一旦证明 Merkle 分支变得足够高效,Verkle 树就可以替换为具有合适的 SNARK 友好哈希函数的二叉哈希树。
如果 N 个用户进行 N 笔交易(或者更实际地说,NERC-4337 UserOperations)需要证明N个跨链声明,我们可以通过以下方式节省大量gas聚合 这些证明:将这些交易组合成一个块或进入一个块的捆绑包的构建器可以创建一个单身的 同时证明所有这些主张的证据。
这可能意味着:
在这三种情况下,每个证明只需要花费几十万天然气。建筑商需要制作其中之一每个 L2 上 对于该 L2 中的用户;因此,为了使其有用,该方案作为一个整体需要有足够的用途,以至于同一块中经常至少有一些交易在多个主要 L2 上。
如果使用 ZK-SNARK,主要的边际成本只是在合约之间传递数字的“业务逻辑”,因此每个用户可能需要几千个 L2 Gas。如果使用 KZG 多重证明,证明者将需要为该块内使用的每个持有密钥库的 L2 添加 48 个 Gas,因此每个用户的方案的边际成本将为每个 L2 添加约 800 个 L1 Gas(不是每个 L2)。用户)在顶部。但这些成本远低于不聚合的成本,后者不可避免地涉及超过 10,000 个 L1 Gas 和数十万个 L2 Gas每个用户。对于 Verkle 树,您可以直接使用 Verkle 多重证明,为每个用户添加大约 100-200 字节,或者您可以制作 Verkle 多重证明的 ZK-SNARK,其成本与 Merkle 分支的 ZK-SNARK 类似,但证明起来要便宜得多。
从实施的角度来看,最好让捆绑器通过以下方式聚合跨链证明:ERC-4337 帐户抽象标准。 ERC-4337 已经为构建者提供了一种以自定义方式聚合 UserOperations 部分的机制。甚至还有一个@voltrevo/BJ0QBy3zi">BLS 签名聚合的实现,这可以将 L2 的 Gas 成本降低 1.5 倍到 3 倍,具体取决于还包括哪些其他形式的压缩。
图表来自@voltrevo/BJ0QBy3zi">BLS 钱包实施帖子 显示 ERC-4337 早期版本中 BLS 聚合签名的工作流程。聚合跨链证明的工作流程可能看起来非常相似。
最后一种可能性,仅适用于 L2 读取 L1(而不是 L1 读取 L2),是修改 L2,让它们直接对 L1 上的合约进行静态调用。
这可以通过操作码或预编译来完成,它允许调用 L1,您可以在其中提供目标地址、gas 和 calldata,并且它返回输出,但因为这些调用是静态调用,所以它们实际上不能改变 任何 L1 状态。 L2 必须了解 L1 才能处理存款,因此没有什么根本性的因素可以阻止此类事情的实施;这主要是技术实施方面的挑战(参见:Optimism 的此 RFP 支持静态呼叫 L1)。
请注意,如果密钥库位于 L1,和 L2 集成了 L1 静态调用功能,因此根本不需要任何证明!然而,如果 L2 不集成 L1 静态调用,或者密钥库位于 L2 上(一旦 L1 变得太昂贵而用户无法使用,哪怕是一点点,它最终可能必须如此),那么将需要证明。
上述所有方案都要求 L2 访问最近的 L1 状态根或整个最近的 L1 状态。幸运的是,所有 L2 都已经具有一些访问最近的 L1 状态的功能。这是因为他们需要这样的功能来处理从 L1 到 L2 的消息,尤其是存款。
事实上,如果 L2 具有存款功能,那么您可以按原样使用该 L2 将 L1 状态根移动到 L2 上的合约中:只需在 L1 上有一个合约,调用区块哈希 操作码,并将其作为存款消息传递给 L2。可以在 L2 侧接收完整的块头,并提取其状态根。然而,如果每个 L2 都有一种明确的方式来直接访问完整的最近 L1 状态或最近的 L1 状态根,那就更好了。
优化 L2 如何接收最近的 L1 状态根的主要挑战是同时实现安全性和低延迟:
此外,在相反方向(L1s 读取 L2):
对于许多 defi 用例来说,去信任的跨链操作的某些速度慢得令人无法接受;对于这些情况,您确实需要更快的网桥和更不完善的安全模型。然而,对于更新钱包密钥的用例,较长的延迟是更可以接受的:你不是延迟交易 几个小时,你就耽误了关键变化。您只需将旧钥匙保留更长时间即可。如果您因为密钥被盗而更改密钥,那么您确实会在很长一段时间内容易受到攻击,但这可以减轻,例如。通过钱包有冻结 功能。
最终,最好的延迟最小化解决方案是 L2 以最佳方式实现直接读取 L1 状态根,其中每个 L2 块(或状态根计算日志)包含一个指向最近的 L1 块的指针,因此如果 L1 恢复,L2也可以恢复。密钥库合约应该放置在主网上,或者放置在 ZK-rollups 的 L2 上,这样可以快速提交到 L1。
L2 链的区块不仅可以依赖于之前的 L2 区块,还可以依赖于 L1 区块。如果 L1 恢复经过这样的链接,L2 也会恢复。值得注意的是,这也是早期(Dank 之前)版本分片的设想工作方式;看这里 对于代码。
令人惊讶的是,没有那么多。它实际上甚至不需要是 Rollup:如果它是 L3 或 validium,那么只要您在 L1 或 ZK Rollup 上保存密钥库,就可以在那里保存钱包。你那件事做 链需要直接访问以太坊状态根,并且如果以太坊重组愿意重组,如果以太坊硬分叉则愿意硬分叉的技术和社会承诺。
一个有趣的研究问题是确定一条链在多大程度上可以与多种的 其他链(例如以太坊和 Zcash)。天真地这样做是可能的:如果以太坊,你的链可能会同意重组或者 Zcash 重组(如果以太坊则硬分叉或者 Zcash 硬分叉),但是您的节点运营商和您的社区通常具有双倍的技术和政治依赖性。因此,这种技术可用于连接到其他一些链,但成本会增加。方案基于ZK桥 具有有吸引力的技术特性,但它们有一个关键弱点,即对 51% 攻击或硬分叉的鲁棒性不强。或许还有更巧妙的解决方案。
理想情况下,我们还希望保护隐私。如果您有多个由同一密钥库管理的钱包,那么我们要确保:
这会产生一些问题:
使用 SNARK,解决方案在概念上很简单:默认情况下,证明是信息隐藏的,并且聚合器需要生成递归 SNARK 来证明 SNARK。
如今这种方法的主要挑战是聚合需要聚合器创建递归 SNARK,目前速度相当慢。
有了KZG,我们可以使用@vbuterin/non_index_revealing_proof">这项工作涉及非索引揭示 KZG 证明 (另请参阅:该工作的更正式版本填缝纸)作为起点。然而,盲证明的聚合是一个需要更多关注的开放问题。
不幸的是,从 L2 内部直接读取 L1 并不能保护隐私,尽管实现直读功能仍然非常有用,既可以最大限度地减少延迟,也可以用于其他应用程序。
特别感谢 Yoav Weiss、Dan Finlay、Martin Koppelmann 以及 Arbitrum、Optimism、Polygon、Scroll 和 SoulWallet 团队的反馈和审查。
在这篇关于三个转变的文章,我概述了一些关键原因,为什么开始明确考虑 L1 + 跨 L2 支持、钱包安全和隐私作为生态系统堆栈的必要基本特征,而不是将这些东西构建为可以单独设计的插件个人钱包。
这篇文章将更直接地关注一个特定子问题的技术方面:如何更容易地从 L2 读取 L1、从 L1 读取 L2 或从另一个 L2 读取一个 L2。解决这个问题对于实现资产/密钥库分离架构至关重要,但它在其他领域也有有价值的用例,最显着的是优化可靠的跨 L2 调用,包括在 L1 和 L2 之间移动资产等用例。
一旦 L2 变得更加主流,用户将拥有跨多个 L2 的资产,也可能拥有跨 L1 的资产。一旦智能合约钱包(多重签名、社交恢复或其他)成为主流,访问某些帐户所需的密钥会随着时间的推移而改变,旧的密钥将不再有效。一次两个都 当发生这些事情时,用户将需要有一种方法来更改有权访问位于许多不同地方的许多帐户的密钥,而无需进行大量交易。
特别是,我们需要一种方法来处理反事实地址:尚未以任何方式在链上“注册”的地址,但仍然需要接收和安全持有资金。我们都依赖于反事实地址:当你第一次使用以太坊时,你能够生成一个 ETH 地址,有人可以用它来向你付款,而无需在链上“注册”该地址(这需要支付交易费用,因此已经持有一些 ETH)。
和环氧乙烷,所有地址都以反事实地址开始。使用智能合约钱包,反事实地址仍然是可能的,这很大程度上要归功于创建2,它允许您拥有一个 ETH 地址,该地址只能由具有与特定哈希匹配的代码的智能合约填充。
EIP-1014(创建2) 地址计算算法。
然而,智能合约钱包带来了一个新的挑战:访问密钥的可能性改变。地址,它是哈希值初始化代码,只能包含钱包的最初的 验证密钥。这当前的 验证密钥将存储在钱包的存储中,但该存储记录不会神奇地传播到其他 L2。
如果用户在许多 L2 上拥有许多地址,包括他们所在的 L2 不知道的地址(因为它们是反事实的),那么似乎只有一种方法允许用户更改其密钥:资产/密钥库分离架构。每个用户都有(i) “密钥库合同” (在 L1 或一个特定的 L2 上),它存储验证密钥全部 钱包以及更改密钥的规则,以及(二) “钱包合约” 在 L1 和许多 L2 上,读取跨链以获取验证密钥。
有两种方法可以实现此目的:
为了展示完整的复杂性,我们将探讨最困难的情况:密钥库位于一个 L2 上,而钱包位于另一个 L2 上。如果密钥库或钱包位于 L1,则只需要此设计的一半。
假设密钥库已打开线,并且钱包已打开划伤的。钱包密钥的完整证明包括:
这里有两个主要棘手的实施问题:
主要有五个选项:
就所需的基础设施工作和用户成本而言,我大致排名如下:
”聚合“是指将每个区块中用户提供的所有证明聚合成一个将所有这些证明组合在一起的大元证明的想法。这对于 SNARK 和 KZG 是可能的,但对于 Merkle 分支则不行(你能 稍微合并 Merkle 分支,但它只会拯救你log(每个块的交易数)/ log(密钥库总数),实际上可能是 15-30%,所以可能不值得这个成本)。
只有当该方案拥有大量用户时,聚合才变得值得,因此实际上,版本 1 实现可以忽略聚合,并在版本 2 中实现聚合。
这个很简单:遵循上一节中的图表 直接地。更准确地说,每个“证明”(假设将一个 L2 证明为另一个 L2 的最大难度情况)将包含:
不幸的是,以太坊状态证明很复杂,但存在用于验证它们的库,并且如果你使用这些库,这个机制实现起来并不太复杂。
更大的问题是成本。 Merkle 证明很长,不幸的是,Patricia 树比所需的长度长约 3.9 倍(准确地说:理想的 Merkle 证明包含在树中)氮 对象是32 log2(N) 字节长,并且因为以太坊的 Patricia 树每个子节点有 16 个叶子,所以这些树的证明是32 15 log16(N) ~= 125 log2(N) 字节长)。大致处于一个状态2.5 亿 (~2²⁸) 个账户,这使得每个证明125 * 28 = 3500 字节,或大约 56,000 个 Gas,加上解码和验证哈希值的额外成本。
两个证明加在一起最终将花费大约 100,000 到 150,000 Gas(如果每笔交易使用签名验证,则不包括签名验证)——明显高于当前每笔交易 21,000 Gas 的基础。但如果在 L2 上验证证明,差距会变得更糟。 L2 内部的计算成本低廉,因为计算是在链下完成的,并且是在节点数比 L1 少得多的生态系统中完成的。另一方面,数据必须发布到 L1。因此,比较不是 21000 Gas 与 150,000 Gas;而是 21000 Gas 与 150,000 Gas 的比较。这是 21,000 L2 气体 vs 100,000 L1 气体。
我们可以通过比较 L1 Gas 成本和 L2 Gas 成本来计算这意味着什么:
目前,对于简单发送,L1 比 L2 贵约 15-25 倍,对于代币交换,L1 比 L2 贵 20-50 倍。简单发送的数据量相对较大,但交换的计算量要大得多。因此,交换是近似 L1 计算与 L2 计算成本的更好基准。考虑到所有这些,如果我们假设 L1 计算成本和 L2 计算成本之间的成本比为 30 倍,这似乎意味着在 L2 上放置 Merkle 证明的成本可能相当于 50 个常规交易。
当然,使用二叉 Merkle 树可以将成本降低约 4 倍,但即便如此,在大多数情况下成本还是太高了——如果我们愿意做出牺牲,不再与以太坊当前的十六进制兼容状态树,我们不妨寻求更好的选择。
从概念上讲,ZK-SNARK 的使用也很容易理解:您只需将 Merkle 证明替换为上图 用 ZK-SNARK 证明这些 Merkle 证明存在。一个 ZK-SNARK 的计算成本约为 400,000 Gas,大约 400 字节(比较:未来一笔基本交易需要 21,000 Gas 和 100 字节)通过压缩可减少至约 25 字节)。因此,从一个计算性的 从角度来看,ZK-SNARK 的成本是当今基本交易成本的 19 倍,并且从数据 从角度来看,ZK-SNARK 的成本是目前基本交易的 4 倍,未来可能是基本交易的 16 倍。
这些数字比 Merkle 证明有了巨大的进步,但它们仍然相当昂贵。有两种方法可以对此进行改进:(i)特殊用途的 KZG 证明,或(ii)聚合,类似于ERC-4337聚合 但使用更奇特的数学。我们可以对两者进行研究。
警告,这一部分比其他部分更加数学化。这是因为我们超越了通用工具,并构建了一些更便宜的专用工具,因此我们必须更多地深入“幕后”。如果您不喜欢深入的数学,请直接跳至下一节。
首先,回顾一下 KZG 承诺的运作方式:
需要理解的一些重要属性是:
因此,我们有一个结构,我们可以不断地将值添加到不断增长的列表的末尾,尽管有一定的大小限制(实际上,数亿个值是可行的)。然后我们使用那 作为我们的数据结构,用于管理(i)对每个 L2 上的密钥列表的承诺,存储在该 L2 上并镜像到 L1,以及(ii)对 L2 密钥承诺列表的承诺,存储在以太坊 L1 上,镜像到每个 L2。
保持承诺更新可以成为核心 L2 逻辑的一部分,也可以通过存款和取款桥在不更改 L2 核心协议的情况下实现。
因此,完整的证明需要:
实际上可以将两个 KZG 证明合并为一个,因此我们得到的总大小只有 100 字节。
请注意一个微妙之处:因为键列表是一个列表,而不是像状态那样的键/值映射,所以键列表必须按顺序分配位置。密钥承诺合约将包含其自己的内部注册表,将每个密钥库映射到一个 ID,并且对于它将存储的每个密钥hash(密钥,密钥库的地址) 而不是仅仅钥匙,明确地与其他 L2 进行通信哪个 特定条目正在谈论的密钥库。
该技术的优点是它执行L2 非常好。数据为 100 字节,比 ZK-SNARK 短约 4 倍,比 Merkle 证明短得多。计算成本主要是一次 size-2 配对检查,或者约 119,000 天然气。在 L1 上,数据不如计算重要,因此不幸的是 KZG 比 Merkle 证明更昂贵。
Verkle 树本质上涉及堆叠 KZG 承诺(或《近期行动计划》承诺,这可以更有效并使用更简单的加密技术)相互叠加:要存储 2⁴⁸ 值,您可以对 2²⁴ 值列表做出 KZG 承诺,其中每个值本身都是对 2²⁴ 值的 KZG 承诺。 Verkle 树正在@vbuterin/verkle_tree_eip">强烈考虑以太坊状态树,因为 Verkle 树可用于保存键值映射,而不仅仅是列表(基本上,您可以创建一个大小为 2²⁵⁶ 的树,但一开始它是空的,只有在实际需要填充时才填充树的特定部分)。
Verkle 树是什么样子的?实际上,您可以为基于 IPA 的树指定每个节点的宽度 256 == 2⁸,或者为基于 KZG 的树指定 2²⁴。
Verkle 树中的证明比 KZG 稍长;它们可能有几百字节长。它们也很难验证,尤其是当您尝试将许多证据聚合为一个时。
实际上,Verkle 树应该被认为与 Merkle 树类似,但在没有 SNARKing 的情况下更可行(因为数据成本更低),并且在使用 SNARKing 的情况下更便宜(因为证明者成本更低)。
Verkle 树的最大优点是协调数据结构的可能性:Verkle 证明可以直接在 L1 或 L2 状态上使用,无需覆盖结构,并且对 L1 和 L2 使用完全相同的机制。一旦量子计算机成为一个问题,或者一旦证明 Merkle 分支变得足够高效,Verkle 树就可以替换为具有合适的 SNARK 友好哈希函数的二叉哈希树。
如果 N 个用户进行 N 笔交易(或者更实际地说,NERC-4337 UserOperations)需要证明N个跨链声明,我们可以通过以下方式节省大量gas聚合 这些证明:将这些交易组合成一个块或进入一个块的捆绑包的构建器可以创建一个单身的 同时证明所有这些主张的证据。
这可能意味着:
在这三种情况下,每个证明只需要花费几十万天然气。建筑商需要制作其中之一每个 L2 上 对于该 L2 中的用户;因此,为了使其有用,该方案作为一个整体需要有足够的用途,以至于同一块中经常至少有一些交易在多个主要 L2 上。
如果使用 ZK-SNARK,主要的边际成本只是在合约之间传递数字的“业务逻辑”,因此每个用户可能需要几千个 L2 Gas。如果使用 KZG 多重证明,证明者将需要为该块内使用的每个持有密钥库的 L2 添加 48 个 Gas,因此每个用户的方案的边际成本将为每个 L2 添加约 800 个 L1 Gas(不是每个 L2)。用户)在顶部。但这些成本远低于不聚合的成本,后者不可避免地涉及超过 10,000 个 L1 Gas 和数十万个 L2 Gas每个用户。对于 Verkle 树,您可以直接使用 Verkle 多重证明,为每个用户添加大约 100-200 字节,或者您可以制作 Verkle 多重证明的 ZK-SNARK,其成本与 Merkle 分支的 ZK-SNARK 类似,但证明起来要便宜得多。
从实施的角度来看,最好让捆绑器通过以下方式聚合跨链证明:ERC-4337 帐户抽象标准。 ERC-4337 已经为构建者提供了一种以自定义方式聚合 UserOperations 部分的机制。甚至还有一个@voltrevo/BJ0QBy3zi">BLS 签名聚合的实现,这可以将 L2 的 Gas 成本降低 1.5 倍到 3 倍,具体取决于还包括哪些其他形式的压缩。
图表来自@voltrevo/BJ0QBy3zi">BLS 钱包实施帖子 显示 ERC-4337 早期版本中 BLS 聚合签名的工作流程。聚合跨链证明的工作流程可能看起来非常相似。
最后一种可能性,仅适用于 L2 读取 L1(而不是 L1 读取 L2),是修改 L2,让它们直接对 L1 上的合约进行静态调用。
这可以通过操作码或预编译来完成,它允许调用 L1,您可以在其中提供目标地址、gas 和 calldata,并且它返回输出,但因为这些调用是静态调用,所以它们实际上不能改变 任何 L1 状态。 L2 必须了解 L1 才能处理存款,因此没有什么根本性的因素可以阻止此类事情的实施;这主要是技术实施方面的挑战(参见:Optimism 的此 RFP 支持静态呼叫 L1)。
请注意,如果密钥库位于 L1,和 L2 集成了 L1 静态调用功能,因此根本不需要任何证明!然而,如果 L2 不集成 L1 静态调用,或者密钥库位于 L2 上(一旦 L1 变得太昂贵而用户无法使用,哪怕是一点点,它最终可能必须如此),那么将需要证明。
上述所有方案都要求 L2 访问最近的 L1 状态根或整个最近的 L1 状态。幸运的是,所有 L2 都已经具有一些访问最近的 L1 状态的功能。这是因为他们需要这样的功能来处理从 L1 到 L2 的消息,尤其是存款。
事实上,如果 L2 具有存款功能,那么您可以按原样使用该 L2 将 L1 状态根移动到 L2 上的合约中:只需在 L1 上有一个合约,调用区块哈希 操作码,并将其作为存款消息传递给 L2。可以在 L2 侧接收完整的块头,并提取其状态根。然而,如果每个 L2 都有一种明确的方式来直接访问完整的最近 L1 状态或最近的 L1 状态根,那就更好了。
优化 L2 如何接收最近的 L1 状态根的主要挑战是同时实现安全性和低延迟:
此外,在相反方向(L1s 读取 L2):
对于许多 defi 用例来说,去信任的跨链操作的某些速度慢得令人无法接受;对于这些情况,您确实需要更快的网桥和更不完善的安全模型。然而,对于更新钱包密钥的用例,较长的延迟是更可以接受的:你不是延迟交易 几个小时,你就耽误了关键变化。您只需将旧钥匙保留更长时间即可。如果您因为密钥被盗而更改密钥,那么您确实会在很长一段时间内容易受到攻击,但这可以减轻,例如。通过钱包有冻结 功能。
最终,最好的延迟最小化解决方案是 L2 以最佳方式实现直接读取 L1 状态根,其中每个 L2 块(或状态根计算日志)包含一个指向最近的 L1 块的指针,因此如果 L1 恢复,L2也可以恢复。密钥库合约应该放置在主网上,或者放置在 ZK-rollups 的 L2 上,这样可以快速提交到 L1。
L2 链的区块不仅可以依赖于之前的 L2 区块,还可以依赖于 L1 区块。如果 L1 恢复经过这样的链接,L2 也会恢复。值得注意的是,这也是早期(Dank 之前)版本分片的设想工作方式;看这里 对于代码。
令人惊讶的是,没有那么多。它实际上甚至不需要是 Rollup:如果它是 L3 或 validium,那么只要您在 L1 或 ZK Rollup 上保存密钥库,就可以在那里保存钱包。你那件事做 链需要直接访问以太坊状态根,并且如果以太坊重组愿意重组,如果以太坊硬分叉则愿意硬分叉的技术和社会承诺。
一个有趣的研究问题是确定一条链在多大程度上可以与多种的 其他链(例如以太坊和 Zcash)。天真地这样做是可能的:如果以太坊,你的链可能会同意重组或者 Zcash 重组(如果以太坊则硬分叉或者 Zcash 硬分叉),但是您的节点运营商和您的社区通常具有双倍的技术和政治依赖性。因此,这种技术可用于连接到其他一些链,但成本会增加。方案基于ZK桥 具有有吸引力的技术特性,但它们有一个关键弱点,即对 51% 攻击或硬分叉的鲁棒性不强。或许还有更巧妙的解决方案。
理想情况下,我们还希望保护隐私。如果您有多个由同一密钥库管理的钱包,那么我们要确保:
这会产生一些问题:
使用 SNARK,解决方案在概念上很简单:默认情况下,证明是信息隐藏的,并且聚合器需要生成递归 SNARK 来证明 SNARK。
如今这种方法的主要挑战是聚合需要聚合器创建递归 SNARK,目前速度相当慢。
有了KZG,我们可以使用@vbuterin/non_index_revealing_proof">这项工作涉及非索引揭示 KZG 证明 (另请参阅:该工作的更正式版本填缝纸)作为起点。然而,盲证明的聚合是一个需要更多关注的开放问题。
不幸的是,从 L2 内部直接读取 L1 并不能保护隐私,尽管实现直读功能仍然非常有用,既可以最大限度地减少延迟,也可以用于其他应用程序。