cloud-sre
修复 Polygon Erigon 归档节点卡在 87218600 区块的问题
一次 Polygon Erigon 归档节点故障排查:节点在 Chicago hardfork 激活块出现稳定的 gas used mismatch,最终通过从 0xPolygon/erigon v3.5.0 升级到 v3.6.0 修复。
一个 Polygon Mainnet Erigon 归档节点停止在 87218600 区块附近。
归档节点出问题时,很容易先怀疑磁盘、CPU、内存、peer 数量或者快照数据损坏。
但这次问题不在这些地方。
真正的问题是:节点稳定卡在 Polygon Chicago hardfork 的激活块。
修复方式是把运行中的 0xPolygon/erigon 从 v3.5.0 升级到 v3.6.0,然后保留同一个 /data 数据目录重启容器。
背景解释
区块链节点不是简单地“下载一份数据”。 它会自己重新执行区块里的交易,然后检查自己算出来的结果是否和链上区块头里的结果一致。 所以,即使 CPU、内存、磁盘 I/O 都够,节点仍然可能在某一个区块停住。 原因可能不是机器不够强,而是当前客户端软件不认识这个区块开始生效的新规则。
Polygon 和其他 EVM 链一样,会通过 hardfork 修改执行规则。 hardfork 可以理解成“链协议升级”。 到了某一个指定区块后,新规则开始生效:gas 价格规则可能变化,precompile 行为可能变化,Polygon 自己的系统交易处理方式也可能变化。 如果节点软件没有包含这些新规则,它就会算出和链上区块头不一致的结果,然后认为这个区块无效。
这次的 87218600 正是 Polygon Mainnet 的 Chicago hardfork 激活块。
当时节点实际运行的还是旧的 v3.5.0 build。
它能执行 hardfork 前面的区块,但执行到新规则边界时就失败了。
文中专有名词解释
| 术语 | 含义 |
|---|---|
| Archive node / 归档节点 | 保存历史状态的节点,可以查询老区块、receipt、trace、合约历史状态。它比普通裁剪节点需要更多磁盘。 |
| Erigon | 一个以太坊客户端实现。这里用的是 Polygon 维护的 0xPolygon/erigon,用来跟随 Polygon Bor mainnet 规则。 |
| Bor mainnet | Polygon PoS 主网的执行链。Erigon 里用 --chain=bor-mainnet 指定。 |
| Hardfork | 某个指定区块开始生效的协议规则升级。节点必须在到达这个区块前升级到认识新规则的版本。 |
| Chicago hardfork | Polygon 的一次 hardfork,在 0xPolygon/erigon v3.6.0 release notes 中写明 Mainnet 激活块是 87218600。 |
| Gas used | 一个区块执行消耗的 gas 总量。节点会自己计算一次,再和区块头里的值对比。 |
| StateSync | Polygon 特有的状态同步事件。在 Polygon 的 fork 规则附近,它可能影响执行校验。 |
| Heimdall | Polygon 的共识/checkpoint 层。Erigon 可以通过 --bor.heimdall=... 连接远端 Heimdall。 |
| Fork choice | 节点判断哪个链头有效的过程。如果执行层认为某个区块无效,fork choice 就不能推进到这个区块。 |
| Datadir | Erigon 存数据库和快照的目录。这次是 /data。 |
现象
节点反复 unwind 到最后一个有效 tip,然后因为 gas-used mismatch 崩溃:
gas used mismatch block=87218600 header=79467913 execution=62712118
Execution failed block=87218600
err="invalid block, txnIdx=186, gas used by execution: 62712118, in header: 79467913"
pos sync failed: unexpected bad block at finalized waypoint
这里最关键的不是错误文本本身,而是区块号:87218600。
这个区块不是随机区块。
0xPolygon/erigon 的 v3.6.0 release notes 明确写着,Polygon Mainnet 的 Chicago 激活块是 87218600,并要求 validator、RPC provider、node operator 和基础设施伙伴在 hardfork 前升级 [1]。
实际判断方式是:
- 如果节点只是在随机区块失败一次,可能是数据、peer 或资源问题。
- 如果节点反复在同一个 hardfork 激活块失败,先看客户端版本。
- 在确认实际运行版本之前,不要急着删除数据重同步。
排除项
这个节点已经按归档节点规格做过基础优化:
| 层级 | 状态 |
|---|---|
| 存储 | 15TB 级数据盘,并升级到高 IOPS EBS |
| 内存 | 机器已升级到 128GB 级别 |
| P2P | 重启后 peer 可以恢复 |
| Heimdall | 远端 Heimdall scraper 持续前进 |
| 快照数据 | 现有 /data 可读,并且能执行到失败区块前 |
重启后,节点可以重新下载和插入区块:
GoodPeers eth68=3
inserting fetched blocks start=87214502 end=87216293 blocks=1792
这说明它不是随机资源耗尽,也不是网络完全不可用。 它每次都能执行到同一个 hardfork 边界,然后在同一类校验上失败。
GitHub 上的匹配问题
0xPolygon/erigon 仓库里已经有同类 open issue:
- Issue
#143报告了bor-mainnet+v3.5.0-230b11a7上相同的 gas mismatch 模式,并指向 StateSync gas 处理问题 [2]。 - Issue
#133报告了pos sync failed: unexpected bad block at finalized waypoint[3]。 - Issue
#100是更早版本里的同类gas used by execution ... in header ...问题 [4]。
这些 issue 本身不能单独当作修复依据。 但它们足以改变排查方向:这更像 client/fork rule mismatch,而不是磁盘或者 peer 问题。
决定性检查:真正运行的版本
容器实际还在运行旧版本:
Build info git_tag=v3.5.0-dirty git_commit=230b11a713...
当时虽然已经 clone 了 v3.6.0 源码,但还没有 build 新镜像,也没有用新镜像启动容器。
这点很重要:源码目录存在,不代表 Docker 正在跑这个版本。
正确的确认命令是:
docker logs --tail 100 polygon-erigon | grep 'Build info'
docker images | grep polygon-erigon
修复前,镜像列表里只有 local-v3.5.0。
修复步骤
先构建 v3.6.0 镜像:
docker stop polygon-erigon
rm -rf /opt/erigon-v3.6.0
git clone --branch v3.6.0 --single-branch https://github.com/0xPolygon/erigon.git /opt/erigon-v3.6.0
cd /opt/erigon-v3.6.0
DOCKER_BUILDKIT=1 docker build -t polygon-erigon:local-v3.6.0 .
然后删除旧容器,用同一个 /data 启动新容器:
docker update --restart=no polygon-erigon
docker rm -f polygon-erigon
docker network create erigon-net 2>/dev/null || true
核心变化只有运行镜像:
polygon-erigon:local-v3.6.0
归档数据没有删除。 节点继续使用:
--chain=bor-mainnet
--datadir=/data
--prune.mode=archive
--db.size.limit=12TB
--db.pagesize=16KB
--bor.heimdall=https://heimdall-api.polygon.technology
为了避免重新暴露公网 RPC,Docker 端口发布层把 JSON-RPC 和 metrics 绑定在本机:
-p 127.0.0.1:8545:8545
-p 127.0.0.1:6060:6060
这样本机验证仍然可用,但公网不能直接访问 RPC。
验证
新容器启动后,日志显示版本正确:
Build info git_tag=v3.6.0-dirty git_commit=231d67e50b...
Initialised chain configuration ... Chicago: 87218600
刚启动时出现过短暂 peer 警告:
can't use any peers to download blocks
No GoodPeers
这是暂时现象。随后节点找到 peer,并恢复插入区块:
GoodPeers eth68=3
inserting fetched blocks start=87214502 end=87216293 blocks=1792
真正的验证点是越过原来的失败区块:
blk=87218538
blk=87218647
blk=87218727
blk=87218801
这说明 v3.6.0 已经越过 87218600。
以下旧错误没有再次出现:
gas used mismatch block=87218600
unexpected bad block
Execution failed
可复用 Runbook
遇到这类 Polygon Erigon 故障时:
- 先确认失败区块号。
- 确认正在运行的 binary 版本,而不是只看源码目录。
- 把失败区块和 Polygon hardfork 激活块对照。
- 搜索上游 issue 里的精确错误文本。
- 升级到包含对应 hardfork 规则的 release。
- 除非上游明确要求重建数据库,否则保留现有 datadir。
- 验证是否越过精确失败区块。
最有用的两个命令是:
docker logs --tail 100 polygon-erigon | grep 'Build info'
以及:
docker logs --since 15m polygon-erigon | \
grep -E 'gas used mismatch|unexpected bad block|Execution failed|polygon.sync.*crashed'
第一个命令确认真实运行版本。 第二个命令确认旧故障是否还在。
这次故障的教训
这次问题一开始很像性能问题,因为节点此前已经经历过磁盘和实例规格调优。 但性能调优无法修复 hardfork 规则不匹配。
如果一个区块链节点稳定卡在同一个区块,尤其是已知 hardfork 激活块,先看 client 版本。
参考资料
- 0xPolygon/erigon v3.6.0 release notes
- 0xPolygon/erigon issue #143: Block 76879430 - same gas mismatch pattern
- 0xPolygon/erigon issue #133: pos sync failed: unexpected bad block at finalized waypoint
- 0xPolygon/erigon issue #100: gas used by execution mismatch
- Polygon documentation: Erigon archive node