本文档记录了一个关于 Lighthouse-Reth 集成想法的小实验,目标是使用单一二进制文件同时运行共识层 (CL) 和执行层 (EL),从而简化以太坊节点的运行。实验结果表明,这种集成在用户体验、资源使用和可观察性方面具有潜在优势,但也存在维护、版本协调和编译时间等方面的挑战。
截图:“Fullhouse”正在同步。
这篇文章记录了一个关于 Lighthouse-Reth 集成想法的小实验,这个想法很久以前就出现了,但一直没有真正进展。随着 gas 限制和 blob 数量的增加,共识层(CL)和执行层(EL)之间流动的数据也越来越多,而当前 HTTP + JSON 路径可能会变得越来越昂贵。Engine API 中的 SSZ 支持会有所帮助,但仍然有一些 UX 方面的优势值得探索 - 所以我用 Claude 做了个短期的、有时间限制的实验,看看单二进制设置会是什么样子。
这个想法是拥有一个运行共识层和执行层的单一二进制文件 - 我们称之为 "Fullhouse" - 运行以太坊节点可以像这样简单:
fullhouse --checkpoint-sync-url https://checkpoint.sigp.io
┌─────────────┐ HTTP (JSON-RPC) ┌──────────────┐
│ Lighthouse │ ←─────────────────→ │ Reth |
│ (Consensus) │ │ (Execution) │
└─────────────┘ └──────────────┘
┌─────────────────────────────────────────────────┐
│ Single Lighthouse-Reth Binary │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ Lighthouse │ ←──────→ │ Reth │ │
│ │ (Consensus) │ Direct │ (Execution) │ │
│ └─────────────┘ Calls └──────────────┘ │
└─────────────────────────────────────────────────┘
CL + EL 在单个二进制文件中的潜在优势:
本节将介绍原型的技术细节。如果你不关心内部原理,请随时跳到下一节。
这是一个有时间限制的 POC,因为我知道这个兔子洞可能非常深。Claude 帮助生成了大部分初始代码,目标是在最短的时间内让一些东西能够工作,所以代码非常粗糙,但重点是看到一些东西能够工作,并了解可行性和所需的工作量。
我想以最小的更改来解决这个问题,因为我认为如果这种集成增加太多的维护或依赖开销,就不值得了。所以我考虑只在 Lighthouse 的 Engine 结构体中,将 HttpJsonRpc 替换为 RethEngineApi。如果我实现了所有的接口,这应该就可以工作了。
pub struct Engine {
pub api: RethEngineApi,
pub json_api: HttpJsonRpc,
payload_id_cache: Mutex<LruCache<PayloadIdCacheKey, PayloadId>>,
state: RwLock<State>,
latest_forkchoice_state: RwLock<Option<ForkchoiceState>>,
executor: TaskExecutor,
}
感谢 Reth 的模块化设计,我能够相当快地在 Lighthouse 中运行 Reth,并获得了 Reth 的 ConsensusEngineHandle 的Handle,它公开了 CL 驱动 EL 的函数(forkchoice_updated 和 new_payload)。
我意识到 ConsensusEngineHandle 不够用,因为它没有公开完整的 engine 方法集 - 然后我找到了 EngineApi 结构体,它是要使用的正确接口,并公开了所有的 engine 方法。启动 Reth 并获取 EngineApi 的Handle相当简单:
debug!("Launching Reth node");
let engine_api = EngineApiExt::new(
BasicEngineApiBuilder::<EthereumEngineValidatorBuilder>::default(),
move |api| {
info!("Reth node started, extracting engine API handle");
let _ = handle_tx.send(Ok(api));
debug!("Extracted engine api handle");
},
);
let tasks = TaskManager::current();
let node_builder = NodeBuilder::new(node_config)
.with_database(db)
.with_launch_context(tasks.executor())
.with_types::<EthereumNode>()
.with_components(EthereumNode::components())
.with_add_ons(EthereumAddOns::default().with_engine_api(engine_api));
match node_builder.launch().await {
就这样 -- 我们有了 CL 向 EL 发送新块和分叉选择更新的工作同步!🥳

该分支可以在这里找到:https://github.com/jimmygchen/lighthouse/tree/lighthouse-reth
forkchoice_updatednew_payloadget_payloadget_blobs_v1get_blobs_v2network 和 datadir。该实现没有优化,但如果没有一些测试结果,这篇文章就不完整了!
我使用单独的 Lighthouse 和 Reth 二进制文件与 Hoodi 测试网上的 Fullhouse 进行了比较。这是我使用的命令和下面的指标。
fullhouse bn --network hoodi \
--checkpoint-sync-url https://hoodi.beaconstate.info \
--http --metrics --telemetry-collector-url http://localhost:4317
fetch_and_process_engine_blobs这个 span 指标显示了从 EL 获取 blob(engine_getBlobs 方法)并将它们转换为数据列的 p95 时间。Fullhouse 在这里表现明显更好。
| Lighthouse + Reth | Fullhouse |
|---|---|
![]() |
![]() |
execution_layer_request_times这个指标显示了调用 EL 的 p95 持续时间。
forkchoice_updated:两者在这里表现相似。new_payload:Fullhouse 在这里较慢。| Lighthouse + Reth | Fullhouse |
|---|---|
![]() |
![]() |
beacon_block_processing_seconds这个指标衡量了区块处理的 p95 总运行时间,包括执行区块,可能包括将区块和数据列导入数据库的时间。正如预期的那样,Fullhouse 在这里表现更差,因为大部分时间都花在了 EL payload 验证(上面的 new_payload 方法)上。
| Lighthouse + Reth | Fullhouse |
|---|---|
![]() |
![]() |
更快的 get_blobs_v2 响应时间似乎没有太大帮助,因为关键路径是 payload 执行。请参见下面的区块处理跟踪示例:

我之前提到了潜在的可观察性优势,这个想法是,如果我们检测 EL 中的函数,我们将能够看到所有关键步骤,轻松识别出哪个步骤花费的时间最长,以及所采用的代码路径,所有这些都在一个简单的跟踪中!
beacon_block_delay_attestable_slot_start这个指标显示了区块变得可证明的时间和 slot 开始之间的时间间隔。结果非常相似,对于两者来说,平均区块可证明时间约为 2 秒。
| Lighthouse + Reth | Fullhouse |
|---|---|
![]() |
![]() |
produce_block_v3我运行了一个连接到信标节点的 blockdreamer 实例,以在每个 slot 中触发区块生产。Fullhouse 在这里表现明显更差。不幸的是,我在关闭测试实例之前没有捕获正确的屏幕截图,但在测试期间,p95 持续时间似乎慢了大约 2 倍。
总的来说,Fullhouse POC:
new_payload 和 get_payload)上表现更差。我怀疑这与任务调度或争用相同的 Tokio 和/或 Rayon 线程池有关,当在单个进程和 Tokio 运行时中运行 Lighthouse 和 Reth 时。这很可能是一个可以解决的问题。get_blobs_v2)表现更好。能够用一个命令运行整个以太坊节点非常简洁:
fullhouse bn --checkpoint-sync-url https://checkpoint.sigp.io
注意:bn 子命令仅在此处,因为 Lighthouse 期望它 - 在组合二进制文件中删除它很简单。
性能结果不是主要重点,但看到它工作令人非常满意!我在短时间内从这个 POC 中学到了很多东西,这是一个有趣的实验。代码是快速拼凑起来的(大部分由 Claude 完成),非常粗糙,但这就是目标 - 只是一个快速实验,以了解挑战和可行性。我确实遇到了一些挑战,并设法解决了一些,我认为这个想法是可行的,并且可能值得付出努力,并具有前面提到的好处:更好的 UX、更低的资源开销、改进的性能和可观察性。
在潜在的缺点方面:
我考虑过在一个单独的 repo 中实现这个,但是让依赖项正确非常耗时(Claude 也为此苦苦挣扎),所以我选择了更快的替代方案,将其嵌入到 Lighthouse 中,以便用于本次实验。这并不意味着它是一个正确或首选的方法 - 引入像 Reth 这样的大型代码库作为依赖项并不理想 - 但对于本次有时间限制的 POC 来说,对 Claude 和我来说都更容易。这也会减慢编译时间,因此在真正的实现中,将其隔离在 feature gate 后面非常重要。
很想听听你的想法,欢迎随时提出任何反馈或想法。感谢阅读!
感谢 Reth 团队 - 他们的模块化设计使本次实验比其他情况更顺利,感谢 Lighthouse 团队审查这篇文章并提供早期反馈。
- 原文链接: blog.sigmaprime.io/fullh...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!