到高度必须停:Cosmos SDK 的 upgrade 模块怎么协调换软件 图 1
到高度必须停:Cosmos SDK 的 upgrade 模块怎么协调换软件 · 图 1

一条 Cosmos 系链做破坏性升级时,链上不会出现”某一刻规则悄悄变了”的状态:老节点会在预定高度集体停住,等新二进制到位后再续上。这套”到点必停”的协调机制来自 Cosmos SDK 的 x/upgrade 模块。官方文档对它的概括很克制:模块不负责治理怎么决定升级,只负责把升级安全地协调到位。本文按官方文档拆解它提供了哪些零件、停机是怎么触发的、以及节点运营者视角的自查点。

Plan:一份写进链上的停机计划

x/upgrade 的核心数据结构叫 Plan,只有三个字段:名字、区块高度、附加信息。名字是升级的唯一标识,在同一条链上不能复用;高度是预定执行升级的区块高度;信息字段通常放元数据,比如运营者可以自动跟进的 git commit。Plan 通常经治理流程产生——提案带上 MsgSoftwareUpgrade 消息,投票通过后被持久化并排期;官方文档特别说明,如果想给”升级后才发现这是个坏主意”留反应时间,预定高度应当设在提案启动后至少两倍投票期加抵押期再加一个安全余量的位置;原提案还在投票期间也可以提 MsgCancelUpgrade 把它撤掉。治理决定什么与怎么决定,模块不作规定。

到高度后老节点为什么必须停

机制藏在一个 BeginBlocker 钩子里:链每开始一个新区块,模块检查当前高度是否命中某个 Plan。命中时老二进制先看本地有没有注册与 Plan 同名的升级处理器——老代码自然没有,于是判定自己运行的是过时软件,把 Plan 写进磁盘后优雅 panic 退出,不再处理任何共识消息。文档强调这一步的用意:宕机前落盘的 Plan 信息能保证新二进制之后无论重启多少次,存储迁移只在该高度、按正确的升级名执行一遍;如果同一高度排了多个升级,名字是防止重复执行的钥匙。换言之,全网”统一停车”不靠约定,靠的是每个老节点在同一个高度各自确认自己该退休。

一摞平价基板在预定高度停住、最上层错位发光的升级协调抽象示意

状态怎么跟着版本搬家

除了停机,x/upgrade 还管存储迁移。应用开发者通过 StoreLoader 机制声明哪个高度上要重命名或删除哪些存储库,官方示例里就是把旧模块的数据键换成新键。配合可选的 cosmovisor 一类进程管理工具,运营者可以提前把新二进制放在约定位置,节点停在旧高度后由工具在重启时自动切换到 Plan 指定的版本,减少人工踩点的时差风险。升级完成、新高度出块后,旧二进制如果误启动会再次停在同一个位置——这套设计让”谁没升级”在观测上非常显眼:停住的节点会安静地等在同一个高度。

观察者视角的三个核对点

对不跑验证者的普通用户与数据读者,这套机制给了三个可核对的信号。第一,升级高度是链上可读的公开信息,任何节点都能查询当前排期的 Plan,不必依赖公告截图;第二,链在预定高度暂停出块是预期行为而不是事故,真正的异常信号是过了高度还无法在运营者全部就位后恢复;第三,升级前后同名标识不能复用,历史数据里出现过的升级名意味着那次升级已经执行过,索引器可以据此对齐前后状态版本。把升级看成”全网在同一个高度交换了对规则的理解”,比把它看成一次发版更接近这套机制的本意。

本文只作机制解释,各链的具体升级流程与参数以对应官方文档为准;不构成任何投资建议。