从 geth 迁移至 reth:X Layer 执行客户端的演进
X Layer,作为OKX基于OP Stack构建的EVM兼容Layer 2网络,其技术栈的演进一直备受关注。继从Polygon CDK整体迁移至OP Stack后,团队又完成了一次关键升级:将执行客户端从主流的geth切换到了由Rust编写的reth。今天,我们就来深入聊聊这次升级背后的技术考量,以及为了让它平稳运行在X Layer的生产环境中,工程师们解决了哪些独特的挑战。

一、技术选型背景:为什么是reth?
在OP Stack生态里,geth(即go-ethereum的OP Stack分支)长期是执行客户端的默认选择,其稳定性经过了时间的充分考验。然而,随着链上交易量的持续攀升,geth底层架构的两个局限性开始显现。
首先是存储成本。归档节点的磁盘占用会随着链历史线性增长,在L2这种高吞吐场景下,数据膨胀的速度相当可观,长期运营的成本压力不容忽视。
其次是延迟的稳定性。Go语言的垃圾回收机制在持续高负载下会引入不确定的停顿,同时,底层存储引擎LevelDB或PebbleDB的后台压缩也会周期性地阻塞出块流程。这些因素最终都会转化为用户可感知的交易确认延迟抖动。
而Paradigm用Rust重写的reth,从设计之初就瞄准了这些问题。Rust的所有权模型在编译期就完成了内存管理,彻底消除了运行时垃圾回收的开销;同时,reth采用了MDBX存储引擎和阶段式同步架构,在磁盘利用效率和延迟一致性上都实现了质的飞跃。
除此之外,可扩展性也是一个关键考量。reth提供了一套完整的Node Builder API,通过预定义的框架钩子,开发者可以在不触碰上游核心源码的前提下,将自定义逻辑注入节点生命周期的各个阶段。对于像X Layer这样需要深度定制的L2来说,这套机制意味着更小的代码差异、更低的升级成本,以及更清晰的自定义逻辑边界。
二、整体架构设计:清晰的分层与依赖
为了确保项目的可持续性,xlayer-reth采用了清晰的三层依赖结构:
xlayer-reth ← X Layer customization layer
└── op-reth ← OP Stack execution layer (maintained by Optimism)
└── paradigmxyz/reth ← core Rust Ethereum execution engine
所有X Layer特有的定制代码,都被收敛在最顶层的xlayer-reth中,通过reth的框架钩子接入。中间两层(op-reth和reth核心)则直接引用各自上游仓库的代码,不做任何私有改动。这种结构保证了上游的安全补丁和性能优化能够以极低的成本持续同步,合并时也无需处理核心逻辑的分叉冲突。
在代码组织上,各个功能模块被拆分为独立的crate,例如Chainspec配置、历史RPC路由、Flashblocks、自定义RPC等。每个crate都可以独立演化与测试,主二进制文件只负责将它们组合装配起来,模块化程度非常高。
三、核心功能实现:解决生产环境的特有挑战
1. 非零起始块号支持
X Layer从Polygon zkEVM迁移至OP Stack时,设计上保持了区块号的连续性:旧链在主网第42,810,021号区块终止,新链则从这个高度开始接续出块。这对于区块浏览器、链上索引服务以及所有依赖历史区块号的应用来说至关重要。
然而,reth最初在初始化创世块时,会固定使用区块号0,而忽略genesis JSON文件中的number字段。为此,X Layer团队直接向reth社区提交了Issue #19874,并通过PR #19877将非零创世块号的支持合入了官方仓库,而不是在私有分支中维护一个补丁。这项改进现已原生集成在reth中,其他有类似迁移需求的链可以直接受益。
2. 历史数据透明路由
迁移高度(第42,810,021号区块)之前的历史状态数据,只存储在旧的Polygon zkEVM节点(基于erigon)中,新的reth节点本地并不持有。如果不做处理,针对这部分历史区块的RPC查询将返回空结果。
xlayer-reth的解决方案是在jsonrpsee RPC服务栈中引入了一个Tower中间件路由器。请求到达reth之前,路由器会根据查询的区块号与迁移高度的关系进行判断:低于阈值的请求被透明地转发至旧节点处理,其余的则由本地reth处理。对于调用方来说,整个过程完全无感知,同一个RPC端点就覆盖了完整的区块范围。
对于涉及区块范围的查询(例如eth_getLogs),则需要额外处理。当查询范围横跨迁移前后时,路由器会自动将其拆分为两个并发子请求,分别发往新旧节点,最后将结果合并后统一返回,客户端无需做任何特殊处理。
3. 硬分叉与链参数配置(Chainspec)
xlayer-reth内置了X Layer三个网络(主网链ID 196、测试网、开发网)的完整链参数配置,涵盖了链ID、创世区块配置以及所有硬分叉的激活时间表。
一个关键设计是,从以太坊的Frontier阶段到OP Stack的Isthmus升级,所有历史硬分叉在X Layer创世时就被全部激活了。这意味着节点无需回放漫长的协议演进历史,从第一个区块开始就运行在完整的当前EVM执行环境中,极大地简化了节点的启动逻辑。
此外,X Layer还定义了一个自定义硬分叉“Jovian”,对应OP Stack的第17次网络升级。该升级已于2026年12月在主网激活,与OP主网的升级节奏保持同步。
4. Flashblocks
OP Stack的标准出块间隔大约是2秒。Flashblocks作为一种预确认机制,可以将用户感知到的交易确认延迟降低约10倍。其原理是,sequencer大约每250毫秒就对外发布一个flashblock(即区块执行的中间状态),客户端无需等待完整区块上链,就能获取交易的执行结果。
在节点侧,RPC节点通过WebSocket实时订阅上游sequencer推送的flashblock流,在本地完整执行后,将状态写入内存缓存。随后,22个标准的以太坊RPC方法(包括eth_getBlockByNumber、eth_getBalance、eth_call等)被覆写,当查询latest或pending状态时,直接从该缓存返回预确认结果。
为了提升效率,引擎验证器与Flashblocks处理器共享同一套执行状态缓存。在flashblock阶段已经执行过的交易,在引擎验证完整的payload时可以直接复用结果,避免了重复执行。
5. 自定义RPC扩展
除了标准接口,xlayer-reth还新增了两个X Layer专有的RPC接口,用于增强可观测性和运维监控:
- :用于查询节点的Flashblocks服务是否处于活跃状态,并能正常接收sequencer的数据流。
eth_flashblocksEnabled
- :返回sequencer节点上Flashblocks P2P层各个peer的连接状态,方便运维人员进行网络健康度监控。
eth_flashblocksPeerStatus
这两个接口通过reth的RPC扩展机制注册,与标准接口和谐共存,完全不影响现有方法的行为。
四、工程实践原则:可持续的协作模式
在xlayer-reth的开发过程中,团队始终坚持两条核心原则。
第一,优先通过框架钩子注册,绝不轻易修改上游代码。
第二,将具有通用价值的修改贡献回上游社区。
五、总结
将执行客户端从geth迁移至reth,最直接的驱动力无疑是性能——追求更低的区块构建延迟、更小的存储开销以及更快的节点同步速度。但要在一个像X Layer这样已经承载真实资产和流量的生产环境中兑现这些收益,就必须系统性地解决一系列特有的工程问题:从旧链继承的非零创世区块、分散在两套架构中的历史状态、跨越不同协议的链参数配置、改变交互模式的预确认机制,以及配套的运维监控能力。
X Layer的实践表明,以成熟的开源框架为基础进行扩展,而非简单分叉核心基础设施,并将通用改进积极回馈给社区,这不仅是一条高效的技术演进路径,更是一种在L2生态中可持续的工程协作模式。