该文章提出了一种快速提款方案,允许L2用户在相同的L1 slot内将代币从L2提款到L1。该方案依赖于“解算器(Solvers)”,解算器观察L2的提款请求,并在L1上提供解决方案。为了降低解算器的风险,提款解决方案取决于包含该提款请求的L2批次是否成功提交到L1。只有包含提款请求的状态根在L1上被证明后,解算器才能获得报酬。
由 @The-CTra1n、@linoscope、@AnshuJalan 和 @smartprogrammer 共同撰写,他们都来自 Nethermind。感谢 Taiko 的 @Brecht 和 Daniel Wang 对此解决方案的反馈。反馈不一定代表认可。
概要
我们引入了一种新的快速通道,用于在同一 L1 插槽内将 L2 提款至 L1,该通道由求解器启用。为了降低求解器的风险并最大化参与度,求解器可以观察包含快速提款请求的预确认 L2 批次,并将这些请求的解决方案建立在包含这些请求的批次成功提交到 L1 的基础上。这确保了求解器除了需要等待包含请求的 L2 状态根在 L1 上被证明以获得促进提款的报酬外,没有其他风险。
概述
本文介绍了一种我们称之为快速 & 慢速 (FnS) 提款的协议,该协议使 rollup 用户能够原子性地将代币从 L2 提取到 L1。当我们说原子性提款时,我们的意思是 L2 提款交易首次出现在 L1 上时,可以在同一插槽中为 L1 用户提供代币,而不会给 L2 桥带来重组风险。这是通过促进这些原子性提款的求解器来实现的。
任何 L1 用户都可以充当求解器。潜在的求解器观察 L2 上的 L2 提款请求,并代表 L2 用户在 L1 上解决该请求。这重新利用整个 L1 网络作为快速 L2 提款的求解器网络。利用 rollup 将其交易批次发布到 L1 的特点,FnS 允许求解器根据 L2 提款存在的顺序来确定提款解决方案。这使 L1 求解器能够保证当包含提款请求的状态根在 L1 上得到证明时,L2 用户提取的代币将提供给求解器。
当包含请求的状态根在 L1 上得到证明时,请求提款的 L2 代币在 L1 上可用。这就是“慢速提款”,它总是会发生。请求的求解器获得对应该请求的慢速提款代币的唯一访问权。因此,L1 求解器必须等待一段时间,L2 代币才能在 L1 上可用。但正如所提到的,选择加入快速 FnS 提款对于求解器来说是完全可选的,除了他们必须在提供有效解决方案后等待慢速提款外,求解器无需锁定代币。因此,L2 用户有责任提供涵盖以下内容的费用:
- 等待在 L1 上接收 L2 代币的成本
- 提交解决方案并从桥接器收集慢速提款代币的 L1 费用。
这是如何运作的?
在下文中,我们将请求(Request)、请求的解决方案(Solution)、提交请求的用户(Users)和提供解决方案的求解器(Solvers)大写,以确保这些对象与动词清晰区分。
下图说明了由求解器促进的快速通道提款是如何运作的:
[
\\
1188×883 102 KB](https://ethresear.ch/uploads/default/original/3X/c/c/cc9b2976555f7cc812f27ddbaf62b2ad7a318c08.png "")
- (A) 想要快速提款的 L2 用户向 L2 FnS 合约发送一个请求(Request),其中包括用户希望提取的代币和应将代币发送到的 L1 地址。
- (B) L1 求解器(成为求解器是无需许可的,没有注册要求)通过预确认(或 L1 内存池)观察 L2 批次中的 FnS 请求,可以尝试使用解决方案(Solution)来后运行 L2 批次,以满足提款请求(Request)。解决方案(Solutions)将指定的代币发送到指定的目的地地址。
- (C) 解决方案(Solutions)可以建立在包含请求(Request)的 L2 批次的基础上,从而确保执行 L2 区块的求解器没有重组风险:如果 L2 区块被记录,则解决方案(Solution)不会执行。
- (D) 在包含请求(Request)的已证明 L2 状态根提供给 L1 之前的任何时间,都可以通过解决方案(Solution)进行快速提款。
- (E) 在包含请求(Request)的已证明状态根在 L1 上执行后,将触发慢速提款路径。此时,FnS 合约中的 L2 代币可从 L1 FnS 合约中提取。
- (E-1) 如果为有效请求(Request)提供了有效的解决方案(Solution)(有效性在状态根证明中检查),则提交解决方案(Solution)的求解器(每个请求(Request)只接受一个解决方案(Solution))可以从 L1 FnS 合约中提取代币。
- (E-2) 否则,用户可以从 L1 FnS 合约中提取他们的代币。
技术规范
本节规定了 FnS 提款的算法。
使用的智能合约和交易术语表
我们引入以下术语、概念、数据结构和智能合约。
- L1 rollup 合约。L1 上的智能合约,rollup/L2 状态转换在此记录和证明。
- L2 交易批次。L2 交易通过其发布到代表 L2 的 L1 智能合约的数据结构。每个 L2 交易批次都可以附带一个签名,验证签名者提议了该批次。
- L2 提议者。关于给定的 L2 交易批次,有权将该批次发布到 L1 rollup 合约的实体。此提议者角色可以是:
- 许可的。在中心化排序器或预确认器的情况下,L2 提议者分别是相应的中心化排序器或预确认器。
- 无需许可的。任何人都可以签名并提交批次。
- L2 提款请求(Request):来自 L2 用户的 L2 交易,指定
- 请求 ID(Request ID)。每个请求(Request)的唯一标识符,它是对该请求(Request)中其他变量的抗冲突承诺,例如,这些变量编码的哈希值。
- L2 输入代币(L2 input tokens)。由用户发送(代币地址和金额)
- 输出条件 1。L1 输出代币(代币地址和金额)
- 输出条件 2(Output Condition 2)。L1 输出地址。必须接收 L1 输出代币的地址。
- 随机数(Nonce)。用于生成唯一请求 ID(Request ID)的随机数。
- 求解器(Solver):通过满足请求(Request)的 L1 输出条件来“解决”用户请求(Request)的实体。
- 解决方案(Solution):来自求解器(Solver)的 L1 交易,满足请求(Request)的 L1 输出条件。尽管求解器(Solvers)可以通过任何产生输出条件的方式解决请求(Requests),但每个请求 ID(Request ID)只允许一个解决方案(Solution)。解决方案(Solutions)具有以下输入:
- (L2 input tokens, Output Condition 1, Output Condition 2, Nonce)
- 请求 ID(Request ID)。必须对应于来自解决方案(Solution)中先前变量的承诺函数输出结果。请注意,L1 智能合约不需要读取 L2 状态来匹配解决方案(Solution)的“请求 ID”(Request IDs)与实际的请求 ID(Request IDs)。这是求解器(Solver)的工作。
- 链接的 L2 批次承诺(Chained L2 batch commitment)。(先前的链接的 L2 交易批次承诺,当前的 L2 交易批次承诺)对的承诺。需要防止请求(Request)输入代币的双重花费。
- L1 求解器输出地址(L1 Solver output address)。如果请求 ID(Request ID)被正确解决,则请求 ID(Request ID)的 L2 输入代币将被发送到的地址。
- Calldata(实际的解决在此完成,可以是任何指令,例如将代币发送到 L1 求解器合约,然后该合约根据输出条件发送代币)。
- L1 求解器合约(Contract):一个 L1 智能合约,它将解决方案(Solutions)作为输入。对于每个解决方案(Solution),它应该记录请求 ID(Request ID)以防止双重解决(一件坏事)。对于每个解决方案(Solution),L1 求解器合约必须验证解决方案(Solution)中指定的 L2 输出条件是否得到满足。
- (L1-L2) 桥合约(Bridge Contract):一个存在于 L1 上的合约。发送到 L2 上的特定销毁合约的 L2 代币可以在 L1 上从桥合约(Bridge contract)中提取,前提是向桥提供一个已证明的 L2 状态根,其中包含 L2 上的那些代币销毁。对于本文档,我们假设这是将代币从 L2 提取到 L1 的唯一方法。
- L2 求解器合约(Contract):一个 L2 智能合约,请求(Requests)被发送到该合约。发送到 L2 求解器合约(Solver Contract)的代币被销毁,并发出信号以从桥合约(Bridge Contract)在 L1 上提取。
- L1 求解器提款交易:包含:
- L2 状态根 ID(L2 State Root ID):发布到 L1 的已证明 L2 状态根的 ID。
- 请求 ID(Request ID)。
- Merkle 证明(Merkle Proof):请求ID(Request ID)存在于对应于L2 状态根 ID(L2 State Root ID)的状态根中的证明。
- L1 用户提款交易:包含:
- L2 状态根 ID(L2 State Root ID):发布到 L1 的已证明 L2 状态根的 ID。
- 完整的 L2 提款请求交易:(L2 input tokens, L1 output token address, L1 output address, Nonce, Request ID)
- Merkle 证明(Merkle Proof):请求 ID(Request ID)存在于对应于L2 状态根 ID(L2 State Root ID)的状态根中的证明。
协议描述
我们现在将逐步介绍该协议。
- L2 用户向 L2 求解器合约(Solver Contract)提交一个有效的请求(Request)。
- L2 提议者(proposer)将请求(Request)包含在 L2 交易批次中,并签署该批次。
- 求解器(Solver)对有效签名批次中的请求(Request)作出反应:
- 观察签名批次中的有效请求(Request)。
- 根据术语表中指定的格式,构建请求(Request)的解决方案(Solution)(如何满足请求输出条件)。
- 以下任一方式:
- 求解器(Solver)创建一个 L1 交易包,第一个交易是包含请求(Request)的 L2 批次,第二个交易是请求(Request)的解决方案(Solution)。
- 求解器(Solver)将解决方案(Solution)提交给第三方,例如 L2 提议者,另一个求解另一个请求(Request)的求解器(Solver),以便与 L2 批次捆绑在一起,并将此捆绑包发布到 L1。
- L2 交易批次在 L1 上接收到 L1 rollup 合约。
- 如果 L2 提议者(proposer)是许可的,请验证随附的签名是否来自 L2 提议者(NOT 与验证 msg.sender 相同)。
- 解决方案(Solution)在 L1 上接收到 L1 求解器合约(Solver contract)。对于给定形式的解决方案(Solution),
( _L2 input tokens,_
_L1 output token address,_
_L1 output address,_
_Nonce,_
_Request ID,_
_Chained L2 batch commitment,_
_L2 solver output address,_
_Calldata_ ),
执行以下操作,或回滚:
- 验证没有提交与请求 ID(Request ID)对应的其他解决方案(Solution)。
- 验证请求 ID(Request ID)与(L2 input tokens, L1 output token address, L1 output address, Nonce)的承诺匹配。
- 验证是否存在与chained L2 batch commitment 对应的 L2 批次。
- 执行 Calldata。
- 验证请求(Request)的输出条件(Output Conditions)是否已满足。
- 包含请求(Request)的状态根被证明,并通过证明提交到 L1 rollup 合约。
- 请求(Request)的求解器(Solver)向桥合约(Bridge Contract)提交 L1 求解器提款交易(Solver Withdrawal Transaction)。完成以下所有操作,或回滚:
- 验证提款的Merkle 证明(Merkle Proof)是否有效。
- 验证求解器(Solver)地址是否与 L1 求解器提款交易(Solver Withdrawal Transaction)的请求 ID(Request ID)对应的解决方案(Solution)对应的L1 求解器输出地址(L1 Solver Output Address)匹配。
- 将与请求(Request)对应的请求 ID(Request ID)对应的L2 输入代币(L2 Input Tokens)发送给求解器(Solver)。
- 提交请求(Request)的用户向桥合约(Bridge Contract)发送 L1 用户提款交易(User Withdrawal Transaction)。完成以下所有操作,或回滚:
- 验证提款的Merkle 证明(Merkle Proof)是否有效。
- 验证不存在与请求 ID(Request ID)对应的解决方案(Solution)。
- 发送与用户对应的请求(Request)的L2 输入代币(L2 Input Tokens)*。*
比较
横向比较
所提出的解决方案可以被视为一种基于意图的求解器解决方案的变体,类似于 Across 等协议。然而,一个关键的区别在于使求解器能够根据源链的批次来确定他们的解决方案,从而消除源链的重组风险。这是可能的,因为源链是一个 rollup,而目的地是它结算到的 L1——也就是说,源链批次直接提交到目标链。
聚合结算
聚合结算 通过将一个 rollup 的结算建立在其他 rollup 的状态上来实现 L2 之间的同步可组合性。这允许一个 rollup 导入来自另一个 rollup 的消息,基于这些消息执行 L2 区块,并且如果通过在共享结算时间验证其他 rollup 的状态根发现消息无效,则稍后回滚执行。该协议有效地实现了 L2 之间的同步可组合性,因为它利用了 L2 共享一个结算层的事实。但是,聚合解决方案不能解决 L2→L1 的可组合性,因为 L1 不能根据 L2 的已结算状态进行重组。