本文是关于 ZKStack 协议升级的技术结构的探索,重点介绍了构成安全跨链通信骨干的三个关键 Merkle 树实现:L2ToL1LogsTree
、ChainTree
和 SharedTree
。文章详细描述了每个树的叶子节点构成、大小以及根的用途,并解释了它们在 ZKChain 生态系统中的作用,为理解跨链交互的底层机制提供了重要信息。
这个分为两部分的文章系列探讨了即将到来的 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 上结算,或者它本身是否是 Settlement Layer,所有 ZKChain 都以相同的方式构建 L2ToL1LogsTree
。
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
前缀,然后加上相应的 batchNumber
后缀,然后将其全部哈希到大小为 32 字节的适当叶子。
与具有固定大小的 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 数据结构表示,它在存储中保存所有叶子和节点值。
每个叶子通过哈希chainRoot
的填充版本及其 chainId(称为 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/pa...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!