Rust区块链治理插件pallet-democracy的使用:实现去中心化链上投票与民主决策机制
Rust区块链治理插件pallet-democracy的使用:实现去中心化链上投票与民主决策机制
Democracy Pallet
概述
Democracy pallet负责处理一般利益相关者的投票管理。
提案在成为公投前可以加入两个不同的队列:
- 提案队列:包含所有公开提案
- 外部队列:包含单个提案,该提案源自某个外部来源(如集体组织)
每个启动周期(runtime中定义的长度),Democracy pallet会从提案队列或外部队列中轮流取出一个提案发起公投。系统中的任何代币持有者都可以对公投进行投票。投票系统使用时间锁定投票,允许代币持有者设置其投票的"信念(conviction)"。信念将决定代币被锁定的时间长度,以及缩放投票权重的乘数。
术语
- 颁布期(Enactment Period):最低锁定周期和提案被批准后到颁布之间的周期。
- 锁定周期(Lock Period):提案颁布后的一段时间,在此期间获胜投票者的代币将被锁定。
- 信念(Conviction):表示投票者对其投票的信念强度。信念每增加1表示代币持有者愿意在颁布后锁定其代币两倍的锁定周期。
- 投票(Vote):可以是赞成(“Aye”)或反对(“Nay”)特定公投的值。
- 提案(Proposal):提交到链上的行动建议,可以由账户或外部来源提出。
- 公投(Referendum):正在投票表决以决定是否接受或拒绝的系统变更提案。
- 委托(Delegation):将你的投票权授予另一个账户的决定,最多到某个信念值。
自适应法定人数偏差
公投可以是简单多数通过(50%+1的投票决定结果)或自适应法定人数偏差。自适应法定人数偏差根据公投最初提出的方式提高或降低通过或拒绝公投的阈值。有两种类型的自适应法定人数偏差:
- 正向参与偏差:要求公投需要超级多数才能通过,随着参与率增加而降低
- 负向参与偏差:要求公投需要超级多数才能拒绝,随着参与率增加而降低
接口
可调度函数
公共
这些调用可以从任何能够创建签名外部的账户进行。
基本操作:
propose
- 提交敏感行动,表示为哈希。需要押金。second
- 表示同意提案,将其移到提案队列更高位置,并需要与原始提案匹配的押金。vote
- 在公投中投票,可以是"赞成"以颁布提案或"反对"以保持现状。unvote
- 取消之前的投票,必须在投票结束前由投票者完成。delegate
- 将投票权(代币*信念)委托给另一个账户。undelegate
- 停止将投票权委托给另一个账户。
可以对任何账户执行的管理操作:
reap_vote
- 移除某些账户的过期投票。unlock
- 重新确定账户的余额锁定,可能使代币可用。
预映像操作:
note_preimage
- 注册即将提案的预映像,需要押金,提案颁布后返还。note_preimage_operational
- 同上,但由T::OperationalPreimageOrigin
提供。note_imminent_preimage
- 注册即将提案的预映像。不需要押金,但提案必须在调度队列中。note_imminent_preimage_operational
- 同上,但由T::OperationalPreimageOrigin
提供。reap_preimage
- 移除过期提案的预映像。只有在同一账户注册且投票期结束后,或不同账户在颁布期结束后才能工作。
取消来源
此调用只能由CancellationOrigin
进行。
emergency_cancel
- 安排公投的紧急取消。对特定公投只能发生一次。
外部来源
此调用只能由ExternalOrigin
进行。
external_propose
- 安排提案成为公投,一旦外部提议的公投合法。
外部多数来源
此调用只能由ExternalMajorityOrigin
进行。
external_propose_majority
- 安排提案成为多数通过的公投,一旦外部提议的公投合法。
外部默认来源
此调用只能由ExternalDefaultOrigin
进行。
external_propose_default
- 安排提案成为负向参与偏差的公投,一旦外部提议的公投合法。
快速跟踪来源
此调用只能由FastTrackOrigin
进行。
fast_track
- 立即安排当前外部提议的"多数通过"提案成为公投。
否决来源
此调用只能由VetoOrigin
进行。
veto_external
- 否决并列入外部提案哈希黑名单。
根
cancel_referendum
- 移除公投。cancel_queued
- 取消排队等待颁布的提案。clear_public_proposal
- 移除所有公开提案。
完整示例代码
// 引入必要的模块和类型
use frame_support::{decl_module, decl_event, decl_storage, dispatch::DispatchResult};
use frame_system::{self as system, ensure_signed};
use sp_runtime::traits::Hash;
// 定义模块配置
pub trait Config: system::Config {
type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;
type Currency: Currency<Self::AccountId>;
type Proposal: Parameter + Dispatchable<Origin = Self::Origin>;
type VotePeriod: Get<Self::BlockNumber>;
type MinimumDeposit: Get<BalanceOf<Self>>;
type EnactmentPeriod: Get<极简示例代码:
```rust
// 引入必要的模块
use frame_support::{dispatch::DispatchResult, traits::Currency};
use frame_system::ensure_signed;
// 定义投票类型
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Vote {
Aye, // 赞成
Nay, // 反对
}
// 定义信念类型
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Conviction {
None, // 无锁定
Locked1x, // 锁定1倍时间
Locked2x, // 锁定2倍时间
Locked3x, // 锁定3倍时间
Locked4x, // 锁定4倍时间
Locked5x, // 锁定5倍时间
Locked6x, // 锁定6倍时间
}
// 简化的Democracy模块实现
pub struct Democracy<T: Config> {
_phantom: std::marker::PhantomData<T>,
}
impl<T: Config> Democracy<T> {
// 提交提案
pub fn propose(
origin: T::Origin,
proposal: T::Proposal,
value: BalanceOf<T>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
// 确保押金足够
ensure!(value >= T::MinimumDeposit::get(), "Deposit too low");
// 扣除押金
T::Currency::reserve(&who, value)?;
// 存储提案逻辑...
Ok(())
}
// 投票
pub fn vote(
origin: T::Origin,
referendum_index: u32,
vote: Vote,
conviction: Conviction,
) -> DispatchResult {
let who = ensure_signed(origin)?;
// 验证公投存在
// 存储投票逻辑...
Ok(())
}
// 委托投票
pub fn delegate(
origin: T::Origin,
to: T::AccountId,
conviction: Conviction,
) -> DispatchResult {
let who = ensure_signed(origin)?;
// 存储委托逻辑...
Ok(())
}
}
// 测试用例
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_propose() {
// 测试提案提交逻辑
}
#[test]
fn test_vote() {
// 测试投票逻辑
}
}
这个简化示例展示了Democracy pallet的核心功能:
- 提案提交:用户需要质押代币才能提交提案
- 投票机制:支持赞成/反对投票和信念乘数
- 委托投票:允许用户将投票权委托给他人
实际使用时,需要根据Substrate框架的完整API进行更详细的实现。
1 回复
Rust区块链治理插件pallet-democracy使用指南
完整示例代码
以下是一个完整的runtime集成示例,展示了如何配置和使用pallet-democracy:
// 引入必要的依赖
use frame_support::{parameter_types, traits::Everything};
use frame_system as system;
use sp_core::H256;
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
BuildStorage,
};
use pallet_balances as balances;
// 定义runtime配置
pub type Block = frame_system::mocking::MockBlock<Test>;
pub type AccountId = u64;
frame_support::construct_runtime!(
pub enum Test
{
System: frame_system,
Balances: pallet_balances,
Democracy: pallet_democracy,
}
);
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const SS58Prefix: u8 = 42;
}
impl system::Config for Test {
type BaseCallFilter = Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Nonce = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u128>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u128 = 1;
pub const MaxLocks: u32 = 50;
}
impl balances::Config for Test {
type MaxLocks = MaxLocks;
type Balance = u128;
type RuntimeEvent = RuntimeEvent;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type WeightInfo = ();
type MaxReserves = ();
type ReserveIdentifier = [u8; 8];
type HoldIdentifier = ();
type FreezeIdentifier = ();
type MaxHolds = ();
type MaxFreezes = ();
}
parameter_types! {
pub const LaunchPeriod: u64 = 100;
pub const VotingPeriod: u64 = 100;
pub const EnactmentPeriod: u64 = 100;
pub const MinimumDeposit: u128 = 1;
pub const MaxProposals: u32 = 100;
pub const MaxDeposits: u32 = 100;
pub const MaxVotes: u32 = 100;
}
impl pallet_democracy::Config for Test {
type Proposal = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type EnactmentPeriod = EnactmentPeriod;
type LaunchPeriod = LaunchPeriod;
type VotingPeriod = VotingPeriod;
type MinimumDeposit = MinimumDeposit;
type ExternalOrigin = frame_system::EnsureRoot<AccountId>;
type ExternalMajorityOrigin = frame_system::EnsureRoot<AccountId>;
type ExternalDefaultOrigin = frame_system::EnsureRoot<AccountId>;
type FastTrackOrigin = frame_system::EnsureRoot<AccountId>;
type CancellationOrigin = frame_system::EnsureRoot<AccountId>;
type BlacklistOrigin = frame_system::EnsureRoot<AccountId>;
type CancelProposalOrigin = frame_system::EnsureRoot<AccountId>;
type VetoOrigin = frame_system::EnsureRoot<AccountId>;
type CooloffPeriod = ();
type PreimageByteDeposit = ();
type OperationalPreimageOrigin = frame_system::EnsureRoot<AccountId>;
type Slash = ();
type Scheduler = ();
type PalletsOrigin = OriginCaller;
type MaxProposals = MaxProposals;
type MaxDeposits = MaxDeposits;
type MaxVotes = MaxVotes;
type WeightInfo = ();
type MaxBlacklisted = frame_support::traits::ConstU32<100>;
}
// 测试用例
#[test]
fn test_democracy_workflow() {
new_test_ext().execute_with(|| {
// 初始化账户
let alice = 1;
let bob = 2;
let charlie = 3;
// 给账户分配资金
Balances::make_free_balance_be(&alice, 1000);
Balances::make_free_balance_be(&bob, 1000);
Balances::make_free_balance_be(&charlie, 1000);
// 1. 提交提案
let proposal = Box::new(Call::System(frame_system::Call::remark { remark: vec![1, 2, 3] }));
assert_ok!(Democracy::propose(
RuntimeOrigin::signed(alice),
proposal,
3
));
// 2. 对提案进行投票
assert_ok!(Democracy::vote(
RuntimeOrigin::signed(bob),
0,
pallet_democracy::Vote {
aye: true,
conviction: pallet_democracy::Conviction::Locked1x,
}
));
// 3. 委托投票
assert_ok!(Democracy::delegate(
RuntimeOrigin::signed(charlie),
bob,
pallet_democracy::Conviction::Locked1x,
500
));
// 4. 启动公投
assert_ok!(Democracy::launch(RuntimeOrigin::root()));
// 模拟区块前进到投票期结束
run_to_block(200);
// 检查提案状态
assert_eq!(
Democracy::referendum_status(0),
Some(ReferendumStatus {
end: 200,
proposal: Call::System(frame_system::Call::remark { remark: vec![1, 2, 3] }),
threshold: VoteThreshold::SuperMajorityApprove,
delay: EnactmentPeriod::get(),
tally: Tally {
ayes: 1500,
nays: 0,
turnout: 1500
}
})
);
});
}
fn new_test_ext() -> sp_io::TestExternalities {
let mut storage = frame_system::GenesisConfig::<Test>::default()
.build_storage()
.unwrap();
let _ = balances::GenesisConfig::<Test> {
balances: vec![(1, 1000), (2, 1000), (3, 1000)],
}
.assimilate_storage(&mut storage)
.unwrap();
let ext = sp_io::TestExternalities::new(storage);
ext
}
fn run_to_block(n: u64) {
while System::block_number() < n {
Democracy::on_finalize(System::block_number());
System::on_finalize(System::block_number());
System::set_block_number(System::block_number() + 1);
System::on_initialize(System::block_number());
Democracy::on_initialize(System::block_number());
}
}
示例说明
这个完整示例展示了:
- Runtime集成:如何将pallet-democracy集成到Substrate runtime中
- 配置参数:设置了LaunchPeriod、VotingPeriod等关键参数
- 完整治理流程:
- 账户初始化
- 提案提交
- 投票和委托投票
- 公投启动
- 结果检查
- 测试环境:使用mock环境测试治理流程
使用方法
- 将上述代码集成到你的Substrate链runtime中
- 根据实际需求调整参数(如VotingPeriod、MinimumDeposit等)
- 通过RuntimeCall提交不同类型的提案
- 在前端通过polkadot.js API与pallet-democracy交互
这个示例提供了完整的链上治理功能实现,可以作为开发自定义治理模块的基础。