Polkadot Provisioner 子系统深度解析:区块作者的候选人与共识数据装配中枢 区块链【免费下载链接】polkadotPolkadot Node Implementation项目地址https://gitcode.com/gh_mirrors/po/polkadot点击查看免费下载导读在 Polkadot 中中继链区块的产出权由 BABE 共识决定但区块里装什么——具体选择哪些可支持的平行链候选块backable candidates、可用性位域availability bitfields与争议陈述dispute statements——则由 Provisioner 子系统决定。本文基于 implementers-guide 中 provisioner.md 的协议设计结合仓库内node/core/provisioner的真实 Rust 实现完整讲解四类可供给数据、按中继父块relay parent组织的工作循环、候选选择与位域选择算法以及异步支持prospective parachains与遗留legacy两套候选选择模式的取舍。读完本文你将掌握 Provisioner 的完整数据流并能在源码中定位每一步决策对应的实现与测试。Provisioner 的定位BABE 之下的内容决策者中继链区块的出块权由 BABE 控制这超出了 Overseer 与所有子系统的职责范围。但出块者block author最终需要从候选块、可用性位域、不当行为报告和争议陈述中挑选出一组数据装配成一个中继链区块。Provisioner 子系统的职责就是向所有潜在的区块作者提供他们装配区块所需的全部数据。用一句话概括BABE 决定谁来写区块Provisioner 决定区块里写什么。这在源码中有直接对应——lib.rs 的文件级注释写道The provisioner is responsible for assembling a relay chain block from a set of available parachain candidates of its choiceProvisioner 负责从其选择的一组可用平行链候选中装配出一个中继链区块。四类可供给数据Provisionable Data设计中有一类统称为可供给数据Provisionable Data的消息它们虽然形态各异但有一个共同属性最终都应被包含进某个中继链区块。在协议层面对应的消息枚举见 overseer-protocol.md 中的ProvisionerMessage::ProvisionableData(ProvisionableData)。Backed Candidates已背书候选块区块作者对每条平行链最多只能选择 0 或 1 个已背书平行链候选块唯一约束是每个可背书候选块必须带有合适的中继父块。候选块的选择权必须属于区块作者本人而 Provisioner 子系统正是区块作者在实践中行使这一选择权的途径。从源码看ProvisionerSubsystem收到ProvisionableData::BackedCandidate后会将候选块记录到对应中继父块的PerRelayParent.backed_candidates向量中并记录 candidate hash 与 para_id见 lib.rs。Signed Bitfields签名可用性位域签名可用性位域 是某个特定验证人对哪些候选块已可用的证明性声明。它们只会在新鲜的叶子块fresh leaves上被提供——这是为了防止链回滚reversion时产生争议。源码 lib.rs 对此有明确注释Only include bitfields on fresh leaves. On chain reversions, we want to make sure that there will be at least one block, which cannot get disputed, so the chain can make progress.即在链回滚时要保证至少有一个区块不会被争议从而让链能够继续前进。对应实现中LeafStatus::Stale的叶子块位域选择结果为空向量。Misbehavior Reports不当行为报告不当行为报告是验证人或验证人群体不当行为的自包含证明。以双重投票double-voting为例报告包含由同一密钥签名的、主张不同结果的两张选票极易验证。具体而言不当行为报告会成为 inherents导致违规者被罚没slashed。设计文档特别提醒一个博弈论要点没有任何机制强制区块作者包含他不喜欢的报告例如该报告会导致作者自己被罚没。链对此的防御是设置相对较长的罚没期slash period使得在罚没期过期前大概率能遇到一个诚实的作者。当前源码的处理则更为保守。note_provisionable_data对ProvisionableData::MisbehaviorReport直接忽略见 lib.rs注释与文档一致We choose not to punish these forms of misbehavior for the time being. Risks from misbehavior are sufficiently mitigated at the protocol level via reputation changes.文档列出了四种不当行为Misbehavior::ValidityDoubleVote—— 有效性双重投票Misbehavior::MultipleCandidates—— 多个候选块Misbehavior::UnauthorizedStatement—— 未授权声明Misbehavior::DoubleSign—— 重复签名但它们当前均不被惩罚风险已在协议层通过信誉reputation变化得到充分缓解未来可能值得投入时间实施惩罚性措施。Dispute Inherent争议 inherent争议 inherent 与不当行为报告类似也是对验证人或验证人群体不当行为的证明。区别在于它不自包含——解决争议需要多个验证人的协调行动。典型场景是批准检查者approval checker发现一组验证人不当批准了一个无效平行链区块解决该争议需要全体验证人重新验证该区块以便对少数派进行罚没。争议解决的完整机制较为复杂详见 runtime/disputes.md。当前实现同样对ProvisionableData::Dispute暂不处理lib.rs源码注释给出的理由是候选块在被包含前触发争议无法保证得出结论未结束的争议不可接受且未被置为可用的候选块不构成安全风险——因为它们不可能被包含、批准或最终确认。协议流程按中继父块组织的生命周期子系统需要维护一组当前存活的区块作者供给迭代Block Authorship Provisioning iteration句柄。在源码中这个句柄集合就是run_iteration中的per_relay_parent: HashMapHash, PerRelayParentlib.rs其中PerRelayParent结构体lib.rs保存了每个叶子块对应的leaf: ActivatedLeaf—— 激活叶子块信息backed_candidates—— 已背书候选块记录遗留选择模式使用prospective_parachains_mode—— 异步支持模式开关signed_bitfields—— 已收集的签名位域is_inherent_ready与awaiting_inherent—— 供给数据就绪状态与等待中的请求者队列。处理 Overseer SignalOverseerSignal::ActiveLeavesUpdate触发两类动作源码 lib.rs对每个**激活activated**的头部以给定中继父块 spawn 一个供给迭代并与该迭代建立双向通道——实现上即创建PerRelayParent并插入 HashMap同时注册一个PRE_PROPOSE_TIMEOUT2000 毫秒的延迟延迟到期后该叶子块的 inherent 数据才标记为 ready对每个**停用deactivated**的头部终止对应供给迭代——实现上即从 HashMap 移除该 relay parent。OverseerSignal::Conclude向所有迭代转发Conclude等待一小段时间让它们汇合join然后硬退出hard-exit。源码中收到Conclude即返回Ok(())结束事件循环。处理ProvisionerMessage将消息转发给合适的供给迭代若没有合适的活跃迭代则丢弃。源码 handle_communication 处理两种消息RequestInherentData(relay_parent, return_sender)若对应状态已就绪is_inherent_ready立即异步装配并发回 inherent 数据否则把请求者放入awaiting_inherent队列等延迟到期后再统一处理。整个装配与发送过程受SEND_INHERENT_DATA_TIMEOUT500 毫秒超时保护ProvisionableData(relay_parent, data)将数据记入对应 relay parent 的状态位域、候选块不当行为报告与争议则按前述策略忽略。每次供给迭代的输入来源每次迭代的输入是ProvisionerMessage各类数据来自不同的子系统已背书候选块 ← Candidate Backing 子系统签名位域 ← Bitfield Distribution 子系统争议 ← Disputes 子系统dispute-coordinator不当行为报告 ← 目前来自 Candidate Backing 子系统包含上述四种不当行为。迭代初始化时没有任何输出。区块作者通过请求区块作者应使用的 inherent 数据即包含平行链执行信息的区块 inherent 所需数据来主动拉取供给。区块生产RequestInherentData的完整装配流水线当验证人被 BABE 选中成为区块作者时它需要决定哪些已背书候选块、哪些可用性位域应该被装进区块。Provisioner 是最适合做这个选择的子系统。外部子系统通过发送ProvisionerMessage::RequestInherentData来触发响应是一个ParaInherentData。装配流水线在 send_inherent_data 中按序执行四个步骤从运行时 API 获取可用性核心状态列表request_availability_cores选择争议陈述select_disputes选择可用性位域select_availability_bitfields仅 Fresh 叶子选择候选块select_candidates按模式分派到遗留或异步支持实现。最终产物是ProvisionerInherentData { bitfields, backed_candidates, disputes }。日志会输出各部分的计数见 lib.rs对应的观测指标定义在 metrics.rs。设计文档强调两条硬规则每条中继链平行链最多背书 1 个候选块在之前的候选块被宣布可用或过期之前不能背书新的候选块。例外若位域表明候选块 AB 的前驱应被宣布可用则 B 可以在同一中继区块中被背书。位域选择Bitfield Selection目标很简单最大化可用性maximize availability。但并非简单地全收必须满足两条约束每个验证人最多一个位域每个 1 位必须对应一个被占用的核心occupied core。约束之外的半任意选择策略都是可接受的。为最大化可用性冲突时采用选择 1 位数量最多的位域的启发式。源码 select_availability_bitfields 的实现细节用BTreeMapValidatorIndex, SignedAvailabilityBitfield以验证人为键去重天然满足每验证人一个的约束位域长度必须与核心数一致否则丢弃若位域的某位对应到未占用核心Free/Scheduled整份位域无效并被丢弃同一验证人提交多份位域时保留count_ones()更大的那份。对应的单元测试见 tests.rsnot_more_than_one_per_validator验证去重each_corresponds_to_an_occupied_core验证1 位必须对应占用核心的约束。争议陈述选择Dispute Statement Selection这是区块作者为活跃争议补充投票、或在运行时状态中发起新争议的环节。运行时区块作者逻辑在处理 inherent 数据与实际生成 inherent 调用之间有一个额外步骤假定它负责过滤与链上状态无关的争议。背书投票backing votes总是保留在争议陈述集中以确保惩罚最大数量的不当背书者。选择流程为向 dispute-coordinator 发出DisputeCoordinatorMessage::RecentDisputes消息并等待响应——这是我们在最近会话中知晓的所有争议集合。当前实现采用优先级化选择prioritized selection见 disputes/prioritized_selection/mod.rs其核心思路从运行时 API 拉取链上已知争议RuntimeApiRequest::Disputes用于过滤出只有链上不知道的新鲜投票将最近争议按活跃/非活跃 × 链上已知/未知 × 链上已/未结论划分为 6 个分区PartitionedDisputes按优先级从高到低依次处理其中链上已结论且本地非活跃的争议被直接丢弃投票以VOTES_SELECTION_BATCH_SIZE生产环境 1100测试 11为批大小分批向 dispute-coordinator 拉取总票数上限MAX_DISPUTE_VOTES_FORWARDED_TO_RUNTIME生产环境 200,000测试 200防止向运行时洪泛数据按(SessionIndex, CandidateHash)排序运行时要求的顺序产出一个MultiDisputeStatementSet背书类投票BackingValid/BackingSeconded无条件保留。源码注释点明了一个关键不变式若某争议的投票无法全部塞入限额则整组投票都不加入——这保证背书投票总能完整进入供给的投票集。判定位域可用性Determining Bitfield Availability被占用的核心带有一个CoreAvailability位域OccupiedCore.availability同时我们有一份SignedAvailabilityBitfield列表。需要据此判定索引为某值的核心是否已变为可用。关键洞察是CoreAvailability与SignedAvailabilityBitfield是转置transverse关系。把位域列表想象成多行每位的取值是各自的列那么某个核心索引的CoreAvailability就是该索引处所有列位的垂直切片。计算流程拷贝OccupiedCore.availability作为起点遍历SignedAvailabilityBitfield列表取位域的validator_index更新可用性概念上等价于位向量运算availability[validator_index] | bitfield[core_idx]可用性采用 2/3 阈值判定3 * availability.count_ones() 2 * availability.len()。源码 bitfields_indicate_availability 完整实现了这一逻辑包括越界索引的警告处理文档对其的评述in principle, this function might return aResultbool, Error在源码注释中也有呼应。候选选择Prospective Parachains 模式PerRelayParent状态中有一个重要开关ProspectiveParachainsMode它决定了候选选择方法ProspectiveParachainsMode::Disabled—— Provisioner 使用自己的内部遗留候选选择逻辑ProspectiveParachainsMode::Enabled—— Provisioner 请求 prospective parachains 子系统 提供已选候选块。源码在 select_candidates 中按该模式分派Enabled走request_backable_candidatesDisabled走select_candidate_hashes_from_tracked。模式本身在激活叶子时通过prospective_parachains_mode(sender, leaf.hash)从运行时查询lib.rs。Enabled模式下选出的候选块能受益于异步支持asynchronous backing带来的更长区块生产时间。因此所有 Polkadot 协议网络最终都会采用 prospective parachains 候选选择届时遗留候选选择将作为过时实现被移除。Prospective Parachains 候选选择候选选择的目标是确定哪些核心空闲并尽可能为每个空闲核心挑选一个合适的候选块。在此模式下Provisioner 负责前者判定空闲核心prospective parachains 负责后者挑选候选。选择流程源码 request_backable_candidates从运行时 API 获取核心状态列表遍历每个核心状态CoreState::Free核心未被调度无需提供候选CoreState::Scheduled核心空闲且已调度将接受某个para_id的已背书块。Provisioner 以期望的中继父块、核心的调度para_id和空 required path向 prospective parachains 请求一个可背书候选CoreState::Occupied核心被一个等待可用性的平行链候选块占用。除非核心将在本区块被腾空否则无需提供新候选。腾空发生在两种情况位域表明当前占用者已可用若Some(scheduled_core) occupied_core.next_up_on_available核心将被腾空并需要供给新候选。此时以核心的调度para_id和含一个条目的 required path请求候选——该条目对应此前占用此核心、已可用但尚未被看到包含进中继链区块的平行链候选块可在此基础上继续构建若occupied_core.next_up_on_available为None被腾空的核心未调度无需供给候选若occupied_core.time_out_at block_number超时若Some(scheduled_core) occupied_core.next_up_on_timeout与CoreState::Scheduled完全相同的请求方式否则无需供给。整个过程的最终产物是一个CandidateHash向量按核心索引排序。Required Path必需路径Required Path是ProspectiveParachainsMessage::GetBackableCandidate的参数由 Provisioner 在候选选择中发送。空 required path表示请求的候选块应是指定para_id在给定中继父块下最近被包含的平行链区块的直接子块含一个或多个条目的 required path促使 prospective parachains 沿其 fragment tree 为给定para_id与中继父块向前步进直到抵达期望的平行链区块再选择该区块的一个直接子块返回给 Provisioner。构成 required path 的平行链区块无需曾被看到包含进中继链区块。因此基于 required path 供给可背书候选块的能力实质上将背书backing与包含inclusion解耦——这正是异步支持的核心收益。源码中 required path 的构造清晰可见CoreState::Occupied且位域表明可用时以vec![occupied_core.candidate_hash]作为 required path源码还附有一个 TODO 注释lib.rs指出该逻辑对按需平行链on-demand parachains不适用因为其强依赖会话内核心固定分配给特定平行链这一假设。遗留候选选择Legacy Candidate Selection遗留候选选择在 Provisioner 内部进行因此 Provisioner 需要按中继父块维护一份最新的所有 backed_candidates 记录以供挑选即PerRelayParent.backed_candidates。可用性判定流程源码 select_candidate_hashes_from_tracked获取核心状态列表遍历每个核心状态CoreState::Scheduled可做出OccupiedCoreAssumption::Free假设CoreState::Occupied可能做出假设——若位域表明可用且有调度的next_up_on_available做出OccupiedCoreAssumption::Included若位域不表明可用、有调度的next_up_on_time_out且occupied_core.time_out_at block_number_under_production做出OccupiedCoreAssumption::TimedOut若未做出任何假设跳到下一个核心计算核心的validation_data_hash给定已知ParaId与OccupiedCoreAssumption从运行时获取PersistedValidationData为核心寻找合适候选块两条约束backed_candidate.candidate.descriptor.para_id scheduled_core.para_idcandidate.candidate.descriptor.validation_data_hash computed_validation_data_hash多个候选满足约束时选择是任意的但每个核心最多只能选一个候选块。最终产物同样是按核心索引排序的CandidateHash向量。根据已选哈希获取完整BackedCandidate两种候选选择流程最终都得到CandidateHash向量。它们通过CandidateBackingMessage::GetBackedCandidates传给 backing 子系统select_candidates。响应是按核心索引排序的BackedCandidate向量可直接供给区块作者使用。实现中有两处关键收尾逻辑顺序校验源码用双指针逐个比对所选哈希与返回候选块的哈希确保GetBackedCandidates保持按核心索引的升序BackedCandidateOrderingProblem错误对应校验失败验证代码升级限制候选选择与获取流程最多只允许选择一个升级运行时验证代码new_validation_code的候选块——实现上对含新验证代码的候选做去重保留lib.rs。观测指标MetricsProvisioner 的指标定义在 metrics.rs与上述流程一一对应inherent_data_requestsinherent 数据请求成功/失败计数request_inherent_data_duration、provisionable_data_duration两类关键处理的耗时直方图inherent_data_response_bitfields返回给请求者的位域数量inherent_data_dispute_statement_sets/inherent_data_dispute_statements供给给运行时处理的争议陈述集/陈述数量只在节点出块时更新各节点数值不同partitioned_disputes按分区统计从 dispute-coordinator 收到的争议fetched_onchain_disputes从运行时拉取的链上争议数量。这些指标对排障出块内容异常如位域未包含、争议未推进非常有用。术语表GlossaryRelay-parent中继父块作为锚点与参考点、依赖中继链状态的进程与数据所基于的特定中继链区块。Active Leaf活跃叶子块中继链某个活跃分叉头部的区块。区块作者供给任务按活跃叶子块 spawn并对变为非活跃的叶子块结束。Candidate Selection候选选择Provisioner 选择可背书平行链候选块以传给区块作者的过程。有两个版本prospective parachains 候选选择与遗留候选选择各自协议见上文对应小节。Availability Core可用性核心常简称为cores是资源管理的抽象。对 Provisioner 而言核心状态决定要为哪些para_id供给可背书候选块。更多细节见 Scheduler 模块Availability Cores 与 availability-cores 运行时 API。Availability Bitfield可用性位域常简称为bitfield表示某个验证人视角下平行链候选块可用性的视图每位对应一个可用性核心。更多细节见 availability 类型。Backable vs. Backed可背书 vs 已背书注意文档有时用 backed 指代可背书但尚未在链上被背书的候选。Backable指候选块的指派背书组中已有足够多成员提供签名确认声明。源码导航与延伸阅读若想进一步深入推荐按以下顺序阅读仓库内容核心实现node/core/provisioner/src/lib.rs供给数据记录、inherent 装配、位域/候选选择全流程争议优先级化选择node/core/provisioner/src/disputes/prioritized_selection/mod.rs 与 node/core/provisioner/src/disputes/mod.rs单元测试node/core/provisioner/src/tests.rs位域约束、可用性判定等与 node/core/provisioner/src/disputes/prioritized_selection/tests.rs协议类型定义overseer-protocol.md 中 ProvisionerMessage上下游子系统Candidate Backing、Bitfield Distribution、Dispute Coordinator、Prospective Parachains运行时侧runtime/disputes.md、scheduler.md、types/runtime.md 的 ParaInherentData。综上Provisioner 是 Polkadot 出块流水线中内容决策的核心枢纽它通过按中继父块组织的供给迭代收集各方共识数据在出块请求到达时执行位域筛选、争议陈述筛选与候选块选择最终产出一份可直接写入区块的ParaInherentData。理解它的两套候选选择模式与各类约束是理解 Polkadot 平行链出块全链路的关键一步。赞分享区块链【免费下载链接】polkadotPolkadot Node Implementation项目地址https://gitcode.com/gh_mirrors/po/polkadot点击查看免费下载相关推荐Polkadot 平行链区块生成Collation Generation子系统全解析候选区块的生产、签名封装与分发Polkadot 平行链区块生成Collation Generation子系统全解析候选区块的生产、签名封装与分发 Collation Generatio区块链Polkadot 异步共识核心Prospective Parachains 子系统与 Fragment Tree 深度解析Polkadot 异步共识核心Prospective Parachains 子系统与 Fragment Tree 深度解析 本篇技术指南以 Polkadot区块链超越.envrc用direnv.toml与dotenv命令管理.env配置的完整方案超越.envrc用direnv.toml与dotenv命令管理.env配置的完整方案 direnv 是一款让 .profile 保持整洁的环境变量管理工具帮区块链上一篇ctf-wiki 区块链重入攻击Re-Entrancy深度解析从 The DAO 硬分叉到 CTF 实战下一篇Bagel性能优化如何安全地在生产环境中禁用调试创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考