本文介绍了FOCIL (Fork Choice Enforced Inclusion List) 在 Reth 客户端的实现,旨在增强以太坊的抗审查能力,确保区块构建者无法审查交易,详细描述了FOCIL的核心逻辑,包括包含列表的构建、验证以及使用包含列表构建区块的过程,并讨论了实施过程中遇到的挑战和未来的发展方向。
审查制度在去中心化系统中是一个真正的威胁。当区块构建者获得更大的影响力时,他们的决定会破坏以太坊的可信中立性。FOCIL (强制包含列表的分叉选择) 是一项至关重要的功能,旨在通过确保区块构建者不再审查交易,使以太坊真正具有抗审查性。通过此实现,Reth 加入了实现弹性和无需许可的未来竞赛。
Jihoon: 感谢他对我所有问题的宝贵帮助,协助调试,并推动我在 FOCIL 突围会议上发言。
Mario 和 Josh: 感谢他们运行 EPF 队列并组织前往 Devconnect 的旅行。
Kayta: 感谢她在定义和实现 FOCIL 指标方面与我合作。
当我加入 以太坊协议奖学金 (EPF) 时,我感到非常迷茫。我有为以太坊做出贡献的动力和兴趣,但我患有选择疲劳症,经常从一个酷炫的想法跳到下一个。我知道我需要一个具体的项目来做,而不是另一个“好的首要问题”。
作为一名无需许可的成员,我在酷炫的预先分配的项目方面处于“垫底”的位置,所以我决定定义自己的项目。这个自主项目最终确定为 Reth 的 FOCIL,回顾过去,这是我在整个队列中做出的最佳决定。这个 EIP 对于入门来说难度适中:它涉及向 Reth 添加一个完整的功能,代码涉及代码库的不同部分。它的范围也很完美,让我可以将一半的时间用于测试,与其他的客户端团队及其实现合作,编写规范测试,以及全面获得以太坊核心开发经验,而不仅仅是某个孤立的项目。
现在,为什么特别选择 FOCIL?我进入以太坊是因为高中时期对偏执、网络安全和隐私的意识增强。从我了解以太坊如何运作的那一刻起,我就清楚地认识到:快速的区块链固然好,但如果没有无需许可性、去中心化和可信中立性,区块链就毫无用处。在一个每个人都沉迷于区块链性能的世界里,我想做一些即使不能提高每秒交易量,也能对确保以太坊作为全球结算层,在一个日益分裂和充斥着旧世界衰落力量的世界中的地位,产生更大影响的事情。
当开始这次新的冒险时,我知道前任 EF 员工 Jacob Kaufmann 已经做了一些工作。我假设这将给我一个很好的起点。不幸的是,我通过借用代码来节省时间的计划变成了花费数周的时间来解释他来自陈旧分支的代码并将其移植到我的新分支上。这被证明是浪费时间的残酷行为,回顾过去,我应该尝试自己实现一些基本功能。
随着我的工作继续,以下是 Reth 准备好支持 FOCIL 所需的主要变更:
包含列表的构建没有被严格指定,这是有充分理由的。这允许客户或用户决定他们想要如何构建列表,从而增加了包含的交易的多样性。
对于我的 Reth 实现,我选择了一个简单的 概念验证,这样我就可以专注于该功能的其余部分;这些包含列表的算法和研究本身就可以是一个完整的项目。当前的实现采用来自内存池顶部的最佳交易(最高费用)并将它们添加到 8 KB 的包含列表中。
当共识层(CL)调用engine_newPayloadV5时,会发生此验证。客户端检查inclusionListTransactions字段中_可以_包含的所有交易是否实际存在于传递给执行层(EL)的有效负载中。用外行的话来说:如果区块有额外的空间用于交易,并且根据正常的有效性规则,该交易是有效的,那么它必须被包括在内。如果任何可能被包括的交易没有被包括,客户端将返回INCLUSION_LIST_UNSATISFIED,这将触发对该区块的重组。
此逻辑与包含列表验证非常相似,但它适用于本地区块构建。在这里,我们像往常一样构建一个区块。在区块结束时,使用从 engine_forkchoiceUpdatedV4 传递的 inclusionListTransactions,客户端循环遍历 IL(包含列表)交易并添加所有有效的、未包含的交易。此循环继续进行,直到区块已满或所有交易都被标记为无效。n 次方的循环的原因是某些交易可能在包含其他依赖交易_之后_变为有效。
我还实现了所需的包含列表 (IL) 原语并更新了必要的 Engine API 结构体,特别是 Reth Alloy 库中的新的 engine_forkchoiceUpdatedV4 和 engine_newPayloadV5 结构体。这些更改对于 FOCIL 功能至关重要。
由于 FOCIL 正在被 CFI'd(考虑包含),它存在于一个奇怪的灰色地带,导致 Reth 团队没有太关注我的工作。这意味着我的所有更改都保留在一个分支中,而不是逐步将 PR 合并到主分支中。
虽然没有预料到,但 EPF 校友 Kayta 主动联系我,帮助定义和实现 FOCIL 执行层的指标。CL 指标已经在一些客户端上定义和实现。
在几个星期的时间里,Kayta、Jihoon 和我提出了并讨论了几个不同的指标。在实施它们并权衡某些指标的成本和可行性之后,我们达成共识,削减了其中的大部分指标,因为它们与 CL 中捕获的数据重叠或无关。
EL 的最终指标是:
execution_inclusion_list_block_validation_transactions_included_totalexecution_inclusion_list_block_validation_transactions_excluded_totalexecution_inclusion_list_block_validation_time_secondsexecution_inclusion_list_block_building_transactions_included_totalexecution_inclusion_list_block_building_transactions_excluded_total实施这些还需要向 Reth 添加一些必要的指标功能,例如 Prometheus buckets,以准确处理验证时间指标。
总的来说,我没有实施指标的经验,但通过反复试验,我能够掌握它并生成有用的指标,这些指标有助于发现小的错误并提高我对 FOCIL 实现的可见性。
在整个实施过程中,令人惊讶的是,是小事情具有挑战性。不是关于 IL 应该如何通过 Reth 传递、如何优化验证或如何添加全新的 Engine API 这些更大的问题。阻碍和漫长的夜晚来自于一些小问题,这些问题消耗了我大量时间。
这些问题出现的原因是对整个 Reth 代码库不熟悉以及在如此大型的 Rust 项目上的工作经验不足。Reth 的高于平均水平的可组合性也导致许多看似简单的修复花费比预期更长的时间。我认为这既不是坏事也不是好事,而是一种设计选择;当然,Geth 更容易阅读和理解,但在某些情况下,Reth 更模块化且性能更好。然而,这种设计选择确实导致了更陡峭的学习曲线,即使有 Rust 编译器帮助捕获错误。
尽管我在此过程中面临挑战,但我认为我找不到更好的项目来向我介绍为以太坊客户端实施大型功能。我所做的工作使我能够完美地继续为以太坊做出贡献。
我原以为这会是锦上添花的事情,但测试最终花费了我在项目上花费的近一半的时间。
在 Reth 内部进行了一些基本的单元测试之后,我开始在本地开发网上启动我的实现,看看我花了半个夏天编写的代码是否真的有效。当然,我第一次启动它时,Reth 崩溃了,但这标志着我项目的第二章。
虽然 Kurtosis 和开发网的文档非常丰富,但找到特定于 FOCIL 的镜像、实现的当前状态和其他细节使得入门成为了一件苦差事。在我准备好一切之后,我就可以分析正在运行的客户端了。
当 Reth 首次尝试在 FOCIL 开发网上启动时,我遇到了一系列我没有考虑过的问题:我错过了功能分叉实现,忘记打开新的 Engine API 方法以及其他小错误。有些比其他的更容易追踪,但至少可以说,这次经历让我感到惭愧。
在稳定 Reth 之后,我注意到的下一件事是大多数 CL 客户端中的一个错误。在 CL 配对中运行 Reth 时,链会顺利进行,尽管在 engine_newPayloadV5 出现时没有传递任何 IL。从那里,我在 Lodestar 和 Lighthouse 上测试了我的假设,并在需要时打开了 PR。在与客户端团队成员进行了简短的讨论后,我们能够修复此问题。此修复程序还无意中解决了 Reth 和 Geth 之间之前未完全理解的某些错过区块行为。
每个人最喜欢实施和测试 EIP 的部分当然是 规范更改。对我来说幸运的是,这只是一些小事,并将 FOCIL engine_updateWithInclusionListV1 逻辑移动到 engine_forkchoiceUpdatedV4 中。这是该项目中最令人沮丧的部分,因为大多数客户端团队都忙于 Fusaka,无法更新他们的 FOCIL 实现。例如,在撰写本文时,只有 Lodestar、Reth 和 Geth 重新基于 Fusaka。
一旦 Lodestar 赶上来,我就能够继续测试,这有助于解决两个客户端中的更改。不幸的是,Geth 在重新基于 Fusaka 时遇到了一个未知的错误,这限制了我测试 EL 互操作性的能力。
总的来说,Kurtosis Devnets 负责了绝大多数的测试,这些测试导致发现了隐藏的错误,并帮助我更好地理解其他客户端实现以及 API 在实践中的使用方式。
虽然仍然重要,但 EL 范围的规范测试非常有限,因为 FOCIL 是一个 跨层更改,非常强调 CL。尽管如此,EL 规范测试对于在没有本地开发网的所有开销和等待的情况下快速测试客户端仍然非常有用。
我专注于测试 Reth FOCIL 实现的三个主要方面:包含列表构造、区块包含列表验证和区块构建包含列表验证。到目前为止,由于 EELS 的限制,我只实现了包含列表验证的测试,但我正在积极地进行其余的测试。
区块包含列表验证测试:
| 函数名称 | 目标 | 设置 | 预期 |
|---|---|---|---|
| 区块包含列表验证 | |||
test_focil_block_validation_accepts_empty_inclusion_list |
验证 EL 是否正确验证了具有零 inclusionListTransactions 的有效负载。 |
EL 接收一个有效负载和零 inclusionListTransactions。 |
有效负载必须被视为有效。 |
test_focil_block_validation_accepts_full_inclusion_list |
验证 EL 是否正确验证了一个有效负载,该有效负载正确包含了最大数量的 inclusionListTransactions。 |
EL 接收一个有效负载和 inclusionListTransactions,其大小等于 \(MAX\_BYTES\_PER\_INCLUSION\_LIST\) \* 包含列表委员会大小限制。 |
有效负载必须包含所有交易。 |
test_focil_block_validation_ignores_invalid_transactions_in_inclusion_list |
确保 EL 验证一个区块,该区块正确地省略了在 inclusionListTransactions 中找到的无效交易。 |
EL 接收一个有效负载和 inclusionListTransactions,其中包含无效交易(本质上无效,错误的 nonce,发送者无法承受 gas,错误的编码,eip-4844 交易等)。 |
这些无效交易必须_不_包含在区块主体中。 |
test_focil_block_validation_returns_error_when_inclusion_list_tx_is_omitted |
验证区块验证是否返回 INCLUSION_LIST_UNSATISFIED,如果它省略了一个交易,该交易对于当前状态有效且由该槽位的相应 inclusionListTransactions 引用。 |
inclusionListTransactions 引用了一个对于当前状态有效的交易,但区块主体中省略了该交易。 |
EL 必须返回一个 INCLUSION_LIST_UNSATISFIED 错误。 |
test_focil_block_validation_succeeds_with_interdependent_inclusion_list_transactions |
验证 EL 是否正确处理包含这样的交易的 inclusionListTransactions,其中后面的交易依赖于前面的交易。 |
包含列表为 \([tx\_A, tx\_B]\)。 \(tx\_A\) 为一个新账户提供资金。 \(tx\_B\) 是从该新账户发送的交易。 | 两个交易都必须包含。 |
test_focil_block_validation_accepts_two_independent_inclusion_list_transactions |
验证 EL 是否正确验证包含来自 inclusionListTransactions 的具有两个交易的有效负载。 |
EL 接收一个有效负载和 inclusionListTransactions,该有效负载和 inclusionListTransactions 包含两个独立的有效交易。 |
有效负载必须包含两个交易;顺序无关紧要。 |
这些测试的想法是验证客户端是否正确处理 IL 并将它们应用于来自 engine_newPayloadV4 的有效负载。这些测试涵盖了我们期望客户端处理的基本功能,以及一些稍微复杂的情况。到目前为止,这些测试仅在 Reth 实现上进行了测试并通过。
这些测试的下一步是弄清楚如何测试实现的其余部分(如果可能),并添加更复杂的验证情况,例如 AA(账户抽象)交易,在这些交易中,测试不太容易提出。
随着 Reth 的大部分工作完成,我的主要重点是看到 FOCIL 进入主网硬分叉。这包括添加更多测试,帮助其他客户端团队加快速度,并保持我的 Reth 实现的最新状态,以便在需要时可以发布。
目前,FOCIL 仍然是 CFI'd,但似乎越来越有可能 所有核心开发人员 (ACD) 将投票推动 FOCIL 进入 H*(下一个硬分叉)。虽然这很不幸,因为它将看到我的 FOCIL 更改合并的时间推迟了八个月,但它也给了开发人员更多的时间,尤其是在 CL 方面,来确定我们希望如何在 EPBS(基于执行证明的分片) 之上实施 FOCIL,这将需要对 IL 委员会和传播的时间安排进行一些小的规范更改。
我非常感谢有机会参加以太坊协议奖学金。在过去的四个月里,我能够通过我专注于以太坊协议的第一个完整项目,在以太坊中产生比我以前认为的更大的影响。进来的时候,我认为它会像我过去做过的任何其他实习生式的项目一样。我认为它主要是一个编码项目,最终我会发布我的 Reth 实现。但是,到完成 EPF 时,我不仅开发了一个完全可行的 FOCIL EL 实现,而且我还积极参与了 FOCIL 讨论,广泛地帮助进行测试、指标、开发网,并协助其他人测试他们的实现。还有很多东西要学习,但我终于有信心自己可以解决协议开发所需的许多问题。
在这个项目中最大的警醒是测试我的自主性。我已经习惯于能够在任何时候向专家提问,这在为营利性公司工作时是有道理的,因为你的产出通常是最重要的目标。在我担任 EPF 研究员期间,情况恰恰相反:我几乎所有的事情都必须自己解决,即使花费几天的时间才能克服简单的障碍。虽然我的产出和速度肯定受到了损害,但我很感谢我现在拥有的关于我的主题事项的知识深度。我认为更慎重地平衡这些极端情况将更有利于我未来在该行业的发展。现在我在该行业中有一定的信誉,这也应该更容易获得。
我对 EPF 的唯一遗憾是我有更多的时间做出贡献。更具体地说,我希望我能更多地关注我的每周报告,因为简洁地写作和表达你的想法在开源开发中尤为重要,在开源开发中,团队是真正全球化的并且跨越了几个不同的群体。话虽如此,我将努力在未来写更多关于我的工作的文章,无论是博客文章、X 帖子还是 Discord 讨论。
总的来说,我在 EPF 的时光让我感到谦卑,并磨练了我手中的工具,以创造未来的以太坊。我计划继续我目前的工作,并找到新的项目来挑战我在这个领域。
- 原文链接: hackmd.io/@pellekrab/SyI...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!