ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Substrate Scored Pool Pallet 深度解析:基于评分排名的成员池与治理信号集成

Substrate Scored Pool Pallet 深度解析:基于评分排名的成员池与治理信号集成 区块链开发框架后端【免费下载链接】substrateSubstrate: The platform for blockchain innovators项目地址https://gitcode.com/gh_mirrors/su/substrate点击查看免费下载导读pallet-scored-poolScored Pool是 Substrate FRAME 生态中一个极具特色的成员管理模块它维护一个评分会员池scored membership pool池中每个实体AccountId都可以被赋予一个量化Score系统每隔固定Period个区块自动从池中挑选得分最高的前MemberCount名实体组成正式成员集合Members并在每次刷新时通过MembershipChanged/MembershipInitialized两个 trait 将成员变更信号广播给其他模块如集体投票、联盟治理等。读完本文你将掌握该 pallet 的核心设计、五个公开函数的调用链与参数语义、Config配置项逐项含义以及如何在 runtime 中集成它并借助仓库测试用例验证行为。本文基于仓库 frame/scored-pool/README.md 展开并结合源码 frame/scored-pool/src/lib.rs、测试 frame/scored-pool/src/tests.rs 与 mock 配置 frame/scored-pool/src/mock.rs 进行深度印证。一、模块定位与核心机制1.1 什么是评分成员池从 lib.rs 的 pallet 文档注释 可以提炼出该模块的核心数据模型Pool候选池一个按得分降序排列的有界向量BoundedVec元素为(AccountId, OptionScore)。实体提交候选资格后进入 Pool得分尚未被赋予时Score为None。Members成员集合从 Pool 中取MemberCount个得分最高的实体构成是一个有序的BoundedVecAccountId。无分不入Score为None的实体永远不会进入Members。这是一个强约束测试unscored_entities_must_not_be_used_for_filling_memberstests.rs专门验证了即使把所有已评分成员全部踢出None候选人也不会被填充进成员集合。1.2 成员集合的刷新周期模块实现了 on_initialize hookfn on_initialize(n: BlockNumberForT) - Weight { if n % T::Period::get() Zero::zero() { let pool PoolT, I::get(); PalletT, I::refresh_members(pool, ChangeReceiver::MembershipChanged); } Weight::zero() }即每隔Period个区块在区块初始化阶段从当前 Pool 重新挑选得分最高的MemberCount名成员写入Members并调用T::MembershipChanged::set_members_sorted(new, old)。测试refreshing_happens_every_periodtests.rs演示了在区块 1 提交候选人并打分后Members仍为[20, 40]直到区块 4Period 4触发on_initialize后才变为[15, 40]。二、Config 配置项逐项解析Configtrait 在 lib.rs 中定义完整继承frame_system::Config。以下是全部关联类型的语义与建议取值关联类型约束说明mock 中的示例值CurrencyCurrencyAccountId ReservableCurrencyAccountId用于押金deposit的货币必须是可保留reserve余额的货币类型通常直接用pallet-balancesBalancesMaximumMembersGetu32Pool 与 Members 两个有界向量的容量上限#[pallet::constant]ConstU3210ScoreAtLeast32Bit Clone Copy Default FullCodec ... MaxEncodedLen赋予成员的量化得分类型要求全序可比较u64RuntimeEventFromEventSelf,I IsType...统一事件类型RuntimeEventCandidateDepositGetBalanceOfSelf,I提交候选资格时被 reserve 的押金退出或被踢时返还#[pallet::constant]25PeriodGetBlockNumberForSelf成员集合刷新的区块周期#[pallet::constant]ConstU644MembershipInitializedInitializeMembersAccountId创世pre-genesis时的成员初始化信号接收者通常与MembershipChanged相同TestChangeMembersMembershipChangedChangeMembersAccountId每次成员变更时收到set_members_sorted信号的接收者TestChangeMembersScoreOriginEnsureOriginRuntimeOrigin允许给候选人打分的来源OriginEnsureSignedByScoreOrigin, u64账户 3KickOriginEnsureOriginRuntimeOrigin允许移除实体的来源可配置为 RootEnsureSignedByKickOrigin, u64账户 2值得注意Period、MaximumMembers、CandidateDeposit都通过#[pallet::constant]暴露为常量可供 runtime 在其他地方引用Score的AtLeast32Bit约束保证了二进制搜索排序所需的全序关系。三、公开函数Call详解模块共 5 个可调用函数定义在 pallet::call。它们共同遵守一条索引约定调用者必须传入自己在Pool中的index位置索引pallet 通过ensure_indexlib.rs校验index越界报InvalidIndex与index处实体是否就是操作对象报WrongAccountIndex。3.1submit_candidacy(origin)—— 提交候选资格权限签名ensure_signed。行为先检查CandidateExists防止重复提交AlreadyInPool从origin账户reserve掉CandidateDeposit将(who, None)追加到 Pool 末尾因为None得分永远排在最后写入CandidateExists发出CandidateAdded事件。失败场景账户余额不足以支付押金如测试中账户 99 余额为 1押金 25报InsufficientBalance池已满TooManyMembers。源码位置lib.rs测试 submit_candidacy_works 与 submit_candidacy_must_not_work。3.2withdraw_candidacy(origin, index)—— 主动退出权限签名且index必须指向自己ensure_index。行为调用remove_member从 Pool 移除自己、解除CandidateExists标记、unreserve返还押金若自己当前在Members中则立即触发一次成员刷新由池中下一个最高分候选人替补refresh_members见 lib.rs发出CandidateWithdrew事件。测试验证withdraw_scored_candidacy_must_worktests.rs证明成员 40 退出后Members变为[20, 31]押金归零withdraw_unscored_candidacy_must_worktests.rs证明无分候选人也可退出。3.3kick(origin, dest, index)—— 强制移除权限T::KickOrigin可为 Root 或特定治理账户。行为通过T::Lookup::lookup(dest)解析目标账户校验索引后执行与退出相同的remove_member流程发出CandidateKicked事件。测试验证kicking_workstests.rs证明被踢者押金返还且成员由[20, 31]接替kicking_works_only_for_authorizedtests.rs证明未授权来源被拒绝BadOrigin。3.4score(origin, dest, index, score)—— 打分与重排权限T::ScoreOrigin。行为从 Pool 中按index移除旧条目然后利用 Pool 按得分降序排列的性质做二分查找插入binary_search_by_key键为Reverse(score)None视为Default值将(dest, Some(score))插入到保持排序的正确位置写回 Pool发出CandidateScored事件。排序细节由于得分相同的元素插在已有同分元素之前scoring_same_element_with_same_score_workstests.rs验证了同分31 与 20 同为 2 分时新条目排在原条目之前且顺序不破坏。源码位置lib.rs。3.5change_member_count(origin, count)—— 调整成员数量权限ensure_root仅 Root。行为调用update_member_count校验新值不超过MaximumMembers否则TooManyMembers后写入MemberCount存储。生效时机文档与源码均明确——该修改仅在下一次周期刷新时生效lib.rs不会立即改变Members。四、成员刷新与治理信号集成机制4.1refresh_members内部逻辑refresh_members(pool, notify)lib.rs是模块的核心内部函数读取当前MemberCount与旧Members从 Pool 中过滤掉Score None的实体取前count个Pool 本身按得分降序因此天然是最高分将结果转为BoundedVec并排序sort()按 AccountId 字典序写入Members存储依据notify枚举ChangeReceiver见 lib.rs决定调用哪个信号创世首次加载T::MembershipInitialized::initialize_members(new_members)周期刷新或成员变化T::MembershipChanged::set_members_sorted(new_members, old_members)注意第 2 步也解释了无分不入过滤条件score.is_some()把None实体永远挡在Members之外。4.2 两个信号 trait 的来源与典型实现InitializeMembers与ChangeMembers定义在 frame/support/src/traits/members.rsInitializeMembers只有一个方法initialize_members(members: [AccountId])在创世时被调用()空实现可直接作为占位。ChangeMembers的默认实现set_members_sorted会通过compute_members_diff_sorted计算 incoming/outgoing 差异再调用必须实现的change_members_sorted。()同样有ChangeMembers空实现members.rs。典型下游接收者pallet-collectiveimpl ChangeMembersT::AccountId for Palletframe/collective/src/lib.rs在成员变更时清理旧成员在所有提案中的投票并重置 primeimpl InitializeMembersframe/collective/src/lib.rs在创世写入初始成员。这正是得分池驱动集体治理的经典组合。pallet-membership其Config同样要求MembershipInitialized/MembershipChanged两个关联类型frame/membership/src/lib.rs并在 add/remove/swap 时调用change_members_sorted如 lib.rs、lib.rs。测试 mock 中的TestChangeMembersmock.rs则通过断言old incoming new outgoing校验变更的一致性是理解该 trait 契约的最佳范例。由此可以推断Scored Pool 的典型落地场景是作为自动按分排名的成员供给源——它将成员资格的产生与淘汰完全交给打分机制与周期刷新而非人工增删再把结果以标准信号同步给上游治理模块。五、创世配置GenesisConfig创世配置定义在 lib.rspub struct GenesisConfigT: ConfigI, I: static () { pub pool: PoolTT, I, // 初始候选池元素 (AccountId, OptionScore) pub member_count: u32, // 初始成员数量 }genesis_build的构建流程lib.rs为池中每个候选人reserve押金余额不足会 panic因为仅发生在创世时一次性执行写入CandidateExists按得分降序排序 PoolNone排末尾校验并写入member_count写入 Pool并以MembershipInitialized模式调用refresh_members即首次加载即初始化Members并触发initialize_members信号。mock 中的实例mock.rspallet_scored_pool::GenesisConfig::Test { pool: bounded_vec![(10, Some(1)), (20, Some(2)), (31, Some(2)), (40, Some(3)), (5, None)], member_count: 2, }配合Period 4测试query_membership_workstests.rs验证了创世结果得分最高的两个实体 403 分与 202 分与 31 同分但索引更靠前成为Members [20, 40]且 31 与 40 的押金已被 reserve。六、在 Runtime 中集成6.1 Cargo 依赖声明参考 Cargo.toml在 runtime 中按以下方式引入[dependencies] pallet-scored-pool { version 4.0.0-dev, default-features false, path frame/scored-pool } [features] std [pallet-scored-pool/std]该 pallet 依赖 frame-support、frame-system 与 sp-runtime 等核心库并通过try-runtimefeature 支持链上迁移检查。6.2 模块内封装调用README 的 Usage 示例 展示了在一个自定义 pallet 中封装submit_candidacy的标准写法在自己的Config上叠加scored_pool::Config再通过scored_pool::PalletT::submit_candidacy(...)转发调用对应源码 lib.rs 中同构的 doctestuse pallet_scored_pool::{self as scored_pool}; #[frame_support::pallet] pub mod pallet { use super::*; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; #[pallet::pallet] pub struct PalletT(_); #[pallet::config] pub trait Config: frame_system::Config scored_pool::Config {} #[pallet::call] implT: Config PalletT { #[pallet::weight(0)] pub fn candidate(origin: OriginForT) - DispatchResult { let who ensure_signed(origin)?; let _ scored_pool::PalletT::submit_candidacy( T::RuntimeOrigin::from(Some(who.clone()).into()) ); Ok(()) } } }注意RuntimeOrigin::from(Some(who).into())把签名账户包装为Some形式这是 FRAME pallet 之间调用时构造 signed origin 的惯用写法。6.3 Config 完整接线示例综合 mock.rs 与集体治理组合场景一个可落地的Config接线如下impl pallet_scored_pool::Config for Runtime { type RuntimeEvent RuntimeEvent; type KickOrigin EnsureRootAccountId; // 或治理账户 type MembershipInitialized Council; // 复用 pallet-collective 作为接收者 type MembershipChanged Council; type Currency Balances; // pallet-balances type CandidateDeposit CandidateDeposit; // 例如 ConstU1281_000_000 type Period ScoredPoolPeriod; // 例如 ConstU3214_400每天刷新 type Score u32; type ScoreOrigin EnsureSignedByScoreAuthority, AccountId; type MaximumMembers ConstU32100; }其中MembershipInitialized Council依赖 pallet-collective 实现了InitializeMembers/ChangeMembers见上文 4.2 的 collective 实现。七、存储布局与错误码速查7.1 Storage 一览lib.rs存储项类型getter说明PoolStorageValueBoundedVec(AccountId, OptionScore)pool()按得分降序排列的候选池None在末尾CandidateExistsStorageMapTwox64Concat, AccountId, boolcandidate_exists()冗余索引O(1) 判断是否已在池中Pool 按分而非按账户排序无法直接查询MembersStorageValueBoundedVecAccountIdmembers()当前成员集合已排序MemberCountStorageValueu32member_count()成员集合大小7.2 Error 一览lib.rsAlreadyInPool已提交候选资格重复提交。InvalidIndex传入的index越界。WrongAccountIndexindex位置上的实体与操作目标不符。TooManyMembersPool 容量达到MaximumMembers上限或member_count超过上限。八、测试验证与行为保障模块测试覆盖 tests.rs共 15 个用例重点行为均有对应验证创世成员选取query_membership_works候选资格submit_candidacy_works/submit_candidacy_must_not_work余额不足与重复提交打分与排序scoring_works高分插入首位/scoring_same_element_with_same_score_works同分保持有序踢出与权限kicking_works/kicking_works_only_for_authorized周期刷新refreshing_works/refreshing_happens_every_period退出与替补withdraw_scored_candidacy_must_work/withdraw_unscored_candidacy_must_work/withdraw_candidacy_must_only_work_for_members索引校验oob_index_should_abort/index_mismatches_should_abort边界与生命周期unscored_entities_must_not_be_used_for_filling_members/candidacy_resubmitting_works/pool_candidates_exceeded。这些测试不仅保障了排序、押金、替补、周期刷新等核心不变量也是二次开发时理解模块语义的最佳参考。九、适用场景与注意事项典型场景基于模块机制推断仓库未限定具体用例需要按量化贡献/信誉动态决定治理成员的网络例如把验证人信誉、质押规模、链上贡献转化为Score由ScoreOrigin定期打分的理事会或技术委员会成员供给源与 pallet-collective 组合让理事会成员集合由得分池自动刷新无需人工增删。注意事项Score为None的实体永远不能成为成员提交候选资格不等于获得成员身份必须先被打分change_member_count与score的效果均在下一个Period周期的刷新或触发刷新的移除操作时才体现到Members上所有索引型调用withdraw_candidacy/kick/score必须传入准确的Pool位置否则报InvalidIndex或WrongAccountIndex调用方应通过pool()getter 或find_in_pool辅助逻辑查询押金在创世构建时即被 reserve构建GenesisConfig需保证池内账户余额充足从 Cargo.toml 看该 pallet 版本为4.0.0-dev与 Substrate 主仓同源演进集成时应保持与当前 runtime 的 FRAME 版本一致。赞分享区块链开发框架后端【免费下载链接】substrateSubstrate: The platform for blockchain innovators项目地址https://gitcode.com/gh_mirrors/su/substrate点击查看免费下载相关推荐Substrate 框架中的 Treasury Pallet资金池治理与支出提案机制全解析Substrate 框架中的 Treasury Pallet资金池治理与支出提案机制全解析 本文围绕 Substrate 仓库中 frame/treasury区块链开发框架后端Substrate Tips Palletpallet-tips深入解析基于 Treasury 的敏捷打赏机制与源码实现Substrate Tips Palletpallet tips深入解析基于 Treasury 的敏捷打赏机制与源码实现 导读 本文系统讲解 Substr区块链开发框架后端Substrate pallet-elections-phragmen 深度解析基于顺序 Phragmén 的链上选举模块Substrate pallet elections phragmen 深度解析基于顺序 Phragmén 的链上选举模块 本文围绕 Substrate FR区块链开发框架后端上一篇NoteGen 开源项目使用与启动教程下一篇PuTTY 开源项目使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表