与 L1 不同,有效性证明 Rollup 吞吐量不受限制。L2 有效性证明 Rollup 上可以产生更高的 TPS
StarkNet 性能路线图解决了系统中一个关键要素:排序器(sequencer)
性能改进路线图如下:
— 排序器并行化
— Cairo 虚拟机用 Rust 实现
— 排序器用 Rust 重新实现
经过实战考验的证明器并不是瓶颈,处理量可以比现在更多!
StarkNet 大约在一年前登上主网,团队一开始专注于功能性,而现在重点转移到了提高性能上,通过一系列步骤来提升 StarkNet 用户体验。
在这篇文章中,将解释为什么只有有效性证明 Rollup 可以有广泛优化空间,并分享在 StarkNet 上实施这些优化的计划。其中一些已在 StarkNet Alpha 0.10.2 中实现,该版本于 11 月 16 日上线测试网,并在 11 月 28 日上线主网。那么在讨论解决方案之前,先来回顾一下性能的限制及其原因吧。
提高区块链可扩展性和 TPS 的一种潜在方式是取消区块限制(在 gas 或区块大小方面),同时保持出块时间不变。对生产区块者(L1 上的验证器和 L2 上的排序器)和组件性能要求更高。为此,现在团队的开发重点转移到优化 StarkNet 排序器上,下文将对此部分进行更详细的描述。
这里自然还会出现一个问题。为什么排序器优化仅限于有效性证明 Rollup?换句话说,为什么不能就在 L1 上优化,非要费事部署有效性证明 Rollup?在下一小节中,将阐述两者之间的根本区别,解释为什么可以在 L2 上进行广泛优化,而在 L1 不适用。
如果解除 L1 的区块限制,将会有个严重的问题。提高区块链的吞吐增长率,也会提高对全节点的需求,来保证跟进最新的状态。由于 L1 全节点必须重新执行所有的历史记录,区块大小的大幅增加(就 gas 而言)会带来巨大压力,再次导致较弱的机器退出系统,并将运行完整节点的能力留给足够强大的实体。结果就是,用户无法自己验证状态,也不能无需信任地参与网络。
所以应该限制 L1 吞吐量以维护真正去中心化和安全的系统。
只有从全节点的角度考虑,我们才能看到有效性证明 Rollup 的真正实力。L1 全节点需要重新执行整个历史记录以确保当前状态的正确性。而 StarkNet 节点只需要验证 STARK 证明,且这种验证所占用的计算资源呈指数级下降。特别是,从头开始同步不必涉及执行;节点可以从其他节点获取当前状态的转储,只需通过 STARK 证明来验证状态是否有效。这样就可以增加网络的吞吐量,而不增加全节点负载。
因此可以得出结论,优化 L2 排序器即是全方位提升系统性能,而在 L1 上是无法实现的。
下文将讨论 StarkNet 排序器优化计划。
路线图的第一步是将并行化引入交易执行,在 11 月 28 日上线主网的 StarkNet Alpha 0.10.2 中已引入。现在来深入了解下什么是并行化(这是个半技术性的部分,若要了解路线图,请跳到下一节)。
那么「交易并行化」是什么意思呢?简单来说,并行执行多个交易区块是不可能的,因为不同的交易可能是相互依赖的。下方示例中进行说明,假设有一个区块包含来自同一用户的三笔交易:
交易 A:将 USDC 兑换为 ETH
交易 B:用 ETH 购买某个 NFT
交易 C:将 USDT 兑换为 BTC
显然,交易 A 必须在交易 B 之前发生,但交易 C 完全独立所以可以并行执行。如果每执行一笔交易需要 1 秒时间,那么引入并行化后,出块时间就可以从 3 秒减少到 2 秒。
问题的症结所在是事先并不知道交易的依赖关系。实际上,只有当执行交易 B 时,才能知道它依赖于交易 A 所做的更改。更正式一点的说法是,这种依赖性源于交易 B 从交易 A 写入的存储单元中读取的事实。可以把交易看作一个依赖图,从交易 A 到交易 B 有一个边界,仅当 A 写入以一个由 B 读取的存储单元时,交易 B 才可以执行。下图显示了这种依赖图的一个示例:
在上面这个示例中,每一列都可以并行执行,这是最佳安排(一般会按顺序执行交易 1-9)。
为克服事先不知道依赖图这一难题,团队根据 Aptos Labs 开发的 BLOCK-STM 为 StarkNet 排序器引入了乐观并行化(optimistic parallelization)。在此范式下,系统将乐观地尝试并行执行交易,在发现冲突时重新执行。例如,并行执行图中的交易 1-4 时,会发现交易 4 依赖于交易 1。因此,它的执行是无效的(尽管执行交易 4 需要依照交易 1 执行后的状态,但先尝试对照交易 1 相同的状态同步执行)。在这种情况下,将重新执行交易 4。
请注意,乐观并行还有许多可优化之处。比如,不是单纯等待每次执行结束再判断,而是可以在发现有无效交易依赖项时中止执行。
另一个可以优化的例子是选择哪些交易来重新执行。假设上图中所有交易打包进一个区块送入 5 核 CPU 的排序器。首先尝试并行执行交易 1-5,如果完成顺序是交易2、交易 3、交易 4、交易 1,最后是交易 5,那么只有在交易 4 已经执行完之后,才会发现交易 4 依赖于交易 1,此时应重新执行。一般可能会想也重新执行交易 5,因为考虑到重新执行交易 4 可能会导致交易 5 的行为有所不同。然后,遍历执行已终止的交易所构建的依赖图,可以发现只需重新执行依赖于交易 4 的交易,而不是仅仅交易 4 无效之后重新执行所有交易。
StarkNet 中的智能合约是用 Cairo 编写的,并在 Cairo 虚拟机中执行,说明可查阅 Cairo 白皮书。目前,排序器使用 Python 版 Cairo 虚拟机实现。为优化虚拟机的实现性能,开发团队正在用 Rust 重写虚拟机。感谢 Lambdaclass 的出色工作,他们是 StarkNet 生态系统中非常宝贵的团队,这项工作也很快就会取得成果。
虚拟机的 Rust 实现(cairo-rs)现在可以执行原生 Cairo 代码。下一步是处理智能合约的执行,并与 python 兼容的排序器集成,一旦与 cairo-rs 集成,排序器的性能有望得到显著提升。
从 python 转到 Rust 对性能的提升不仅限于 Cairo 虚拟机。除了上述改进之外,团队还计划用 Rust 重新开始写排序器。除了 Rust 的内部优势之外,还能从其他方面优化排序器。比如可以兼容 cairo-rs,无需负担 python-rust 的通信开销。也可以完全重新设计状态的存储和访问方式(现在是基于 Patricia-Trie 结构)。
在上文中还未提到有效性证明 Rollup 里最重要的原件 — 证明器。可以想象,作为可以说是架构中最为复杂的组件,证明器应该是性能瓶颈,因此也应该是优化的重点。但有趣的是,现在 StarkNet 的瓶颈是更加「标准」的组件。特别是有了递归证明,证明中的可容纳交易量比当前测试网和主网更高。事实上,StarkNet 区块与 StarkEx 交易一起得到有效的证明,后者有时会有数十万 NFT 的铸造事件。
为即将到来的 StarkNet 新版本中改进的 TPS 做好准备吧,敬请期待并行化、Rust 还有更多!