Rust链上治理模块pallet-conviction-voting的使用,实现基于信念投票的Substrate区块链治理机制
Rust链上治理模块pallet-conviction-voting的使用,实现基于信念投票的Substrate区块链治理机制
Voting Pallet
概述
用于公投投票的模块。
元数据
- 版本: 42.0.0
- 发布时间: 4天前
- 许可证: Apache-2.0
- 大小: 46.5 KiB
安装
在项目目录中运行以下Cargo命令:
cargo add pallet-conviction-voting
或在Cargo.toml中添加以下行:
pallet-conviction-voting = "42.0.0"
示例代码
以下是一个完整的示例,展示如何使用pallet-conviction-voting实现基于信念投票的治理机制:
// 在runtime/src/lib.rs中配置conviction-voting模块
// 1. 引入必要的模块
use frame_support::traits::{Currency, LockIdentifier};
use pallet_conviction_voting as conviction_voting;
// 2. 定义锁标识符
pub const CONVICTION_VOTING_ID: LockIdentifier = *b"convictv";
// 3. 配置模块
impl conviction_voting::Config for Runtime {
type Event = Event;
type WeightInfo = ();
type Currency = Balances; // 使用Balances作为投票代币
type VoteLockingPeriod = VoteLockingPeriod; // 投票锁定周期
type MaxVotes = MaxVotes; // 最大投票数
type MaxTurnout = frame_support::traits::TotalIssuanceOf<Balances, Self>; // 最大投票率
type Polls = Referenda; // 关联公投模块
}
// 4. 在construct_runtime!宏中添加模块
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ...其他模块
ConvictionVoting: conviction_voting::{Pallet, Call, Storage, Event<T>},
// ...其他模块
}
);
// 5. 在runtime/src/benchmarking.rs中添加基准测试(可选)
#[cfg(feature = "runtime-benchmarks")]
mod benchmarks {
use super::*;
use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller};
use frame_system::RawOrigin;
benchmarks! {
vote {
let caller: T::AccountId = whitelisted_caller();
let balance = T::Currency::minimum_balance() * 100u32.into();
T::Currency::make_free_balance_be(&caller, balance);
let poll index = 0;
let vote = conviction_voting::Vote { aye: true, conviction: conviction_voting::Conviction::Locked1x };
}: _(RawOrigin::Signed(caller), poll_index, vote)
// 其他基准测试...
}
impl_benchmark_test_suite!(ConvictionVoting, crate::mock::new_test_ext(), crate::mock::Test);
}
// 6. 使用示例 - 前端调用投票
use sp_core::crypto::AccountId32;
use sp_runtime::AccountId32 as AccountId;
use frame_support::dispatch::DispatchResult;
use pallet_conviction_voting::{Vote, Conviction};
fn cast_vote(
origin: AccountId,
poll_index: u32,
vote_aye: bool,
conviction_level: u8,
) -> DispatchResult {
// 创建投票对象
let vote = Vote {
aye: vote_aye,
conviction: Conviction::try_from(conviction_level).expect("Invalid conviction level"),
};
// 调用投票函数
ConvictionVoting::vote(
frame_system::RawOrigin::Signed(origin).into(),
poll_index,
vote,
)
}
完整示例Demo
以下是一个更完整的实现示例,包含测试环境和模拟调用:
// runtime/src/lib.rs
use frame_support::{
construct_runtime, parameter_types,
traits::{Currency, LockIdentifier, LockableCurrency, ReservableCurrency},
};
use sp_core::H256;
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
BuildStorage, Perbill,
};
// 定义runtime类型
pub type BlockNumber = u32;
pub type AccountId = u64;
pub type Balance = u128;
// 参数类型定义
parameter_types! {
pub const BlockHashCount: BlockNumber = 2400;
pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1);
pub const MaximumBlockLength: u32 = 2 * 1024;
pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);
}
// 实现系统配置
impl frame_system::Config for Runtime {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Index = u64;
type BlockNumber = BlockNumber;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = sp_runtime::generic::Header<BlockNumber, BlakeTwo256>;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type DbWeight = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
// 实现余额模块配置
impl pallet_balances::Config for Runtime {
type Balance = Balance;
type DustRemoval = ();
type RuntimeEvent = RuntimeEvent;
type ExistentialDeposit = ConstU128<1>;
type AccountStore = System;
type WeightInfo = ();
type FreezeIdentifier = ();
type MaxFreezes = ();
type RuntimeHoldReason = ();
type MaxHolds = ();
}
// 定义锁标识符
pub const CONVICTION_VOTING_ID: LockIdentifier = *b"convictv";
// 实现信念投票模块配置
impl pallet_conviction_voting::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type Currency = Balances;
type VoteLockingPeriod = ConstU32<7>; // 锁定7个区块
type MaxVotes = ConstU32<100>; // 最大100次投票
type MaxTurnout = frame_support::traits::TotalIssuanceOf<Balances, Self>;
type Polls = Referenda;
}
// 实现公投模块配置
impl pallet_referenda::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SubmissionDeposit = ConstU128<100>;
type MaxQueued = ConstU32<10>;
type UndecidingTimeout = ConstU32<100>;
type AlarmInterval = ConstU32<10>;
type Tracks = ();
type Preimages = ();
type WeightInfo = ();
type Scheduler = ();
}
// 构造runtime
construct_runtime!(
pub struct Runtime {
System: frame_system,
Balances: pallet_balances,
ConvictionVoting: pallet_conviction_voting,
Referenda: pallet_referenda,
}
);
// 测试环境
#[cfg(test)]
mod tests {
use super::*;
use frame_support::{
assert_ok,
traits::{OnFinalize, OnInitialize},
};
use sp_io::TestExternalities;
use sp_runtime::DispatchError;
// 测试环境构建
pub fn new_test_ext() -> TestExternalities {
let mut storage = frame_system::GenesisConfig::<Runtime>::default()
.build_storage()
.unwrap();
pallet_balances::GenesisConfig::<Runtime> {
balances: vec![(1, 1000), (2, 2000), (3, 3000)],
}
.assimilate_storage(&mut storage)
.unwrap();
TestExternalities::new(storage)
}
// 测试投票功能
#[test]
fn test_conviction_voting() {
new_test_ext().execute_with(|| {
// 创建公投
assert_ok!(Referenda::submit(
RuntimeOrigin::signed(1),
Box::new(RuntimeCall::System(frame_system::Call::remark { remark: vec![] })),
DispatchTime::At(10),
));
let poll_index = 0;
// 用户1投票
assert_ok!(ConvictionVoting::vote(
RuntimeOrigin::signed(1),
poll_index,
pallet_conviction_voting::Vote {
aye: true,
conviction: pallet_conviction_voting::Conviction::Locked1x
}
));
// 用户2投票
assert_ok!(ConvictionVoting::vote(
RuntimeOrigin::signed(2),
poll_index,
pallet_conviction_voting::Vote {
aye: false,
conviction: pallet_conviction_voting::Conviction::Locked2x
}
));
// 检查投票结果
let tally = ConvictionVoting::tally(poll_index);
assert_eq!(tally.ayes, 1000); // 1x 1000
assert_eq!(tally.nays, 4000); // 2x 2000
});
}
}
工作原理
-
信念投票(Conviction Voting):
- 用户可以选择锁定代币的时间长度来获得投票权重乘数
- 锁定时间越长,投票权重越高
-
投票类型:
Aye
/Nay
: 支持或反对提案Conviction
: 锁定时间乘数(1x-6x)
-
投票流程:
- 用户发起投票并选择锁定时间
- 代币将被锁定相应时间
- 投票权重 = 代币数量 × 乘数
- 投票结束后,根据总权重决定提案是否通过
注意事项
- 需要配合公投模块(如pallet-referenda)使用
- 投票锁定时间由runtime配置决定
- 投票权重计算在链上自动完成
- 锁定代币在锁定期间不可转移
这个模块提供了灵活且安全的链上治理机制,允许代币持有者通过锁定代币来增加投票权重,从而更有效地参与网络治理。
1 回复
Rust链上治理模块pallet-conviction-voting的使用
介绍
pallet-conviction-voting
是Substrate区块链框架中的一个链上治理模块,实现了基于"信念投票"(Conviction Voting)的治理机制。这种机制允许代币持有者根据他们对提案的信念强度来调整投票权重,而不仅仅是简单的1代币=1票。
核心概念
- 信念乘数:用户可以选择锁定代币的时间长度,锁定时间越长,投票权重乘数越大
- 投票委托:用户可以将投票权委托给其他账户
- 投票周期:提案有固定的投票周期,结束后根据投票结果执行
使用方法
1. 在runtime中集成
首先需要在你的Substrate runtime中集成该pallet:
// runtime/src/lib.rs
impl pallet_conviction_voting::Config for Runtime {
type Event = Event;
type Currency = Balances; // 使用Balances作为货币类型
type VoteLockingPeriod = VoteLockingPeriod; // 定义投票锁定周期
// 其他配置...
}
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ...其他pallet
ConvictionVoting: pallet_conviction_voting::{Pallet, Call, Storage, Event<T>},
}
);
2. 发起提案
use frame_support::weights::{DispatchClass, Pays};
use frame_system::Config;
// 创建一个提案调用
let proposal = Box::new(
frame_system::Call::remark { remark: b"This is a governance proposal".to_vec() }.into()
);
// 发起提案
ConvictionVoting::propose(
RuntimeOrigin::signed(proposer_account),
proposal,
// 需要的保证金
100_000_000,
// 提案描述长度
32,
);
3. 投票
use pallet_conviction_voting::Vote;
// 投票示例
ConvictionVoting::vote(
RuntimeOrigin::signed(voter_account),
proposal_index,
// 投票方向和信念强度
Vote {
aye: true, // 支持(true)或反对(false)
conviction: Conviction::Locked6x, // 锁定乘数(6倍)
},
// 投票金额
1_000_000_000,
);
4. 委托投票
// 将投票权委托给其他账户
ConvictionVoting::delegate(
RuntimeOrigin::signed(delegator_account),
class,
delegate_account,
Conviction::Locked2x, // 委托信念强度
500_000_000, // 委托金额
);
5. 执行提案
投票期结束后,如果提案通过,可以由任何账户执行:
ConvictionVoting::enact_proposal(
RuntimeOrigin::signed(executor_account),
proposal_hash,
proposal_index,
);
信念级别
Conviction
枚举定义了不同的信念级别:
pub enum Conviction {
/// 不锁定,无乘数
None,
/// 锁定1倍投票周期,1x乘数
Locked1x,
/// 锁定2倍投票周期,2x乘数
Locked2x,
/// 锁定4倍投票周期,4x乘数
Locked4x,
/// 锁定8倍投票周期,8x乘数
Locked8x,
/// 锁定16倍投票周期,16x乘数
Locked16x,
/// 锁定32倍投票周期,32x乘数
Locked32x,
}
完整示例
以下是一个完整的示例,展示如何在Substrate链上使用pallet-conviction-voting:
// runtime/src/lib.rs
// 1. 配置ConvictionVoting pallet
parameter_types! {
pub const VoteLockingPeriod: BlockNumber = 7 * DAYS;
}
impl pallet_conviction_voting::Config for Runtime {
type Event = Event;
type Currency = Balances; // 使用Balances模块处理代币
type VoteLockingPeriod = VoteLockingPeriod; // 投票锁定周期为7天
type WeightInfo = pallet_conviction_voting::weights::SubstrateWeight<Runtime>;
}
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// 其他pallet...
ConvictionVoting: pallet_conviction_voting::{Pallet, Call, Storage, Event<T>},
}
);
// 测试代码示例
#[test]
fn test_conviction_voting_workflow() {
use frame_support::{assert_ok, traits::Currency};
use pallet_conviction_voting::{Vote, Conviction};
// 初始化测试环境
ExtBuilder::default().build().execute_with(|| {
// 创建几个测试账户
let alice = 1;
let bob = 2;
let charlie = 3;
let dave = 4;
let eve = 5;
// 给账户分配初始余额
let _ = Balances::make_free_balance_be(&alice, 100_000_000);
let _ = Balances::make_free_balance_be(&bob, 100_000_000);
let _ = Balances::make_free_balance_be(&charlie, 100_000_000);
let _ = Balances::make_free_balance_be(&dave, 100_000_000);
let _ = Balances::make_free_balance_be(&eve, 100_000_000);
// 1. Alice发起提案
let proposal = Box::new(
frame_system::Call::remark {
remark: b"Upgrade runtime version".to_vec()
}.into()
);
assert_ok!(ConvictionVoting::propose(
RuntimeOrigin::signed(alice),
proposal,
10_000_000, // 保证金
32, // 描述长度
));
// 2. Bob投票支持,使用6x乘数
assert_ok!(ConvictionVoting::vote(
RuntimeOrigin::signed(bob),
0, // 提案索引
Vote {
aye: true,
conviction: Conviction::Locked6x
},
50_000_000, // 投票金额
));
// 3. Charlie委托投票权给Dave
assert_ok!(ConvictionVoting::delegate(
RuntimeOrigin::signed(charlie),
0, // 提案类
dave,
Conviction::Locked2x,
30_000_000, // 委托金额
));
// 模拟投票期结束
run_to_block(VoteLockingPeriod::get());
// 4. Eve执行通过的提案
let proposal_hash = BlakeTwo256::hash_of(&frame_system::Call::remark {
remark: b"Upgrade runtime version".to_vec()
});
assert_ok!(ConvictionVoting::enact_proposal(
RuntimeOrigin::signed(eve),
proposal_hash,
0, // 提案索引
));
});
}
注意事项
- 锁定代币在锁定期间无法转移
- 不同的信念级别会影响投票权重和代币锁定时间
- 委托投票可以随时取消
- 提案执行前需要确保投票期已结束且提案通过
通过pallet-conviction-voting
,Substrate区块链可以实现复杂的链上治理机制,让代币持有者根据自己对提案的信念强度参与决策。