容错性与最终确定性
BFT 保证
Savitri 共识采用 BFT(拜占庭容错)投票协议,法定人数阈值为 2f+1(67%)。
容错能力
| 总节点数(n) | 最大拜占庭节点数(f) | 所需法定人数 |
|---|---|---|
| 3 | 0 | 2 |
| 5 | 1 | 4 |
| 7 | 2 | 5 |
| 10 | 3 | 7 |
| 15 | 4 | 11 |
公式:f = floor((n-1) / 3),法定人数 = 2f + 1
安全性
只有当产生包含 2f+1 个验证者签名的区块接受证书(BAC)时,区块才会被最终确定。这保证了:
- 同一高度不会存在冲突区块被最终确定
- 一旦颁发 BAC,不会发生回滚(确定性最终性,非概率性)
- 拜占庭节点无法在没有法定人数参与的情况下伪造证书
活性
只要有 n - f 个诚实节点在线,网络便可持续生产区块:
- 提议者轮换:每生产 50 个区块后,提议者退位,防止单点故障
- 选举看门狗:若 60 秒内无区块进展,重新触发选举
- 渐进式法定人数放宽:连续选举失败 3 次以上后,最低候选人数降至 2
DAG 结构
BlockHeader 支持多父节点 DAG 结构,以实现并发区块生产:
pub struct BlockHeader {
pub parent_hash: Vec<u8>, // 主父节点(向后兼容)
pub parent_hashes: Vec<Vec<u8>>, // DAG 的额外父节点
// ...
}
冲突检测
ConflictDetector 负责识别:
- 双花冲突:同一 UTXO 在多个分支中被引用
- 状态冲突:冲突的状态转换
- 分叉检测:不同提议者在同一高度生产多个区块
冲突解决
检测到冲突时:
- 选择具有更高累计 PoU 分数的分支
- 回滚冲突交易
- 重新应用失败分支中的非冲突交易
感知群组的共识
群组构成
主节点将轻节点组织为群组:
- 按 PoU 分数对轻节点排序
- 分配至大小为
group_size的群组(开发环境 3,生产环境 7) - 通过 gossipsub 发布群组分配
- 基于纪元(而非系统时钟)的确定性群组 ID
群组内选举
在每个群组内:
- 成员共享 PoU 分数
- PoU 分数最高者成为提议者(peer_id 作为平局打破者)
- 至少需要
(total+1)/2个候选人 - 失败 3 次以上后:放宽至最少 2 个候选人
区块接受
- 群组提议者生产区块
- 群组成员验证并签名
- 产生包含 2f+1 个签名的 BAC
- 主节点验证 BAC 并最终确定
恢复机制
选举恢复
| 机制 | 触发条件 | 操作 |
|---|---|---|
| 看门狗定时器 | 60 秒无区块 | 重新触发 PoU + 选举 |
| 渐进式放宽 | 失败 3 次以上 | 降低法定人数至 2 |
| 提议者轮换 | 50 个区块 | 退位,重新选举 |
| 立即重新选举 | 群组重新初始化 | 启动 PoU + 选举任务 |
节点恢复
| 机制 | 触发条件 | 操作 |
|---|---|---|
| Gossipsub 保活 | 60 秒间隔 | 发布 PoU 分数 |
| 网格恢复看门狗 | 60 秒无网格 | 重新拨号群组节点 |
| SlowPeer 断开 | 队列已满 | 断开并重连 |
| Nonce 重置 | 本地 > 存储 + 200 | 重置为存储 nonce |
群组恢复
群组重新初始化时:
- 保留持续成员的 PoU 分数
- 新成员以默认分数起步
- 重置
intra_group_started标志 - 立即启动 PoU + 选举(不等待定时器)
罚没(生产环境)
| 违规行为 | 处罚 | 冷却期 |
|---|---|---|
| 双重投票 | 50% 质押 | 无 |
| 下线 | 10% 质押 | 逐步 |
| 无效提案 | 25% 质押 | 无 |
罚没在开发网络禁用,在主网启用(config/production.toml)。
共识配置
| 参数 | 开发环境 | 生产环境 |
|---|---|---|
| 超时时间 | 15 秒 | 10 秒 |
| 最大轮数 | 5 | 10 |
| 法定人数阈值 | 0.67 | 0.67 |
| BFT 优化 | 禁用 | 启用 |
| 罚没 | 禁用 | 启用 |
| 出块时间 | 2 秒 | 5 秒 |
| 群组大小 | 3 | 7 |