这篇文章基于与 Sutton、Roma 和 Ilia@Starkware 的广泛对话和头脑风暴。
我们正在努力设计 Kaspa 对 L2 的 L1 支持。我们的设计遵循基于 zk 的卷叠 (ZK RU) 的原则:所有智能合约活动都作为有效负载(以特用术语中的 blob)记录在 L1 中,并且所有逻辑区的状态(智能合约或 rollup,我故意互换使用它们,原因我将在后面或单独的帖子中解释)都提交到区块头中。
术语 rollup 最初需要链上排序,但现在链上排序的 rollup 成为例外,而不是规则,被称为基于 rollups。为了将某个 logic zone 的新状态锚定到 Base 层,在一个或多个状态转换之后,某个 prover 节点必须向 base 层发送一个提供 ZKP 的交易。后者在 base consensus 中通过 new 进行验证,并且 logic zone 的 (承诺) 状态随后更新。至关重要的是,证明可以以不可忽略的延迟提交,尽管用户可以获得遵循此逻辑区的任何节点的即时(~100 毫秒)确认,执行其(L1 排序的)交易,并解析其状态。简而言之:证明延迟不会影响最终确定性延迟;它会影响新 L2 节点的状态同步时间、L1 修剪间隔以及跨逻辑区事务的计算负载。op_code
最好的智能合约层是为原子或同步可组合性而设计的,其中智能合约可以允许来自其他智能合约的函数在触发交易的范围内就地调用它并与其变量交互(读写)。这种跨智能合约功能描述了以太坊早期的链上活动,可以说促进了其生态系统和开发社区的惊人增长。不幸的是,以太坊以 rollup 为中心的路线图正在与这种原子可组合性功能相悖,并满足于异步可组合性(这仍然比需要手动组合的 TradFi UX 好得多)。
在异步可组合性中,智能合约仍然可以相互交互和发送消息,但这是通过承载或桥接这些消息的某个层(通常是基础层)完成的,该层存在一些延迟。因此,读/写操作不会在原始交易的范围内进行处理,无法保证原子性,并且无法提前保证可组合交易的效果(合约的变量和交易后发卡机构的账户)。
注意:由于多个用户同时行动,不可避免地缺乏可预测性是一个单独的问题;交易发行者可以通过多种技术来强制执行行为,例如滑点规范、交易中的明确条件或意图规范。异步可组合性的问题在于,只有部分事务的效果可以完全强制执行。
对于为什么异步可组合性足够好,有一些争论,但我不会在这里讨论它们。让我们假设我们不想解决这个问题。好消息是,我们不需要这样做,因为我们正在完全基于 zk 的模式:由于所有数据都在链上,因此每个逻辑区的状态都可以从链上数据完全重建(直到修剪点)。因此, multi-logic zone transaction 的影响发生在其 sequences-close 上,没有延迟,并且可以同时在所有 logic zones上执行其影响的条件。将这种动态与非基于卷叠为中心的以太坊形成对比,其中逻辑区 I 交易的半链下排序可能对 L1(和逻辑区 II 证明者)无法访问,因此无法对其进行证明。我重申,交易的同步原子性与证明器延迟无关:证明到达频率不会影响 L1 或 L2 中的确认时间。
现在,考虑一个可组合的事务 txn,它不仅作用于两个逻辑区,而且还触发它们之间的交互;例如,事务调用 logic zone I 内的函数,并将此交互的输出用作对 logic zone II 的函数调用的参数。请注意,要验证 logic zone II 的正确执行,base layer 必须看到 logic zone II 和 logic zone I 的正确状态转换的证明,因为后者的输出或中间状态是前者状态转换的输入。同样,希望 follow 和 parse 其 state 的 logic zone II 的 operator (读作: provers) 也必须 follow 并执行 logic zone I。
这种依赖关系乍一看似乎是有问题的——如果可组合事务的存在意味着所有证明者都需要执行所有逻辑区的所有事务,那么架构应该会坍缩成一个大逻辑区,该逻辑区遭受与面向计算的 L1 相同的可扩展性障碍——每个 txn 消耗为所有其他 txn 提供服务的相同计算负载。但事实并非如此,因为:
执行其他 logic zones 的 transactions 只需要在 logic zones 交互时发生。
Logic Zones 可以为特定 logic zones 定义权限 (在其程序代码中) 以在 sync 模式下与它们交互,并要求其他 logic zones 通过 Base Layer 的消息传递协议以异步模式进行交互。
交易执行比证明生成便宜 2 或 3 个数量级。请注意, logic zone II 的证明者需要执行,但不需要证明 logic zone I 的 (intermediate) 状态,这是计算密集型部分。
运行 Prover 需要无需许可,但不一定是去中心化的,因为优化商用硬件能够运行系统范围的 Prover。
这些考虑意味着理想的 ecosystem 将最小化 logic zones 的范围,作为副产品,这将最大限度地减少跨 logic zone 依赖性以及依赖性的影响(例如,上述执行负担)。我强烈鼓励在 Kaspa 上构建的 L2/SC 项目遵循此设计原则,并避免将许多可分离的逻辑区(智能合约)聚合在一个总体逻辑区(rollup)下。
需要注意的是,当 logic zones 不支持原子可组合性,而是通过 L1 的消息传递功能使用异步可组合性时,它们会受到证明者的延迟,而不仅仅是(仅)Base Layer 的延迟。因此,即使 Kaspa 实现了 10 (Sutton,读作:100) BPS,如果逻辑区 I 的证明者每 10 分钟提供一次证明,那么这就是异步可组合交易将遭受的延迟;对于许多应用程序,10 分钟 = 无穷大(这就是为什么比特币实际上不能作为基于 zk 的卷叠的基础层)。这就是为什么我认为我们应该坚持原子可组合性。
关于此结构的最后一点评论:回想一下,上述可组合 txn 会强制逻辑区 II 的证明者在 txn 的第一部分之后执行逻辑区 I 的状态。现在,前者运营商的证明只有在后者提交后才能出现在链上;让我们用 来表示此状态。但是,逻辑区 I 证明者可能会提交一个证明,该证明对 I 的一系列状态转换进行批处理,其中只是一个中间成员。为了允许 II 的证明者利用 I 的(块)证明来构建他们的证明,我们必须确保 L1 可以访问 I 的证明者已经证明的中间状态。换句话说,我们需要所有证明来承诺到累加器中的所有中间状态(例如,默克尔树),然后 II 的证明者可以在证明其执行的同时为该承诺添加一个见证。state_I_pre
state_I_pre
冒着过度销售本文中讨论的功能的风险,我认为我在这里提出的结构提取了所有架构中最好的:比特币、以太坊(以 rollup 为中心的路线图)、Solana——Nakamoto 基础层的互联网速度版本(面向验证)、基于 zk 的计算层和类似 Solana 的统一碎片整理状态。
如何控制 L2 的吞吐量?让我们用熟悉的 “gas” 单位来命名计算。应如何执行 gas 限制?由于 L1 的排序器(Kaspa 中的矿工)是唯一能够选择和优先处理进入系统的交易的实体,因此 L1 层也必须采用 gas 调节机制。最简单的设计是将 gas 单位转换为质量单位,由于后者是每个区块的上限,因此每个区块的 gas 也会是上限。这种一维限制将不同性质的资源(L1 mass(脚本计算和存储)和 L2 验证负载)汇集在一起,这在经济上是低效的:例如,它意味着发出 gas 密集型、存储量低的交易的用户可能会被发出存储密集型交易的用户所击败,尽管她没有对它们施加外部性,并且可以在不消耗相同资源的情况下获得共同批准。因此,我们应该保持 mass 和 gas 吞吐量约束的解耦,即以 AND 的形式为块提供二维吞吐量限制。该提案意味着矿工在从 mempool 中选择交易时将面临二维背包问题。mass(block) < mass_limit
gas(block) < gas_limit
注意:同样的讨论也适用于我们对 L1 脚本计算大小和 KIP-9 存储大小 的耦合。尽管如此,我们还是选择将它们耦合在相同的质量函数下,并接受理论上的经济低效率。
请注意,对 L2 业务的需求激增不会转化为证明者更高的收入——矿工,即 L1 俱乐部的选择者,将收取利润这种利润流似乎还可以,因为 gas 限制意味着证明者的负载在需求较高时不会增加(超过限制);需要更多地考虑这个问题。
提出的另一个提案是在 gas 单位上运行 EIP-1559 机制的变体,该机制 (i) 将基本费用利润流向证明者,并且 (ii) 将消除运行双背包的复杂性,因为它为每个 gas 单位提供了足够精确的共识价格。
无论哪种方式,在设置 时,要记住的一个考虑因素是证明器上的平均负载和峰值负载之间的差距:虽然逻辑区 I 通常可能包含区块中 gas 容量的 10%,但其相对需求在峰值需求中可以达到 100%,此时用户最希望与它交互。在这里,经济高效的设计也会限制 block 每个 logic zone 的 gas。然而,这将导致从内存池中构建一个区块的 n 维背包问题,因此我们目前选择一种更简单的设计,每个区块一个,承认经济上的次优性。gas_limit
gas_limit
通过上述任何一种机制,L2 项目都可以为证明者设想额外的资助计划。从生态系统的角度来看,这些费用必须在 KAS 中提供,为 Kaspa 的飞轮做出贡献,并协调所有参与者。
我再次强调,我在这里总结了上述共同贡献者提出的想法和考虑。这里讨论的一些主题仍在开发中,可供讨论,因此采用了研究帖子的形式。