免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Substrate Society Pallet 深度解析:基于 FRAME 的成员制经济游戏设计与实现

Substrate Society Pallet 深度解析:基于 FRAME 的成员制经济游戏设计与实现 区块链开发框架后端【免费下载链接】substrateSubstrate: The platform for blockchain innovators项目地址https://gitcode.com/gh_mirrors/su/substrate点击查看免费下载导读本文围绕 Substrate 仓库中的 frame/societypallet-society展开系统讲解这一以经济激励为核心的成员制社交通用模块它如何通过投标、投票、挑战、奖励与惩罚机制让用户在链上自发参与并维护一个自治社群。读完本文你将掌握 Society pallet 的用户角色体系、四阶段生命周期、旋转周期Rotation Period与挑战周期Challenge Period的运行机制以及全部 20 个可调用函数Dispatchable Function的用途、Config配置项与测试验证方式可直接用于在自己 Runtime 中集成该 pallet。概述Society 是什么Society pallet 是一个经济游戏economic game形式的 FRAME pallet核心目标是通过经济激励促使链上用户加入并持续维护一个会员制社群。它的运行不依赖中心化管理员而是依靠一整套自洽的博弈规则会员通过正确投票获得奖励怠于参与或投错票会被扣罚Strike / Slash成员资格面临周期性挑战最终实现社群的自我更新与淘汰。模块的完整实现位于 frame/society/src/lib.rs测试位于 frame/society/src/tests.rsmock 运行时配置见 frame/society/src/mock.rs。pallet 的Cargo.tomlframe/society/Cargo.toml显示其名称为pallet-society版本为4.0.0-dev依赖frame-support、frame-system、sp-runtime等核心库并可选开启runtime-benchmarks、try-runtime、experimental迁移使用VersionedRuntimeUpgrade等 feature。用户类型User Types在任意时刻一个用户在社会中只可能是以下五种状态之一对应源码中的Members、SuspendedMembers等存储用户类型说明Bidder投标者已提交加入社群意向的用户Candidate候选人正在被投票表决是否入社的用户Suspended Candidate被暂停的候选人投票失败后被暂停的候选人Member会员社群的正式成员Suspended Member被暂停的会员累计 Strike 过多或未通过成员挑战的会员在非暂停会员中始终存在两个特殊角色Head首领被豁免于暂停的会员Defender防守者其成员资格正被质疑、需要再次投票的会员。此外每一轮旋转期会从非暂停会员中随机抽取一组怀疑者Skeptics其机制在会员阶段一节详述。源码中这些角色对应Head、Defending、Skeptic存储项frame/society/src/lib.rs。核心机制Mechanics奖励Rewards会员通过参与社群活动获得奖励奖励由 Society 金库treasury支付并带有成熟期maturity period——用户必须等待到期后才能领取资金。源码中奖励通过Payout结构体记账包含value总额、begin起始区块、duration摊分区块数、paid已支付额四个字段frame/society/src/lib.rs。奖励的锁定期长度由lock_duration函数动态计算公式为let lock_pc 100 - 50_000 / (x 500); Percent::from_percent(lock_pc as u8) * T::MaxLockDuration::get()即会员越多锁定期越长上限为MaxLockDurationframe/society/src/lib.rs。只有 rank 0 的会员能累积 payoutbump_payout中显式检查MemberRecord { rank: 0, .. }rank 1 会员不再获得奖励frame/society/src/lib.rs。惩罚Punishments会员可能因两类行为受罚Slash削减直接削减其未领取的奖励发放额度Strike记过会员累积 Strike达到上限后会被暂停Suspended。源码strike_member的实现揭示了惩罚的精确逻辑每记一次 Strike当strikes GraceStrikes宽限次数时将未领取 payout 总额削减一半当strikes params.max_strikes最大记过次数时正式暂停会员frame/society/src/lib.rs。slash_payout按 payout 队列从前到后逐个抵扣frame/society/src/lib.rs。测试用例slash_payout_works、slash_payout_multi_works分别验证了单笔与多笔 payout 的削减行为frame/society/src/tests.rs。怀疑者Skeptics投票期内会随机抽取一组会员作为怀疑者Skeptic他们有义务对当期候选人投票。若怀疑者未投票其怀疑状态会被视为反对票rejection vote该会员被判定为懒惰lazy每缺一票记一次 Strike。源码中每轮通过pick_member(rng)随机指定一名Skepticframe/society/src/lib.rs并由check_skeptic在结算时检查怀疑者是否投了与多数一致的票否则记 Strikeframe/society/src/lib.rs。测试non_voting_skeptic_is_punished与rejecting_skeptic_on_approved_is_punished覆盖了未投票被罚与投反对票但候选人获准被罚两种场景frame/society/src/tests.rs。成员挑战Membership Challenges每个挑战周期Challenge Period系统随机选择一名现有会员来捍卫自己的成员资格其他会员以简单多数majority-wins投票决定其去留。规则要点平票视为挑战失败但若无人投票防守者对自己挑战的免费一票足以让其留在社群中Head 豁免于挑战的负面结果。源码rotate_challenge中防守者只有在tally.more_approvals()严格多数赞成时才能过关否则调用suspend_member暂停之若防守者同时是 Head 或 Founder暂停会静默失败frame/society/src/lib.rs。挑战在on_initialize钩子中按n % ChallengePeriod 0触发frame/society/src/lib.rs。测试challenges_work完整演示了防守者存活、平票被暂停以及新防守者产生的全过程frame/society/src/tests.rs。Society 金库Society Treasury成员社群由本 pallet 独立管理的金库提供资金。金库中的一部分被放入Society Pot社会资金池用于决定每期可接受的投标数量。源码对应两个账户与两个存储account_id()主金库账户由PalletId通过into_account_truncating派生frame/society/src/lib.rspayouts()payout 专用子账户frame/society/src/lib.rsPot存储下一轮投标的可用额度rotate_intake中每期最多按PeriodSpend补充资金池frame/society/src/lib.rs。pallet 还实现了OnUnbalancedtrait任何非零负失衡如罚款收入都会被解析到主金库账户并触发Deposit事件frame/society/src/lib.rs。创世配置GenesisConfig仅含一个pot字段frame/society/src/lib.rs。增长率Rate of Growth社群以每个旋转周期最多吸收 10 名候选人max_intake参数README 描述的默认速率为 10的速度增长直至达到最大成员数阈值。达到阈值后候选人筛选暂停直到通过随机挑战投票淘汰现有成员腾出名额或通过治理提高最大成员数。select_new_candidates的实现同时受三个约束限制min(max_intake, max_members - member_count, bids.len())且选中投标的总成本不得超过 Potframe/society/src/lib.rs。此外insert_bid以 BoundedVec 限制投标队列上限为MaxBids队列满时从最大报价者开始淘汰并触发AutoUnbid事件frame/society/src/lib.rs。用户生命周期User Life Cycle用户会经历以下阶段README 原图------- User ---------- | | | | | ---------------------------------------------- | | | | | | | v | | | | Bidder ----------- | | | | | | | | | | | v Suspended | | | Candidate ---- Candidate | | | | | | | | | | | | | | Suspended ------| | | | Member | | | | ^ | | | | | v | | | ------- Member ---------- | | | | | ------------------Society---------------------初始化Initialization社群初始化时只有一名会员该会员自动成为 Head。初始化通过found_society调用完成它写入Parametersmax_members、max_intake、max_strikes、candidate_deposit将 founder 以 rank 1 身份插入会员列表同时写入Head、Founder、Rules对规则内容的哈希三个存储frame/society/src/lib.rs。测试founding_works验证了非 Founder 源不能创建社群、创始人账户 1 可以创建、规则哈希正确、首次旋转后 Pot 增长、且无法重复创建AlreadyFoundedframe/society/src/tests.rs。注意源码要求max_members 1否则报MaxMembers错误。投标阶段Bid Phase新用户加入社群必须先有投标bid用户可以自行投标通过预留押金reserving a deposit押金即candidate_deposit也可以由现有会员代为投标即担保vouching。投标包含用户希望加入社群后获得的奖励信息担保投标vouching bid可以额外要求将奖励的一部分作为**小费tip**支付给担保人。bid调用内部先T::Currency::reserve预留押金再按价值排序插入Bids队列frame/society/src/lib.rsvouch调用则把被担保人的 bid 记为BidKind::Vouch(voucher, tip)frame/society/src/lib.rs。每个旋转周期投标按奖励金额排序模块根据 Society Pot 的承受能力挑选尽可能多的投标。被选中的投标成为候选人进入候选阶段未选中的留在 bidder 池中直到被选中或用户主动unbid撤销投标押金退回。测试bidding_works展示了报价 300/400 的用户被选中、500 的用户因 Pot 不足留待下期、随着 Pot 每期增长 1000 而逐步吸收更高报价者的完整过程frame/society/src/tests.rs。候选阶段Candidate Phase一旦投标者成为候选人会员们投票决定是否批准其入社投票同样发生在一个旋转周期内。候选人的批准/拒绝标准不在链上设置不同社群可以有不同的标准。在旋转周期结束时pallet 收集某个候选人的所有投票并随机选取一票作为最终结果README 原图[ a-accept, r-reject, s-skeptic ] ---------------------------------- | | | Member |0|1|2|3|4|5|6|7|8|9| | | ----------------------------- | | Vote |a|a|a|r|s|r|a|a|s|a| | | ----------------------------- | | Selected | | | |x| | | | | | | | | | ---------------------------------- Result: Rejected这一机制的源码支撑来自do_vote投票权重为(rank 1)^2即高 rank 会员的票具有更高权重计入Tallyapprovals / rejections并在候选人存储中累计frame/society/src/lib.rs。结算规则投了与被随机选中票相反方向的会员会被惩罚削减未领取 payout、增加 Strike 数被削减的资金奖励给一名投了与选中票相同方向的随机用户候选人获胜则获得投标奖励作为未来 payout若是担保投标担保人先获得自己的份额tip.min(value)剩余部分支付给获胜候选人reward_bidder见 frame/society/src/lib.rs获胜候选人中会选出一名Head选择方式是按获胜候选人累积的批准数加权随机对应NextHead存储的更新逻辑见 frame/society/src/lib.rs候选人落败则被暂停由Suspension Judgement origin暂停裁决源决定其命运重新投标、直接批准入社、或拒绝并没收押金。测试bad_vote_slash_works精确验证了投错票者 Strike1、payout 减半而投对票者全额保留的结算frame/society/src/tests.rs。候选阶段还提供claim_membership候选人自助入社需clear_approval即赞成票 ≥ 2×反对票、bestow_membership创始人授予、kick_candidate/resign_candidacy/drop_candidate清除候选人等配套调用frame/society/src/lib.rs。会员阶段Member Phase候选人成为会员后其职责是参与社群常规参与对想加入的候选人投票投对方向会累积未来 payoutpayout 成熟后可用payout领取注意该调用每次只领取第一笔成熟款项需多次调用领取多笔见 frame/society/src/lib.rs担保vouch为其他用户担保入社并请求小费这对余额不足以支付投标押金的用户很有用一个会员同一时间只能担保一个用户若被担保人被暂停裁决源拒绝担保人会被永久禁止再担保VouchingStatus::Banned见 frame/society/src/lib.rs怀疑者义务旋转周期内随机选出的怀疑者必须对当期候选人投票否则每缺一票记一次 Strike应对挑战挑战周期与旋转周期并行随机选出的会员必须捍卫其成员资格其他会员以多数票决定去留平票视为失败。暂停与恢复会员若 Strike 过多或挑战失败即被暂停。暂停期间无法领取成熟 payout测试suspended_member_life_cycle_works中payout直接报NotMember见 frame/society/src/tests.rs。由 Suspension Judgement origin 决定其重新入社或被除名除名后所有未来 payout 被削减并归还金库judge_suspended_member的 reject 分支调用unreserve_payout(total)见 frame/society/src/lib.rs。值得注意的边界规则Head 和 Founder 永不能被移除——remove_member中显式ensure!(Head ! m)与ensure!(Founder ! m)frame/society/src/lib.rs测试founder_and_head_cannot_be_removed验证了 Founder/Head 可无限累积 Strike 但不会被暂停frame/society/src/tests.rs。接口可调用函数Dispatchable FunctionsREADME 将可调用函数按权限分为三类。对照当前源码frame/society/src/lib.rspallet 实际暴露了 20 个可调用函数call_index 从 0 到 19面向普通用户For General Users函数call_index说明bid0用户预留押金投标加入社群unbid1撤销投标押金退回clean_bid中Currency::unreserve面向会员For Members函数call_index说明vouch2会员为用户代为投标参数含value总奖励与tip担保人分成unvouch3撤销担保仅在被担保人还是 bidder、未成为候选人时有效vote4会员投票批准/拒绝候选人approve: booldefender_vote5会员投票批准/拒绝防守者的继续留任payout6领取第一笔成熟款项waive_repay7归还已领款项并清空待领款项会员从 rank 0 晋升为 rank 1Elevated事件claim_membership13候选人本人主张会员资格需明确批准且投票期已结束resign_candidacy16候选人主动退出候选押金没收、担保人被禁README 中提及的unfound解散社群在源码中对应dissolvecall_index 9要求调用者同时是 Founder 与 Head即只有当社群仅剩一名会员时才能执行执行后清除全部存储并触发Unfounded事件frame/society/src/lib.rs。测试unfounding_works验证了非 Founder 无法解散、存在第二名会员时即使 Founder 调用也报NotHeadframe/society/src/tests.rs。面向超级用户For Super Users函数call_index说明found_society8Founder 源FounderSetOrigin发起社群参数founder、max_members、max_intake、max_strikes、candidate_deposit、rules适用于在已运行的链上引导 Society palletmax_members必须 1judge_suspended_member10暂停裁决源对暂停会员作出裁决forgive: boolset_parameters11Founder 更新max_members/max_intake/max_strikes/candidate_depositmax_members不得小于当前会员数bestow_membership14Founder 在投票期结束后授予未明确被拒的候选人会员资格kick_candidate15Founder 在投票期结束后移除候选人押金没收、担保人被禁punish_skeptic12候选人本人检举未投票的怀疑者获取 Strike 惩罚drop_candidate17任意签名用户在下一轮之后清除被明确拒绝的候选申请cleanup_candidacy18任意签名用户分批清理已结束候选的过期投票防止存储膨胀cleanup_challenge19任意签名用户分批清理已结束挑战轮的过期投票命名差异提示README 接口一节沿用了较早期的命名found、unfound、set_max_membership当前源码中的实际函数名分别为found_society、dissolve、set_parameters功能对应一致集成或编写脚本时请以源码中的 call_index 和函数名为准。Runtime 集成Config 配置项详解Configtraitframe/society/src/lib.rs共 11 个关联类型集成到 Runtime 时需要逐一配置关联类型类型约束说明RuntimeEventFromEventSelf, Ipallet 事件类型PalletIdGetPalletId金库账户的 PalletIdCurrencyReservableCurrency用于投标押金的币种RandomnessRandomness运行时随机源选怀疑者、抽票、选防守者GraceStrikesGetu32Strike 达到该值后开始削减 payout减半PeriodSpendGetBalance每个周期注入 Pot 的激励额度不含 VoterTipVotingPeriodGetBlockNumber候选人投票持续区块数与ClaimPeriod之和构成两次 intake 的间隔ClaimPeriodGetBlockNumber新会员认领资格并成为 Head 的区块数MaxLockDurationGetBlockNumberpayout 锁定的最大区块数FounderSetOriginEnsureOrigin允许调用found_society的源ChallengePeriodGetBlockNumber两次成员挑战之间的区块数MaxPayoutsGetu32会员最多可累积的未领取 payout 笔数MaxBidsGetu32投标队列上限同时存在的最大投标数WeightInfoWeightInfo外部的权重信息frame/society/src/mock.rs 给出了一个可直接参考的完整配置示例parameter_types! { pub const SocietyPalletId: PalletId PalletId(*bpy/socie); } ord_parameter_types! { pub const ChallengePeriod: u64 8; pub const ClaimPeriod: u64 1; pub const FounderSetAccount: u128 1; pub const MaxPayouts: u32 10; pub const MaxBids: u32 10; } impl pallet_society::Config for Test { type RuntimeEvent RuntimeEvent; type PalletId SocietyPalletId; type Currency pallet_balances::PalletSelf; type Randomness TestRandomnessSelf; type GraceStrikes ConstU321; type PeriodSpend ConstU641000; type VotingPeriod ConstU643; type ClaimPeriod ClaimPeriod; type MaxLockDuration ConstU64100; type FounderSetOrigin EnsureSignedByFounderSetAccount, u128; type ChallengePeriod ChallengePeriod; type MaxPayouts MaxPayouts; type MaxBids MaxBids; type WeightInfo (); }创世配置GenesisConfig只有一个pot字段用于预置 Society Pot 的初始资金frame/society/src/lib.rspallet_society::GenesisConfig { pot: self.pot }.assimilate_storage(mut t)生命周期运转on_initialize 钩子与周期推进pallet 的自动运转全部集中在on_initialize钩子frame/society/src/lib.rs从Randomness取随机种子固定短语bsociety_rotation用ChaChaRng构造确定性随机源intake 旋转当当前处于Period::Voting且elapsed 0时调用rotate_intake每周期消耗约max_block / 20权重——推进轮次、按PeriodSpend补充 Pot、调用select_new_candidates从投标池挑选候选人、随机指定本轮 Skeptic挑战旋转当block % ChallengePeriod 0时调用rotate_challenge——结算上一防守者的投票结果不获多数即暂停、惩罚失职怀疑者并使其成为下一防守者、或随机挑选新防守者。period()函数将区块号映射为两个阶段Voting前VotingPeriod块与Claim后ClaimPeriod块一个完整旋转周期长度为voting_period claim_periodframe/society/src/lib.rs。测试环境通过run_to_block 手动触发Society::on_initialize模拟这些周期frame/society/src/mock.rs。存储结构一览pallet 共使用 21 个存储项frame/society/src/lib.rs理解它们有助于链上状态查询与监控存储项类型含义ParametersStorageValueGroupParams社群参数max_members / max_intake / max_strikes / candidate_depositPotStorageValueBalance下一轮投标可用资金池FounderStorageValueAccountId创始人第一名会员HeadStorageValueAccountId当前首领RulesStorageValueHash社群规则哈希仅创始人可设置一次MembersStorageMapAccountId, MemberRecord当前会员及 rank/strikes/vouching/indexPayoutsStorageMapAccountId, PayoutRecordrank-0 会员的已付与待付 payoutMemberCountStorageValueu32会员数不含暂停会员MemberByIndexStorageMapu32, AccountId按稠密索引 0..MemberCount 存储会员用于随机抽取SuspendedMembersStorageMapAccountId, MemberRecord暂停会员及其旧记录RoundCountStorageValueu32已过的候选轮次数BidsStorageValueBoundedVecBid, MaxBids当前投标队列按价值升序CandidatesStorageMapAccountId, Candidacy当期候选人及其 tallySkepticStorageValueAccountId当期怀疑者VotesStorageDoubleMapCandidate, Voter, Vote候选人对投票者的投票VoteClearCursorStorageMapCandidate, Cursor投票清理游标分批删除用NextHeadStorageValueIntakeRecord下一任 Head 的候补记录最低 bid 的最近批准者ChallengeRoundCountStorageValueu32挑战轮次数用于识别过期 DefenderVotesDefendingStorageValue(AccountId, AccountId, Tally)当前被挑战的防守者与计票DefenderVotesStorageDoubleMapRoundIndex, AccountId, Vote按挑战轮记录的防守者投票测试与基准行为验证单元测试frame/society/src/tests.rs 共覆盖迁移、建社/解散、投标/撤销、担保/反担保、投票与 slash、怀疑者惩罚、成员挑战、暂停裁决、Founder/Head 保护等约 20 个场景是理解 pallet 行为的权威参考。迁移frame/society/src/migrations.rs 提供从 v1 到 v2VersionUncheckedMigrateToV2的存储迁移try-runtimefeature 下带pre_upgrade/post_upgrade校验启用experimentalfeature 时使用VersionedRuntimeUpgrade。基准frame/society/src/benchmarking.rs 为每个外部提供benchmarks_instance_pallet基准用例通过runtime-benchmarksfeature 生成权重数据对应 frame/society/src/weights.rs保证链上 gas 计费准确。总结Society pallet 是 Substrate FRAME 生态中少见的纯博弈规则型模块它不依赖任何链下信任仅凭on_initialize周期钩子 确定性随机源 奖惩记账就能运转一个具备自我更新能力的成员社群。理解其用户生命周期Bidder → Candidate → Member → Suspended、双周期旋转 挑战机制以及 20 个可调用函数的权限划分是正确集成与二次开发的前提。建议结合实际 Runtime 配置参考 frame/society/src/mock.rs与单元测试frame/society/src/tests.rs动手验证以获得对该博弈系统最直观的掌握。赞分享区块链开发框架后端【免费下载链接】substrateSubstrate: The platform for blockchain innovators项目地址https://gitcode.com/gh_mirrors/su/substrate点击查看免费下载相关推荐Substrate Scheduler Pallet 深度解析FRAME 定时任务调度机制与实现原理Substrate Scheduler Pallet 深度解析FRAME 定时任务调度机制与实现原理 导读 pallet scheduler 是 Substr区块链开发框架后端Substrate 成员管理 Pallet 深度解析用 pallet-membership 管理 Collective 成员与 Prime MemberSubstrate 成员管理 Pallet 深度解析用 pallet membership 管理 Collective 成员与 Prime Member 导读区块链开发框架后端Substrate Tips Palletpallet-tips深入解析基于 Treasury 的敏捷打赏机制与源码实现Substrate Tips Palletpallet tips深入解析基于 Treasury 的敏捷打赏机制与源码实现 导读 本文系统讲解 Substr区块链开发框架后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表