Bedrock 是 OP Stack 的第一个正式版本,将在降低交易费用、缩短系统延迟、提高节点性能等方面为 Optimism 带来全新可能。
本文首发于 2023 年 2 月 3 日
原文标题:《Bedrock Explainer》
撰文:Optimism 官方
编译:Frank,Foresight News
Bedrock 是 OP Stack 有史以来第一个正式版本的名称,作为一组免费和开源的模块化组件,它旨在为 Optimism 的发展提供动力。
Bedrock 在其前身的基础上进行了改进,主要包含:
Bedrock 使用了经过优化的数据压缩策略,以最大限度地降低数据成本。我们目前正在对这一变化的影响进行基准测试,但我们预计它将大幅降低费用。
Bedrock 还消除了向 L1 提交数据时与 EVM 执行相关的所有 Gas 成本,与之前版本的协议相比,这将额外减少 10% 的费用。
Bedrock 在节点软件中引入了对 L1 重组的支持,这大大减少了用户等待存款入账所需的时间。
该协议的早期版本最多可能需要 10 分钟来确认存款入账,而使用 Bedrock 后,我们预计存款会在 3 分钟内确认入账。
Bedrock 从 OP Stack 中抽象出了证明系统,以便 rollup 可以使用故障证明或有效性证明(例如 zk-SNARK)来证明在 rollup 上输入后的正确执行,这种抽象也将允许使用 Cannon 来证明系统中的故障。
通过在单个 rollup「区块」中执行多笔交易,而不是之前版本中「每个区块一笔交易」的模型,节点软件性能因此得到了显著提高。
这使得 Merkle Trie 更新的成本能在多笔交易中进行分摊,在当前的交易量下,这会使状态数据的增长幅度每年减少大约 15GB。
通过消除之前版本的协议中的技术负债,节点性能也得到进一步提高,这包括不再需要一个单独的「数据传输层」节点来为 L1 编制索引,并更新节点软件以有效地查询来自 L1 的交易数据。
Bedrock 从一开始就被设计成尽可能与以太坊「一致」,先前版本协议中与以太坊的多项偏差已被消除,包括:
Bedrock 还增加了对 EIP-1559、区块链重组和 L1 上存在的其他以太坊功能的支持。
Bedrock 被构建为模块化和可升级的设计,同时可以复用以太坊的现有代码,并尽可能达成 100% 以太坊等效的目标。
通过使用定义良好的接口和版本控制方案,Bedrock 可以轻松地更换 OP 堆栈中的不同组件,并添加新功能。
这就允许其借助一个灵活的架构,适应以太坊生态系统的未来发展,例如:
Bedrock 尽可能地使用现有的以太坊架构和基础设施,这种方法使 OP Stack 能够从以太坊主网使用的经过实战测试的代码库中继承安全性和林迪效应(Lindy effect)优势。
您会在整个设计中找到这样的示例,包括:
Bedrock 旨在最大程度地兼容现有的以太坊开发人员体验,但由于 L1 和 rollup 之间的根本差异,也存在一些例外情况:费用模型的不同、更快的出块时间(2 秒 vs 12 秒)以及包含 L1 存款交易的特殊交易类型。
这些例外情况包括:
Rollups 建立在数据可用性的基础上(通常是像以太坊这样的 L1 区块链),在最常见的配置中,rollup 协议从两个主要信息来源派生出「规范的 L2 链」:
以下是该协议的基本组成部分:
存款是 L1 上的一笔交易,并将包含在 rollup 中。根据定义,存款被保证包含在「规范的 L2 链」中,作为防止审查或控制 L2 的一种手段。
从 L1 传递的任意消息
存款交易是作为存款的一部分进行的 rollup 交易。通过 Bedrock,存款是完全通用的以太坊交易,例如以太坊上的账户或智能合约可以创建「存款」合约。
Bedrock 定义了一个在 L1 上可用的存款合约:它是一个智能合约,L1 帐户和智能合约可以与之交互以写入 L2。L2 上的存款交易是从该存款合约发出的交易中派生出来的,其中包括预期参数,例如 from、to 和 data。
有关详细信息,请参阅存款合约协议规范部分。
在 L1 上购买有担保的 L2 Gas
Bedrock 还明确了 Gas 燃烧机制和存款费用市场,存款交易在 L2 上花费的 Gas 是通过 Gas 燃烧在 L1 上购买的。
这种 Gas 具体是在费用市场上购买的,并且在单个 L1 区块中提供给所有存款交易的 Gas 总量有一个硬性上限,该机制用于防止拒绝服务攻击(DoS),这种攻击可能发生在将交易从 L1 写入 L2 时,因为这些交易在 L2 上耗费大量 Gas,但在 L1 上却很便宜。
提供给存款交易的 Gas 有时被称为「有担保的 Gas」(Guaranteed Gas)。Guaranteed Gas 的独特之处在于它是通过在 L1 上燃烧 Gas 来支付的,因此是不可退还的。
且每单位有担保的 L2 Gas 所要求的必须燃烧的 L1 Gas 总量,取决于 EIP-1559 式收费机制报告的 L2 Gas 价格。此外,用户根据计算费用机制更新所花费的 L1 Gas 数量获得动态 Gas 津贴。
如需更深入的解释,请阅读存款部分的协议规范。
提款是在 L2 上发起并由在 L1 上执行的交易完成的跨层交易。值得注意的是,L2 账户可以使用提款来调用 L1 合约,或将 ETH 从 L2 账户转移到 L1 账户。
提款是通过在 L2 上调用 Message Passer 预部署合约启动的,该合约在其存储中记录了消息的重要属性,然后通过调用 OptimismPortal 合约在 L1 上完成提款,以证明包含此提款消息。
这样,提款和存款就不一样了:提款交易必须使用 L1 上的智能合约来完成,而不是依赖于区块派生出来的信息。
分两步的提款过程
提款证明验证错误是过去几年许多跨链桥黑客攻击事件的根本原因。Bedrock 版本在先前版本的提款过程中引入了一个额外的步骤,旨在为这些类型的错误提供额外的防御设计。
在分两步的提款过程中,每次提款必须在最终退出前 7 天提交与提款对应的 Merkle 证明,这种新的安全机制给了监控工具整整 7 天的时间来查找和检测无效的提款证明。
在此期间如果发现提款证明无效,就可以在资金丢失之前部署智能合约修复,这大大降低了跨链桥妥协的风险。
有关详细信息,请参阅提款协议规范部分。
在 Bedrock 中,为 L1 和 L2 之间的消息传递定义了一种有线格式(即 L2 从 L1 派生区块,L2 将交易写入 L1),这种有线格式的设计目的是将写入 L1 的成本和软件复杂性降到最低。
优化数据压缩
为了优化数据压缩, L2 交易列表(被称为 sequencer batches)被组织到对象组(称为 channel)中,每个 channel 的最大规模能够在可配置参数中定义,最初将设置为 9.5 M,这些 channel 预计将使用压缩功能进行压缩并提交给 L1。
batch 并行提交
为了并行化来自向 L1 提交压缩 channel 数据的定序器消息,channel 被进一步分解为「channel frames」,这些「channel frames」是可以适合单个 L1 交易的压缩 channel 数据块。
假设「channel frames」是相互独立的,并且顺序是已知的,那么由定序器发送到 L1 的以太坊交易可以并行发送,从而最大限度地降低了定序器软件的复杂性,并允许填充 L1 上所有可用的数据空间。
最小化以太坊 Gas
Bedrock 删除了 L1 系统在称为「batcher transactions」的交易中向 L1 提交 channel 数据所使用的所有执行 Gas。之前发生在 L1 智能合约上的所有验证逻辑都被移动到了区块派生逻辑中。相反,「batcher transactions」被发送到以太坊上的单个 EOA 地址,称为「batch inbox address」。
Batches 仍需接受有效性检查(即它们必须正确编码),batche 中的单个交易也是如此(例如签名必须有效),无效 batches 和有效 batches 中的无效单个交易被视为被丢弃并且与系统无关。
注意:以太坊将很快升级包含 EIP-4844 的新版本,它引入了一个单独的数据写入费用市场,并增加了以太坊协议愿意存储的数据量上限,这一变化有望进一步降低与将数据发布到 L1 相关的成本。
如需更深入的解释,请阅读有线格式规范。
区块派生
在 Bedrock 中,该协议的设计旨在保证 L1 上存款的时间与「规范 L2 链」的区块派生有关。这样做是通过定序器、存款和 L1 区块属性将数据写入 L1 的纯函数。
为了实现这一点,该协议定义了保证存款入账、处理 L1 和 L2 时间戳以及处理 channel 中的排序窗口以确保正确排序的策略。
保证存款入账
区块派生协议的目标是这样进行定义的:
每个「L2 区块间隔」过去后,必须有一个 L2 区块,且 L2 区块的时间戳与 L1 的时间戳保持同步(即确保存款包含在逻辑时间顺序中)。
在 Bedrock 中,引入了「sequencing epoch」的概念:它是由一系列 L1 区块派生出来的 L2 区块的范围,每个 epoch 由一个「epoch number」标识,该「epoch number」等于排序窗口中第一个 L1 区块的区块序号。受一些限制,epoch 的大小可以有所不同。
batch 派生 channel 将与「epoch number」相关联的 L1 区块的时间戳视为确定 L2 上交易顺序的锚点。该协议保证一个 epoch 的第一个 L2 区块永远不会落后于所匹配 epoch 的 L1 区块的时间戳。一个 epoch 的第一个区块必须包含 L1 上的存款,以保证存款将被处理。
请注意,在 Bedrock 版本中,L2 上的区块间隔目标配置为 2 秒。
处理 L1 和 L2 时间戳
Bedrock 试图解决将 L2 上时间戳与存入交易中存在的 L1 上时间戳进行协调的问题。
它通过允许一个很短的时间窗口来进行排序,以便在 epoch 之间的 L2 交易上自由应用时间戳来实现这一点。
排序窗口(sequencing window)是 L1 区块的序列,从中可以导出 epoch。一个排序窗口中,其第一个 L1 区块的编号 N 包含 epoch 的「batcher transactions」。
排序窗口包含区块,其中取决于排序窗口的大小:一个固定的 rollup 级别配置参数必须至少为 2,增加它会为定序器提供了更多关于存款的 L2 交易排序机会,降低它为定序器提交「batcher transactions」引入的更严格时间窗口。这是在创造 MEV 机会和增加软件复杂性之间的权衡。
称为「最大定序器漂移」(max sequencer drift)的协议常量控制一个区块在其 epoch 内可以具有的最大时间戳,有了这种漂移,定序器就可以在连接到 L1 出现临时问题时保持活跃。
区块派生管道
「规范的 L2 链」可以从头开始处理,方法是从 L2 创世状态开始,将 L2 链起始设置为第一个 epoch,然后处理所有排序窗口,以便根据以下简化的顺序确定定序器 batches 和存款的正确顺序管道:
在定序器处理一个或多个 L2 区块后,从这些区块中执行交易计算得出的输出将需要用 L1 写入,以实现 L2 到 L1 消息传递的无信任执行,例如提款等。
在 Bedrock 中,输出以树形结构的形式进行哈希处理,从而最大限度地降低了证明输出捕获的任何数据片段的成本。提议者定期向 L1 提交作为整个「规范 L2 链」的 Merkle 根的输出根。
OP Stack 的未来升级应该包括一个故障证明变体的规范,其中包含绑定,以激励提议者提出正确的输出根。
有关完整详细信息,请阅读 L2 输出根提案部分的协议规范。
在 Bedrock 中,OP Stack 通过镜像以太坊执行层和共识层之间的分离,在很大程度上不得不依赖于以太坊指定的技术关注点分离。
所以 Bedrock 以同样的方式引入了执行客户端和 rollup 节点的分离。
执行客户端是定序器和其他类型的节点操作员运行以确定「规范 L2 链」状态的系统。它还执行其他功能,例如处理入站交易和点对点通信,以及处理系统状态以处理针对它的查询。
借助 Bedrock,OP Stack 旨在重用以太坊自己的执行客户端规范及其许多执行操作。在此版本中,Bedrock 展示了对以太坊客户端 go-ethereum 的极其有限的修改,其差异小于 2000 行代码。
存在差异的根本原因有两个:处理存款交易和收取交易费用。
处理存款交易
为了在 rollup 中表示已存入的交易,引入了一种额外的交易类型。执行客户端实现这个新的交易类型是根据 EIP-2718 类型的交易标准。
收取交易费用
Rollups 从根本上说还有两种与交易相关的费用:
一个是定序器费用。使用与以太坊相同的 Gas 表和相同的 EIP-1559 算法计算操作定序器的成本,这些费用用于操作排序器的协议,并根据网络拥堵情况随时波动。
另一个数据可用性费用。数据可用性成本与将批次处理交易写入 L1 相关,这些费用旨在支付定序器向 L1 提交批次交易所需支付的费用。
在 Bedrock 中,费用的数据可用性部分是根据称为 GasPriceOracle 的 rollup 系统智能合约中的信息确定的,该智能合约在区块派生过程中,是根据从每个 epoch 开始时插入的 L1 区块属性中检索到的 Gas 价格信息进行更新。
Bedrock 指定在使用 JSON-RPC 时将这两种费用加到一个字段中。
与以太坊不同,Bedrock 没有 PoS 共识。相反,「规范的 L2 链」的共识是由区块派生定义的。OP Stack 的执行客户端与一个新组件通信,该组件实现了称为 rollup 节点的区块派生,该节点使用与以太坊完全相同的引擎 API 与执行客户端通信。
rollup 节点是一个无状态组件,负责通过读取 L1 上的数据和存款来推导系统的状态。在 Bedrock 中,rollup 节点可用于对来自用户或其他汇总节点的传入交易进行排序,或者通过单独依赖 L1 来验证在 L1 上发布的已确认交易。
rollup 节点的多种用途概述如下:
验证「规范的 L2 链」
运行 rollup 节点的最简单模式是只遵循「规范的 L2 链」。在这种模式下,rollup 节点没有对等节点,严格用于从 L1 读取数据并根据区块派生协议规则对数据进行解释。
这种节点的一个目的是根据协议定义验证由其他节点共享或发布在 L1 上的任何输出根是否正确。此外,打算将输出根提交给 L1 的提议者自己可以使用 optimism_outputAtBlock 生成他们需要的输出根,并返回对应于 L2 输出根的 32 字节哈希值。
为此,节点应该只需要跟随「最终确定」(finalized)的区块头。术语「finalized」指的是以太坊 PoS 共识(即规范的且几乎不可逆的),而「最终确定」的 L2 区块头是仅从「最终确定」的 L1 区块派生的「规范 L2 链」的区块头。
参与 L2 网络
使用 rollup 节点的最常见方法是参与其他 rollup 节点的网络、跟踪 L2 的进程和状态。在这种模式下,rollup 节点同时读取数据和它从 L1 观察到的存款,将其解释为区块,并接受来自其他 rollup 节点网络中的用户和对等方的入站交易。
参与网络的节点可以使用它们正在同步的 L2 的安全和不安全区块头。
在大多数情况下,对于终端用户应用程序,L2 网络中的 rollup 节点将引用不安全的 L2 区块头。
交易排序
使用 rollup 节点的第三种方法是对交易进行排序。在这种模式下,rollup 节点将在不安全的 L2 区块头之上创建新区块。目前,每个 OP Stack 网络只有一个定序器。
定序器还负责将批次交易发布到 L1,以便网络中的其他节点从中同步。
定序器的作用是生产批次交易,为此,排序器可以运行 rollup 节点并具有单独的进程,这些进程通过从它们运行的受信任的 rollup 节点读取来执行批处理。
这保证了 OP Stack 的一个附加组件,称为批次交易处理程序,它从 rollup 节点读取交易数据并将其解释为要写入 L1 的批处理交易,batcher 组件负责读取由定序器运行的 rollup 节点的不安全 L2 区块头,创建 batcher 交易,并将它们写入 L1。
Bedrock 还包括一对用于最常见类型存款的桥接合约,称为标准桥接合约。这些合约封装了存款和提款合约,为 ETH 和 ERC-20 代币的存款和提款提供简单的接口。
这些桥接合约的设计是在跨链桥的一侧为本地代币,另一侧包含一个可以管理铸造和销毁的封装代币,桥接原生代币涉及将原生代币锁定在合约中,然后在跨链桥的另一侧铸造等量的封装代币。
有关详细信息,请参阅标准跨链桥协议规范部分。
虽然在 Cannon 中实现了防故障构建和验证,但故障证明游戏规范和将输出根挑战者集成到 rollup 节点中是后续规范里程碑的一部分。
协议规范定义了 OP Stack 的技术细节,它是协议内部运作的最新事实来源。
要深入了解 Bedrock 与先前版本之间的差异,请参阅「Bedrock 有何不同」。
【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。