本文是 ZKStack 协议升级技术结构的探索系列的第一部分,重点介绍了构成 ZKChain 生态系统中安全跨链通信骨干的三个关键 Merkle 树实现:L2ToL1LogsTree、ChainTree 和 SharedTree,并详细解释了它们的构成、大小以及根的用途。
这个分为两个部分的文章,探讨了即将到来的 ZKStack 协议升级的技术结构,该升级引入了一个 ZKChain 生态系统,该生态系统可以通过在另一个 ZKChain 而不是 L1 上进行结算,从而轻松创建新的 rollup ZKChain,内置跨链功能并降低结算成本。
第一部分描述了三个关键的 Merkle 树实现,它们构成了 ZKChain 生态系统中安全跨层通信的支柱,即 L2ToL1LogsTree
、ChainTree
和 SharedTree
。
第二部分说明了这种树层次结构的最重要的用例,即 ZKChain 在另一个 ZKChain 而不是直接在 L1 上进行结算,方法是为其跨链代币转账构建递归证明。
这个分为两个部分的文章描述了撰写本文时 ZKChain 生态系统的当前状态及其内部流程。 随着生态系统的发展,某些描述的功能将来可能会更新。
让我们深入了解这些支持生态系统的关键树数据结构的细节。 对于每棵树,我们系统地探讨它的:
有用的术语
Gateway
ZKChain。 将在本系列的下一部分中进一步探讨 Gateway
链。DiamondProxy
中。在第一部分中,我们从相对于其结算层的通用角度描述树。 在这里,我们使用 L2 来指代通用的 ZKChain,而 L1 指代其结算层。
这棵树由每个已提交批次从 L2 到 L1 的所有已启动的跨链交易组成。 因此,每个批次都有自己的 L2ToL1LogsTree
。 它构建在每个 L2 链上,其中包含 L2 交易产生的日志,L1 应该收到通知。 无论 ZKChain 是在 L1 上还是在另一个 ZKChain 上结算,或者它本身是否是结算层,L2ToL1LogsTree
对于所有 ZKChain 的构建方式都相同。
L2ToL1Log
L2ToL1Log
叶由来自每个批次的 L2 交易的消息或日志组成,包括但不限于:
通常,不需要 L1 通信的常规 L2 交易不包括在 L2ToL1Logs 中。 相反,L2 状态差异会发布在 L1 上,以确保数据可用性。
16_384
这棵树的 固定大小为 2^14 个叶子,这使得它非常大,高度为 14 层。 但是,它可能非常稀疏,这意味着有很多空叶子。 在这种情况下,它的构造需要用默认值填充所有内容,表示一个空叶子。 因此,这棵树有足够的空间供未来高吞吐量链使用。
LocalLogsRootHash
通过以通常的方式哈希 L2ToL1LogsTree
merkle 树,可以得到局部根,称为 LocalLogsRootHash
,由 16_384 个叶子构成。
请注意,正如本系列的下一部分将解释的那样,这不是在发布批次时提交到结算层的根。 为了获得批次的根,LocalLogsRootHash
进一步与 sharedTree
的根在右侧连接,以生成 fullRootHash
。 (sharedTree
的面纱很快就会在下面揭开。)
当创建一个新的 ZKChain 时,它的 ChainTree
在 L1 上的 MessageRoot
合约以及 ZKChain 的结算层(如果它与 L1 不同)中启动。 每个 ZKChain 都有自己的 ChainTree
。
尽管 MessageRoot
合约在所有 L2 ZKChain 的创世时都被强制部署,但只有结算层具有非平凡的 ChainTree
(以及 SharedTree
,我们稍后会看到)。 与 L2toL1LogsTree
相比,链的 ChainTree
不存在于它自己的链上,而是位于其结算层的 MessageRoot
合约中。
通常,结算层负责跟踪每个在其上结算的 ZKChain 的最新 ChainTree
。 其目的是将有关成功结算的批次的所有信息提交到单个根哈希中。
chainBatchRoot
当来自此 ZKChain 的新批次在结算层结算时,ZKChain 的 ChainTree
会增长一个新的叶子。 因此,这棵树代表了从 L2 到 L1 所有成功执行的批次的记录。 这棵树使用 DynamicIncrementalMerkle.Byte32PushTree 数据结构表示。
叶子的构造方式是,首先在每个批次的已提交根(即 fullRootHash
(即 L2toL1LogsTree
和 SharedTree
的根的串联))前加上 BATCH_LEAF_PADDING
,然后在哈希所有内容到适当的 32 字节大小的叶子之前,后缀相应的 batchNumber
。
与具有固定大小的 L2ToL1LogsTree
不同,ChainTree
是一种动态结构,随着链的结算层上每个新执行的批次而递增地增长。 树的叶子的总数等于已在该层结算的批次数。 但是,如果链已从一个结算层迁移到另一个结算层,则这棵树将拆分。 当链迁移回来时,先前停滞的 ChainTree
将继续增长。 因此,如果发生了迁移,ChainTree
的叶子的索引可能与其批号不对应。
动态性质使其能够有效地适应链的实际吞吐量,而无需预先确定的空间分配。
chainRoot
通过以通常的方式哈希 ChainTree
叶子,可以得到 chainRoot
。 对于每个结算 ZKChain,每当执行一个新批次时,都会更新此根,并立即用于更新 SharedTree
的相应叶子。
SharedTree
中的每个叶子都对应于结算 ZKChain 的 ChainTree
根。 SharedTree
位于 MessageRoot
合约中,这意味着与所有结算 ZKChain 的 ChainTree
所在的合约相同。 请注意,与 ChainTree
类似,尽管所有 ZKChain 共享相同的设计并具有 SharedTree
,但只有结算层的 SharedTree
是非平凡的。 不作为结算层的 ZKChain 将仅公开一个表示默认 SharedTree
的根。
SharedTree
的目的是将包含在所有结算 ZKChain 的 ChainTree
中的所有批次信息提交到单个根哈希中。
chainRoot
当一个新的 ZKChain 第一次添加到该层或是迁移到该层时,结算层的 SharedTree
会增长一个新的叶子。 每个叶子对应于一条链。 这棵树使用 FullMerkle.FullTree 数据结构表示,在存储中保留所有叶子和节点值。
每个叶子由其 ChainId 对 chainRoot
的填充版本进行哈希 形成,称为 ChainIdLeaf
。 每次 ZKChain 的 ChainTree 根更新时(即,每当结算一个批次时),SharedTree
中相应的叶子也会更新。
除非添加一个新的 ZKChain,否则 SharedTree
的大小不会增长。 随着每个新批次的结算,ShareTree 保持其大小,但更新其根。 因此,与 ChainTree
相比,SharedTree
的预期大小较小,并且上限为 100。 请注意,在非结算层中,SharedTree
本质上是空的。
aggregatedRoot
对任何结算 ChainTree
的每次更新都会触发对 aggregatedRoot
的相应更新。 在不是结算层的 ZKChain 上,aggregatedRoot
始终是以其自己的 chainId初始化的默认哈希。 但是,在列入白名单的结算层上,这是对结算在那里的所有 ZKChain 的整个交易历史的承诺。
这三棵 Merkle 树共同构成了一个复杂的密码学架构,可确保 ZKChain 与其结算层之间的安全、可验证的通信。
在第二部分中,我们将探讨这种分层结构的最重要应用,即当 ZKChain 在另一个 ZKChain 上结算时,即所谓的 Gateway
链,这是目前唯一列入白名单的结算层。 我们将说明如何为跨链代币转账的具体用例构建递归证明。
- 原文链接: blog.openzeppelin.com/in...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!