---
title: Stackable L2：一种新的区块链扩容方案
date: '2024-03-19 06:17:09'
draft: false
summary: 围绕互操作、状态读取和扩容路径的一次延伸思考。
slug: stackable-l2
syndication:
- platform: X / Twitter
  url: https://x.com/jolestar/status/1769971353959157761
tags:
- layer2
- rollup
- blockchain
topics:
- blockchain
type: post
---

一直以来，L2 和 L1 之间如何实现互操作，L2 又如何读取 L1 上的状态，一直是 L2 方案设计上的一个挑战。一般的方案是通过状态证明来做，但这种方案要解决两个问题：

1. L2 如何知道 L1 的状态根。
2. 状态证明的表达形式，包括各种状态树或者 zk 证明，需要在成本和便利性之间权衡。

这个问题，Vitalik 在《Deeper dive on cross-L2 reading for wallets and other use cases》里有过详细讨论。Rooch 最早设计多链结算方案时，也采用了类似的思路：让 L2 嵌入 L1 的轻节点，通过验证 L1 区块来获得 L1 的状态根。

但这套方案一旦用在 Bitcoin 上，就会遇到额外的难题：

1. Bitcoin 上的 UTXO 并没有状态树，不能直接证明某个 UTXO 在某个区块高度是否被消费了，只能通过交易证明，证明某个交易是否包含在某个区块中。
2. 如果还要支持 Bitcoin 上附加信息的证明，比如 RGB、Ordinals，还需要证明交易的 Input 和 Output 之间的关联关系，这个挑战更大。

这里补充一下 Bitcoin 的一个基础知识：Bitcoin 共识在验证区块时，只检查 Input 和 Output 的 BTC amount 是否匹配，并不关心 Input 和 Output 的对应关系。

比如一笔交易里，Bitcoin 共识只验证 `Sum(Input) = Sum(Output) + Fee`。假如 Input 上携带了其他信息，它最终转移给哪个 Output 呢？这个就是 Bitcoin 扩展协议要解决的问题。它们各自发明了一套 Input 和 Output 的映射规则，比如 RGB / Runes 通过嵌入 `OP_RETURN` 来指定，Ordinals 通过 SatPoint 指定。

所以，L2 上的应用如果想要读取 Bitcoin 上的状态，就需要全量追踪 UTXO 的创建与消费，以及解析这些扩展协议的数据，于是就有了堆叠式（Stackable）L2 的想法。

![](./1769971353959157761-0-GJAw4OcboAAVBBK.jpg)

## 堆叠式 L2

堆叠式 L2 指的是：在 L2 中全量包含 L1 状态的一种 L2 扩容方案。这种方案要求：

1. L2 中包含 L1 的全量状态。相当于要求 L2 包含 L1 的全节点并完整执行 L1 共识协议，L1 的状态是 L2 状态的一个子集。
2. L2 可以拥有自己的交易以及状态。这点上有别于只读的 Indexer。
3. L2 需要有一种机制实现 L2 状态和 L1 状态之间的原子化绑定，保证 L1 和 L2 的状态（资产）所有权可以原子化转让。

如下图所示，L1 在区块高度 T 时的状态是 State T，而该 Block 会同时触发 L2 的交易 Y，生成 L2 的状态 State Y，State Y 中包含 State T。之后 L2 执行多个交易，其中 L2 的状态在变化，但 L1 State T 一直没有变化，直到 L1 Block T+1 触发新的 L2 交易 Y+n，产生新的 L1 和 L2 状态。

![](./1769971353959157761-1-GJAxEV9bUAA1jpi.png)

这种方案下，由于 L1 的所有状态都已经进入 L2，L2 应用可以直接读取，不需要再处理复杂的状态证明，开发者和用户体验都会好很多。

这个方向在业界也有类似尝试。Ethereum 社区有个 Booster rollups 方案，本质上也是在 Ethereum 上再堆叠一层，和 Rooch 的思路类似，不过解决方案上有所差异。

## 堆叠式状态树

既然 L2 包含了全量 L1 状态，以及 L2 自己的状态，就需要一种层级化的状态树方案。这种方案需要保证 L1 的状态可以生成独立状态树，以便做状态校验。

在 Rooch 中，第一层状态树的叶子结点是 Object，而每个 Object 也携带一个状态子树，子树中可以保存该 Object 的内部状态。

## Rollout not Rollup

堆叠式 L2 的交易包含两部分：一部分是 L1 的区块，另外一部分是 L2 自己的交易。L1 区块的可用性由 L1 保证，那 L2 的交易如何保证可用性？

在传统 Rollup 模式中，L2 的交易会通过 Sequencer 批量 Rollup 到 L1。但这里的关键问题其实是：如果要实现 L1 状态和 L2 状态之间的原子化绑定，我们需要一种资产嵌套的表达模式，而 Move 语言本身很适合表达这类嵌套资产。

比如上图中的 UTXO X 在 Move 中可以表达为一种 Object，它内置一个 Temporary 区域，嵌套放置在该区域中的状态。于是，在堆叠式 L2 的方案中，除了关注 L2 继承 L1 状态之外，还要考虑 L1 和 L2 之间的资产如何跨层流动。这里主要有几个探索方向：

1. L1 → L2 的桥模式：L1 和 L2 之间通过桥实现资产迁移。关键是这个桥如何借助 L1 和 L2 提供的特性来保证安全。
2. L2 的去中心化与安全：Sequencer 如何去中心化，依然是业内探索的方向。在 Rooch 的方案中，主要通过以下方式实现去中心化以及安全性：
   - Sequencer 会把交易公布到 DA，把 L2 状态树的根公布到 Bitcoin 上，保证 L2 状态的可验证。
   - 如果 Sequencer 公布了错误状态，当公布状态根的 Bitcoin 交易被确认之后，就可以通过后续挑战或仲裁机制追责。

![](./1769971358115721448-0-GJAxaDjakAAs-LB.png)

区块链扩容一直是业内最重要的问题之一，已有分层、分片等方向的实践，但层和片应该如何分，依然还在摸索。这个思路的关键不只是“再叠一层”，而是让 L2 启动时不是一个空白世界，而是尽可能继承 L1 的数据和特性。
