问题描述
使用 Substrate 定制的区块链,所有节点听了一段时间后,链无法工作。
Substrate:polkadot-v0.9.25
共识协议: babe
+ grandpa
系统:macOS Big Sur (11.3)
cargo: cargo 1.63.0-nightly (a4c1cd0eb 2022-05-18)
babe
协议算法,要求每个 epoch(session) 周期内都需要出块,因此当链出现以下情况,导致无法正常出块时,链就会变成砖块(bricked),无法正常工作。
- grandpa 节点少于拜占庭容错要求,无法确认块,地址不能在一个 epoch 内出块
- 所有验证者节点离线
解决方法
出现这种问题时,可以用两种方式解决
Hard Spoon
这种方式的原理是通过 rpc 接口(或者状态数据库)获取到最后正常区块高度下的状态数据,生成一个新的创世块,基于新的创世块运行链。本质上完全是一条新的链,只是继承了状态数据。
如果不在乎区块的历史,只需要保持当前的状态数据,则可以使用此方式。
可参考 fork-off-substrate
我并没有基于这种方式实践成功。
依赖的 polkadot-js 版本过低,升级之后能生成新的创世块文件,用新的创世块运行,还是报错。
Time Warp
Babe 无法正常工作是因为,在最后出块的下一个 epoch 内,没有正常出块,那么如果所有的验证者节点把系统时间调整到最后出块的时间,理论上就可以让链恢复工作了。 正常情况下,区块链和外部时间是一致的,人为的让区块链回到历史时间(最后出块时间),再以加速的时间流逝赶上外部世界的时间。
修改代码
我的链是以 git 路径的方式依赖 substrate crates
需要替换要修改的两个 crates 依赖
修改项目的 根 Cargo.tom
,节选如下,路径设置为自己修改的 crates 目录即可:
[patch."https://github.com/paritytech/substrate"]
sc-consensus-slots = { path = "/path/to/custom/sc-consensus-slots" }
sp-timestamp = { path = "/path/to/custom/sp-timestamp" }
修改的两个 crates ,参考 d0decd9
这里我们修改为两倍速运行。
sc-consensus-slots
sc-consensus-slots/src/slots.rs
/// Returns the duration until the next slot from now.
pub fn time_until_next_slot(slot_duration: Duration) -> Duration {
// HOTFIX: poll the slot 2 times as often since we might be in a time warp.
let slot_duration = slot_duration / 2;
...
}
sp-timestamp
sp-timestamp/src/lib.rs文章来源:https://www.toymoban.com/news/detail-795297.html
WARP_FACTOR
:即时间扭曲的倍数,对应被压缩的是出块时间,合理设置自己的倍数;FORK_TIMESTAMP
:分叉时刻,扭曲的基础时间,这里选择最后确认块的下一块出块时间;REVIVE_TIMESTAMP
: 计划的各验证节点开始运行的时刻,不需要很精确,小于验证节点都拿到修改后的可执行文件,运行的时间即可;文章来源地址https://www.toymoban.com/news/detail-795297.html
impl InherentDataProvider {
/// Create `Self` while using the system time to get the timestamp.
pub fn from_system_time() -> Self {
let timestamp = current_timestamp().as_millis() as u64;
// HOTFIX: mutate timestamp to make it revert back in time and have slots
// happen at 2x their speed from then until we have caught up with the present time.
const REVIVE_TIMESTAMP: u64 = 1642105111666; // 2022-08-25T20:18:31.666Z
const FORK_TIMESTAMP: u64 = 1642006674001; // 2022-08-26T16:57:54.001Z
const WARP_FACTOR: u64 = 2;
let time_since_revival = timestamp.saturating_sub(REVIVE_TIMESTAMP);
let warped_timestamp = FORK_TIMESTAMP + WARP_FACTOR * time_since_revival;
// we want to ensure our timestamp is such that slots run monotonically with blocks
// at 1/2th of the slot_duration from this slot onwards until we catch up to the
// wall-clock time.
let timestamp = timestamp.min(warped_timestamp);
Self {
max_drift: std::time::Duration::from_secs(60).into(),
timestamp: timestamp.into(),
}
}
...
}
执行恢复
- 备份本地数据
如:cp -r node1 node1.bak
- 回滚未确认的块
如:./node revert --chain ./specRaw.json --base-path ./node1
- 使用时间扭曲的程序启动链,指向原来的数据
- 观察日志或管理界面看是否能正常出块
- 等待追赶上外部系统时间
- 使用原程序继续运行
参考链接
- https://github.com/paritytech/substrate/issues/4464
- https://github.com/paritytech/substrate/issues/11673
- https://substrate.stackexchange.com/questions/168/how-to-unbrick-a-substrate-chain-revert/175#175%EF%BC%89
- https://medium.com/polkadot-network/kusamas-first-adventure-2cd4f439a7a4
- https://github.com/maxsam4/fork-off-substrate
到了这里,关于恢复 Substrate 链: unexpected epoch changes的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!