开始之前:关键安全规则
绝对不要做的事
- 绝不要同时运行两个使用相同
priv_validator_key.json的节点。 这会导致双重签名,罚没比例为 0%,但会被永久性墓碑处理(tombstoned)。该验证者将永远无法重新加入活跃集。 - 绝不要在验证者节点上使用
unsafe-reset-all,除非你完全理解其后果。它会清除共识状态,并可能产生导致 tombstoned 的冲突投票。 - 绝不要在节点仍在运行时备份
priv_validator_state.json。 该文件在共识过程中持续变化。请先完全停止节点,然后再复制它。 - 绝不要在快照恢复后未还原备份的
priv_validator_state.json就启动节点。 快照不包含此文件。在没有它的情况下启动会将你的签名状态重置为高度 0,从而导致双重签名。 - 绝不要在未事先测试的情况下对归档节点或大型历史节点执行回滚。 在大型数据库上回滚可能耗时数小时,甚至彻底损坏节点,尤其是在使用 PebbleDB 时。
- 始终在完全停止节点后备份
priv_validator_state.json,并且要在任何恢复操作(回滚、快照恢复、二进制升级)之前进行。 - 始终在回滚或快照恢复之后、启动节点之前,将
priv_validator_state.json还原到其原始位置。 - 始终在启动节点前验证
priv_validator_state.json完好无损。 检查其中的高度、轮次和步骤值是否合理。 - 始终为协调升级设置
--halt-height。 跳过 halt-height 标志的节点需要更困难的恢复路径。 - 始终在升级后启动前使用
injectived version验证二进制版本。 - 始终在链稳定后撤销临时的共识覆盖设置(例如
--unsafe-consensus-timeout-precommit-delta=1ms)。
常见问题与解决方案
节点卡在升级高度
症状:- 节点日志显示链在预期的升级高度处停机
latest_block_height无法超过停机高度继续前进
- 停止节点
- 备份
~/.injectived/data/priv_validator_state.json - 安装新的二进制文件
- 验证:
injectived version - 启动节点
升级后区块头哈希不匹配
症状:- 停止节点
- 备份
priv_validator_state.json - 回滚一个区块:
- 还原
priv_validator_state.json - 使用新的二进制文件启动节点
冲突投票警告
症状:priv_validator_state.json 被重置或损坏,因此它在已经签名过的轮次上重新签名,产生了冲突投票。
常见诱因:
- 在验证者上运行了
unsafe-reset-all - 还原了旧的或错误的
priv_validator_state.json - 在节点仍在运行时备份了
priv_validator_state.json(过期副本) - 回滚命令重置了验证者状态
- 如果你有正确的
priv_validator_state.json备份(在完全停止节点后获取):还原它并重启。 - 如果你没有正确的备份:立即停止节点,等待区块生产恢复后再重启。 使用错误状态启动会有双重签名的风险。
- 检查签名信息:
验证者被 tombstoned(双重签名)
症状:- 使用相同的签名密钥运行了两个节点实例
- 还原了旧的
priv_validator_state.json,导致在先前已签名的高度重新签名 - 回滚重置了签名状态,随后节点签署了冲突的区块
- 通过治理提案或升级处理器(upgrade handler)明确为受影响的验证者解除 tombstoned 状态
- 使用新的运营者密钥创建新的验证者(会失去现有的委托)
- 使用 TMKMS 或 Horcrux 以硬件方式强制执行单一签名者语义
- 始终在停止节点后备份
priv_validator_state.json - 绝不同时运行两个使用相同签名密钥的节点
从快照恢复
适用场景: 当回滚失败、耗时过长,或节点状态损坏到无法修复(AppHash 不匹配)时使用。 流程:- 完全停止节点
- 备份
~/.injectived/data/priv_validator_state.json - 如果
~/.injectived/config/priv_validator_key.json尚未在其他地方备份,也进行备份 - 从可信提供商下载快照:
- Injective 团队也可能在安全升级之前或期间分享应急快照
- Polkachu Injective 快照(通常为 goleveldb)
- 社区验证者可能会在事故期间分享应急快照
- 删除旧数据:
- 将快照解压到
~/.injectived/data/ - 将你备份的
priv_validator_state.json还原到~/.injectived/data/ - 验证
priv_validator_state.json存在且正确 - 启动节点
为什么必须保留你自己的 priv_validator_state.json
快照包含区块链数据(区块、应用状态),但绝不会包含你的验证者的签名状态。签名状态记录了你的验证者最后一次签名的高度、轮次和步骤。如果你丢失了它或用空白的替换了它,你的节点将不知道自己已经签名过什么,可能会签署冲突的区块 —— 导致双重签名和永久性的 tombstoned。
下面是一个实际示例,说明为什么签名状态必须始终领先于(或等于)快照高度。
场景设定: 一次协调链升级,halt-height 设置在区块 125。
priv_validator_state.json 内容为:
--halt-height 标志阻止的是区块 126 被提交(最终确认),但 CometBFT 共识引擎仍会进入高度 126 并开始其轮次 —— 提议、预投票和预提交 —— 直到应用层拒绝处理该区块、节点关闭为止。较高的轮次编号(42)反映了随着验证者陆续停机、共识无法达成时网络不断推进轮次的过程。这是正常行为:即使区块 126 从未被最终确认,你的验证者也在高度 126 处投出了真实的投票。
这意味着你的验证者已经在高度 126、轮次 42 处投过票。而可用的快照来自高度 115。
如果你恢复快照时没有还原自己的签名状态,会发生什么:
快照要么不带 priv_validator_state.json,要么带一个空白的(高度 0)。如果你以这种方式启动节点:
- 你的节点从高度 115 加载区块链数据
- 它通过重放区块 116-125 追赶到高度 126
- 在高度 126 处,它进入共识并从轮次 0 开始签名
- 但网络中已经存在你在该高度轮次 42 的投票
- 你的节点在高度 126 签署了不同的区块提案(因为它使用新二进制文件重放,可能产生不同的结果)
- 网络检测到来自你的验证者密钥的两个冲突签名
- 你的验证者被判定双重签名并被 tombstoned。永久性的。
- 停止节点
- 备份你的
priv_validator_state.json(它显示高度 126、轮次 42) - 删除你的数据目录
- 解压快照(来自高度 115 的区块链数据)
- 将你备份的
priv_validator_state.json复制回数据目录 - 启动节点
- 它从高度 115 加载区块链数据
- 它通过重放区块 116-125 追赶到高度 126
- 在高度 126 处,它读取签名状态:「我已经签名到了轮次 42」
- 它跳过轮次 0-42,等到轮次 43 之后才签署任何新内容
- 没有冲突投票。你的验证者是安全的。
数据库后端兼容性: 快照与后端相关。goleveldb 快照无法在配置为 pebbledb 的节点上使用,反之亦然。检查你的配置:
回滚挂起或失败
症状:injectived rollback 一直运行但始终无法完成,或产生错误。
原因:
- PebbleDB 后端: 使用 pebble 进行回滚存在已知问题,可能会无限期挂起
- 数据库非常大: 回滚时间随数据库大小增加
- WAL 损坏(预写日志,Write-Ahead Log)
- 对于 PebbleDB 节点:改为从快照恢复
- 对于大型节点:如果回滚超过 30 分钟仍未完成,请中止并使用快照
- 考虑切换到 goleveldb。PebbleDB 更节省空间,但在回滚方面测试较少
追赶期间的轮次回退错误
症状:AppHash 不匹配
症状:- 尝试回滚:
injectived rollback - 如果回滚失败或错误仍然存在:从快照恢复
- 恢复后始终还原
priv_validator_state.json
升级后哨兵节点卡住
症状: 验证者节点已升级并正在签名,但哨兵节点仍停留在旧的区块高度。 原因: 哨兵节点同样需要新的二进制文件才能处理超过升级高度的区块。 解决方案: 将哨兵节点升级到与验证者相同的二进制版本。哨兵节点没有priv_validator_state.json(它们不签名),但它们确实需要正确的二进制文件。
升级后链停滞(投票权不足)
症状:- 链在升级高度之后不再产生新区块
- 共识轮次编号持续攀升(轮次 50、100、200+)
- 节点日志记录预投票和预提交,但没有区块被提交
latest_block_height一直停留在停机高度
- 链在升级高度处停机(例如:区块 125)
- 已完成升级的验证者开始在高度 126 参与共识
- 每一轮,网络都尝试最终确认区块 126,但由于投票的投票权不足 2/3 而失败
- 轮次编号递增,循环重复:轮次 1、2、3、…… 50、…… 100+
- 这种情况持续到足够多的验证者完成升级并上线为止
- 如果你已经完成升级: 无需任何操作。你的节点正在参与共识,一旦达到法定人数,就会自动完成下一个区块的最终确认。你会在日志中看到轮次回退错误,因为你的节点正在追赶各轮次。这是预期现象(参见追赶期间的轮次回退错误)。
- 如果你尚未升级: 你正是缺失投票权的一部分。请尽快完成升级,以帮助链恢复运行。
- 监控进度: 检查有多少验证者在线并参与:
- 在验证者频道中协调: 在重大升级期间,验证者通常会在经过验证的验证者频道中协调,跟踪升级进度和投票权百分比。
这个窗口期持续得越久,轮次编号攀升得越高。较晚上线的验证者需要追赶所有这些轮次,这正是
--unsafe-consensus-timeout-precommit-delta=1ms 标志存在的原因,但请记住在链稳定后移除它。因停机被监禁(非 tombstoned)
症状:- 验证者显示为非活跃或被监禁(jailed)
- 签名信息中显示
tombstoned: false - 错过区块的计数很高
- 确保节点正在运行并已完全追赶到最新区块
- 解禁:
- 验证验证者已重新活跃:
协调升级检查清单
升级高度之前
- 从治理提案或链团队处确认目标 halt-height
- 下载并验证新的二进制文件(检查 sha256 校验和)
- 在节点配置或 CLI 标志中设置
--halt-height=<target_height> - 如果使用 Cosmovisor:将新二进制文件放入正确的升级目录
- 备份
priv_validator_state.json - 确定快照提供商,以备需要回滚时使用
- 监控链向停机高度的推进
在升级高度时
- 确认节点已在预期高度停机
- 完全停止节点(验证进程已终止)
- 备份
priv_validator_state.json - 安装新的二进制文件
- 验证版本:
升级之后
- 使用新的二进制文件启动节点
- 监控日志中的共识参与情况
- 确认预投票正确(不是每一轮都投 nil)
- 在浏览器上监控投票权(Mintscan)
- 如果看到
wrong Block.Header.LastResultsHash:停止节点,回滚 1 个区块,还原priv_validator_state.json,然后重启 - 如果使用了轮次追赶覆盖设置(
--unsafe-consensus-timeout-precommit-delta):在稳定后移除它们 - 验证验证者正在签名:
监控参考
共识与同步状态
验证者健康状况
节点配置
剪枝节点与归档节点对比
其他资源
快照资源
Injective 官方提供的快照
Injective 在两个区域维护剪枝主网快照。请检查状态端点以获取最新的可用高度和下载 URL:
要下载最新的快照,请检查状态端点以获取当前的文件名:
社区快照提供商
- Polkachu Injective 快照(通常为 goleveldb,剪枝版)
- HighStakes Injective 快照
- 社区验证者可能会在事故期间在验证者频道中分享应急快照
