ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Solana 共识层 Leader 重复区块惩罚机制(Duplicate Block Slashing)设计解析

2026/9/15 1:56:53 拓冰建站 浏览量
Solana 共识层 Leader 重复区块惩罚机制(Duplicate Block Slashing)设计解析 Solana 共识层 Leader 重复区块惩罚机制Duplicate Block Slashing设计解析【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana本设计文档对应仓库中的 docs/src/proposals/handle-duplicate-block.md描述了 Solana 集群如何对产生**重复区块duplicate block**的 Leader 进行惩罚与处理同一槽位slot被 Leader 产出多个版本会急剧增加集群需要解决的分叉数量。本文以该提案为骨架结合core、gossip、ledger等模块的源码实现完整讲解从重复区块的检测与广播到duplicate_confirmed 判定再到死区块修复repair的整条链路并给出各阈值的精确来源与安全边界分析。问题背景为什么重复区块必须被处理在 Solana 的流水线共识模型中每个 slot 由调度表指定的 Leader 负责打包交易并生成 PoH 条目。正常运行时一个 slot 只对应一个区块一旦 Leader 行为异常作恶或被分区同一个 slot 可能出现多个不同哈希hash的区块版本。从源码结构看这会产生两种后果每个重复版本都构成一条潜在分叉集群的 fork choice 需要解析的候选树规模翻倍甚至是指数增长由于一个 slot 的多个版本无法同时在 BankForks 中共存见下文切换证明一节验证者容易在错误版本上停滞最终导致网络失去活性liveness。因此提案的核心目标是让整个集群对某个 slot 是重复的达成共识并据此从 fork choice 中剔除未确认的重复版本同时对已被多数权益确认duplicate_confirmed的正确版本进行修复与对齐。三个基础原语Primitives提案首先定义了三个基础原语它们分别在 gossip 与 replay 层承担职责原语含义仓库中的对应实现gossip_root节点现在会向集群 gossip 自己当前的root已确认并剪枝的最深槽位参见 gossip/src/duplicate_shred_handler.rs 及 cluster info 的 root 广播逻辑gossip_duplicate_slots节点可以向集群 gossip 最多N个重复槽证明duplicate slot proofscore/src/window_service.rs 检测到重复后经duplicate_slots_sender上报DUPLICATE_THRESHOLD一个重复槽的某个版本要变为可投票votable所需的最低权益投票百分比常量定义于 core/src/replay_stage.rsDUPLICATE_THRESHOLD 的精确取值52%提案正文以DUPLICATE_THRESHOLD 52为例进行分析而这一数值并非随意选取——它由两个已确定的共识常量推导而来。在 core/src/replay_stage.rs 中pub const DUPLICATE_LIVENESS_THRESHOLD: f64 0.1; pub const DUPLICATE_THRESHOLD: f64 1.0 - SWITCH_FORK_THRESHOLD - DUPLICATE_LIVENESS_THRESHOLD;其中SWITCH_FORK_THRESHOLD定义在 core/src/consensus.rspub const SWITCH_FORK_THRESHOLD: f64 0.38;因此DUPLICATE_THRESHOLD 1.0 - 0.38 - 0.1 0.52即52%。这三个常量共同界定了系统的安全与活性窗口详见下文阈值的安全与活性分析一节。在共识逻辑中DUPLICATE_THRESHOLD被HeaviestSubtreeForkChoice的is_slot_duplicate_confirmed方法直接引用core/src/consensus.rspub(crate) fn is_slot_duplicate_confirmed( self, slot: Slot, voted_stakes: VotedStakes, total_stake: Stake, ) - bool { voted_stakes .get(slot) .map(|stake| (*stake as f64 / total_stake as f64) DUPLICATE_THRESHOLD) .unwrap_or(false) }即当投在某一槽某版本上的权益占总权益的比例严格大于 52% 时该槽的该版本被判定为duplicate_confirmed。协议流程从检测到确认再到恢复第一步WindowStage 检测重复槽并广播证明当网络中的某个 slot 出现多个版本的 shred 时WindowStage会先通过Blockstore的重复检测路径发现它。在 core/src/window_service.rs 中处理PossibleDuplicateShred的逻辑区分了多种冲突来源LastIndexConflict同一 shred 索引出现了不同的内容ErasureConflict纠删码erasure coding碎片冲突MerkleRootConflictMerkle 根冲突Exists已存在相同索引的 shred。一旦确认冲突节点调用blockstore.store_duplicate_slot(...)落盘记录证明并通过duplicate_slots_sender将shred_slot发送给 ReplayStagecore/src/window_service.rs。对应地ReplayStage 在 core/src/replay_stage.rs 的process_duplicate_slots中消费这些信号构建DuplicateState并交给check_slot_agrees_with_cluster处理。提案对广播条件做了明确约束WindowStage 检测到重复槽证明P后先检查新的gossip_root如果已有不超过 1/3 的节点对某个S P的槽位完成了 rooting才将该证明推入gossip_duplicate_slots进行广播——这是为了防止网络刚启动或多数节点尚未稳定时过早扩散不可靠信息随后 WindowStage 将重复槽S的信号发送给 ReplayStage这些证明的生命周期当验证者观察到超过 2/3 的节点 gossip 的 rootR S时说明该重复槽已经远离共识前沿证明可以从 gossip 中清除purge。第二步ReplayStage 的 duplicate_confirmed 判定当 ReplayStage 收到来自第一步的重复槽S信号后会持续监控 gossip 与 replay 中对该槽的投票若某个版本哈希H累积的投票达到 DUPLICATE_THRESHOLD即 52%该版本被称为duplicate_confirmed版本在S被duplicate_confirmed之前它被排除在 fork choice 的投票候选集之外从 fork choice 中移除避免验证者继续在未确认的重复版本上投票同时ReplayStage 将PoH 重置到最早的non-duplicate/已确认duplicate槽位的最新祖先reset_poh_recorder见 core/src/replay_stage.rs从而让区块生成能够在最早已知的安全区块上继续而不是基于可能存在重复的分叉头继续出块。在 ReplayStage 内部duplicate_confirmed信息经由DuplicateConfirmedSlotsReceiver汇入process_duplicate_confirmed_slotscore/src/replay_stage.rs。该函数会对每个新确认的槽位构建DuplicateConfirmedState并调用check_slot_agrees_with_cluster实现于 core/src/repair/cluster_slot_state_verifier.rs将本地状态与集群确认的哈希进行比对据此决定是否触发后续的修复流程。ReplayStage 还区分了两种确认类型core/src/replay_stage.rsenum ConfirmationType { SupermajorityVoted, DuplicateConfirmed, }普通槽通过SupermajorityVoted超级多数投票确认而重复槽必须通过DuplicateConfirmed路径确认两者在ConfirmedSlot中分别用new_supermajority_voted与new_duplicate_confirmed_slot构造。DUPLICATE_THRESHOLD 的安全与活性分析提案用DUPLICATE_THRESHOLD 52推导出两个关键边界a) 安全性恶意容限为 4%如果网络中作恶恶意的权益占比小于2 * DUPLICATE_THRESHOLD - 1的百分比那么整个网络至多只能产生一个duplicate_confirmed版本的 slot。代入DUPLICATE_THRESHOLD 522 × 52% - 1 4%即恶意容限为4%直觉理解52% 的确认线意味着要同时让两个不同版本都达到 52% 的投票总共需要 104% 的权益而104% - 100%的差额正是作恶者可以利用的多出的权益因此只要恶意权益低于 4%双确认就不可能发生。b) 活性网络活性容限为 10%网络的活性上限为1 - DUPLICATE_THRESHOLD - SWITCH_THRESHOLD当某重复分叉上只有 DUPLICATE_THRESHOLD52%的权益在投票、且尚未duplicate_confirmed时验证者要切换到另一条分叉至少需要SWITCH_THRESHOLD38%的权益投票即满足切换证明代入DUPLICATE_THRESHOLD 52、SWITCH_THRESHOLD 381 - 0.52 - 0.38 10%即活性容限为10%。也就是说只要诚实验证者的权益不低于 90%系统在最坏情况下仍能维持出块活性。提案给出了一个直观的反例原文档 ASCII 图|-------- 2 (51% voted, then detected this slot was a duplicate and removed this slot from fork choice) 0---| |---------- 6 (39%)在槽 2 上投过票的验证者51%不能再继续在分叉 2 上投票因为该槽已被从 fork choice 中移除此时槽 6 必须累积足够的权益以构成切换证明至少 38%否则网络将停滞。本例中槽 6 有 39%恰好满足切换阈值。切换证明Switching Proofs需要扩展跨重复版本投票Solana 的投票机制要求验证者在切换分叉时提供切换证明switching proof即我在旧分叉上投票的槽位是这条新分叉的严格祖先或与其兼容。提案指出现有切换证明必须扩展以允许包含同一 slot 不同版本的投票哈希通过第一步的重复检测获得。当前实现不支持这一点的根本原因切换证明只能使用 BankForks 中存在的 bank 的投票来构建而同一个 slot 的两个不同版本无法同时存在于 BankForks 中bank 以 slot 为键一个 slot 只有一个 bank。反例场景原文档 ASCII 图|-------- 2 | 0------------- 1 ------ 2 | |---------- 6槽 2 与槽 2 是同一 slot 的两个重复版本各自获得了DUPLICATE_THRESHOLD / 2约 26%的投票因此两者都无法被单独确认槽 6 至多获得1 - DUPLICATE_THRESHOLD / 2 ≈ 74%的投票但仍低于切换阈值所需的证明强度——因为投在 2 上的验证者无法用 2 的投票构造切换证明反之亦然结论为了让投票在 2 或 2 上的验证者能够切换到槽 6 并继续前进必须把另一个重复版本的投票也纳入切换证明。这正是提案要求扩展切换证明数据结构的原因。修复问题The Repair Problem三种触发修复的异常情形即使重复检测与确认机制正常工作集群仍可能因以下情况出现部分验证者无法前进错过 gossip 投票由于网络抖动/延迟部分验证者未能在旧投票被新投票覆盖前观察到 gossip 中的投票导致他们对某个 slotS是否duplicate_confirmed的判断与其他人不一致lockout 导致延迟确认由于投票锁定期lockout重复槽S的任何版本都未能直接达到duplicate_confirmed但它的某个后代槽位可能在 lockout 到期后达到duplicate_confirmed——而根据定义一旦后代被确认S也随之被确认追赶中的节点正在追赶网络状态的验证者没有看到 gossip 中的投票遇到重复区块dup block后无法继续前进。提案给出一个前提假设只要网络最终稳定且至少有一个诚实验证者观察到S被duplicate_confirmed那么只要S属于最重分叉heaviest fork最终所有验证者都会观察到S的某个后代被 duplicate confirmed。核心场景模型提案将待解决的问题抽象为如下链1 - 2 (duplicate) - 3 - 4 (duplicate)并假设三种情况同时成立通过 gossip 重复证明所有人最终都会看到槽 2 和槽 4 的重复证明并一致同意将它们从 fork choice 中移除直到它们被duplicate_confirmed由于 lockout超过DUPLICATE_THRESHOLD52%的权益投在了槽 4 上而非槽 2这意味着至少 52% 的人持有槽 2 与槽 4 的正确版本但剩余1 - DUPLICATE_THRESHOLD48%的人持有的是槽 2 的错误版本于是这些人在重放replay时会把槽 3 标记为死区块dead——即使出错的槽是 2 而不是 3。目标就是把这些验证者重新拉回正确的分叉。修复方案的四步流程提案给出的解决方案在源码中已有对应实现核心校验函数位于 core/src/repair/cluster_slot_state_verifier.rs1. 改变EpochSlots的信号语义从slot 完成改为bank 冻结frozen当观察到超过DUPLICATE_THRESHOLD的验证者冻结了死槽 3 时才发起恢复尝试注意这不代表所有这些验证者冻结的是同一版本的 bank——它只是一个信号提示我们的 bank 版本可能有问题。2. 发起特殊修复请求RepairDuplicateConfirmed请求格式为RepairDuplicateConfirmed(dead_slot, Vec(Slot, Hash))指定一个死槽位以及它的N个最新祖先的(slot, hash)向量该请求让修复方repairer可以校验请求方的祖先哈希是否正确。3. 修复者repairer的响应条件只有当(slot, hash)向量中的任意一个元素同时满足两个条件时修复者才回复正确的哈希该槽位是duplicate_confirmed该哈希与请求方向量中提供的哈希不一致。4. 请求方的自愈流程请求方一旦发现正确哈希与自己冻结的哈希不同就dump丢弃本地该区块以便接受新区块并向网络请求正确哈希对应的区块在 core/src/replay_stage.rs 的process_duplicate_slots_to_repair中可以看到完整实现本地节点会检查duplicate_slots_to_repair中记录的待修复槽将其与bank_forks.bank_hash对比若哈希不一致则 dump 该槽及其后代并通过MAX_REPAIR_RETRY_LOOP_ATTEMPTS定义为 10见 core/src/replay_stage.rs限制重试次数防止无限循环。关于修复者可能说谎的说明提案在最后明确指出修复者可能会欺骗你给你错误版本的区块此时你最终会再次得到一个死区块并重复整个流程。这一信任但验证的循环正是修复机制能够在拜占庭环境中收敛的基础——因为正确的哈希最终会由超过 52% 的权益投票所锚定错误版本的修复请求会在后续轮次中再次被纠正。源码中的落地实现速览以下是本文涉及的核心实现位置供深入阅读功能文件与关键位置DUPLICATE_THRESHOLD等阈值定义core/src/replay_stage.rs、core/src/consensus.rsduplicate_confirmed 判定core/src/consensus.rs重复槽信号处理core/src/replay_stage.rsduplicate_confirmed 信号处理core/src/replay_stage.rs重复槽检测与上报core/src/window_service.rs集群状态一致性校验含死槽修复判定core/src/repair/cluster_slot_state_verifier.rs重复槽修复执行core/src/replay_stage.rsancestor hashes 服务中的正确哈希下发core/src/repair/ancestor_hashes_service.rsgossip 层重复 shred 处理gossip/src/duplicate_shred_handler.rsBlockstore 中重复槽证明的存取ledger/src/blockstore.rs、ledger/src/blockstore_db.rs集群级集成测试local-cluster/tests/local_cluster.rs总结Solana 的重复区块惩罚机制是一套检测 → 广播 → 排除 → 确认 → 修复的完整闭环检测层WindowStage Blockstore识别同一 slot 的多个 shred 版本并落盘证明共识层ReplayStage HeaviestSubtreeForkChoice通过DUPLICATE_THRESHOLD 52%将重复槽的某个版本确定为duplicate_confirmed在此之前将重复槽移出投票候选集并把 PoH 重置到最早安全祖先阈值设计同时保证了 4% 的恶意容限与 10% 的活性容限修复层通过扩展切换证明、改变 EpochSlots 信号语义以及RepairDuplicateConfirmed特殊修复请求把持有错误重复版本的验证者拉回正确分叉并容忍修复者说谎后再次修复的循环。这套设计的关键在于所有安全与活性边界都由DUPLICATE_THRESHOLD52%、SWITCH_THRESHOLD38%与活性容限10%三个常量共同锁定它们不是经验参数而是从数学上可推导的共识保证这一严谨性正是 Solana 在面对恶意 Leader 产出重复区块时仍能维持最终一致性的基础。【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考