Rust链上治理模块pallet-collective的使用,实现Substrate区块链多签账户管理与集体决策功能
Rust链上治理模块pallet-collective的使用,实现Substrate区块链多签账户管理与集体决策功能
概述
Collective系统允许一组账户ID的成员通过两种专门来源的调度调用表达他们的集体意愿。成员资格可以通过两种方式提供:
- 直接使用Root可调度的
set_members
函数 - 间接通过实现
ChangeMembers
trait
主要功能
-
成员管理:
- 成员数量应保持在
MaxMembers
以下 - 可以设置"prime"成员来帮助基于链配置确定默认投票行为
- 成员数量应保持在
-
投票机制:
- 投票通过包含提案(dispatchable)和所需批准数的动议进行
- 动议对成员开放投票至少
MotionDuration
指定的时间 - 如果达到所需批准数,动议关闭并执行
- 如果投票期间未达到批准数,任何账户可以调用
close
强制结束动议
-
Prime成员投票:
- 如果定义了prime成员,他们的投票将代替任何弃权票
- 如果计入新票数后有足够批准,提案将执行
- 如果没有足够票数或未设置prime成员,动议将被丢弃而不执行
完整示例代码
// runtime/src/lib.rs
// 引入必要的依赖
use frame_support::{parameter_types, traits::Everything};
use frame_system::EnsureRoot;
use sp_runtime::traits::AccountIdConversion;
// 定义参数类型
parameter_types! {
pub const MotionDuration: BlockNumber = 7 * DAYS;
pub const MaxProposals: u32 = 100;
pub const MaxMembers: u32 = 100;
}
// 实现collective pallet配置
impl pallet_collective::Config for Runtime {
type Event = Event;
type Proposal = Call;
type MotionDuration = MotionDuration;
type MaxProposals = MaxProposals;
type MaxMembers = MaxMembers;
type DefaultVote = pallet_collective::PrimeDefaultVote;
type WeightInfo = ();
}
// 扩展Collective模块功能
pub mod collective_ext {
use super::*;
// Collective扩展模块
pub struct CollectiveExt;
impl CollectiveExt {
// 初始化Council成员
pub fn initialize_council(initial_members: Vec<AccountId>) -> DispatchResult {
// 使用root权限设置初始成员
Collective::set_members(
frame_system::RawOrigin::Root.into(),
initial_members,
None,
)
}
// 提交提案(带阈值计算)
pub fn submit_proposal(
origin: OriginFor<T>,
proposal: Box<T::Proposal>,
) -> DispatchResult {
// 获取当前成员数
let member_count = Collective::members().len() as MemberCount;
// 计算简单多数阈值
let threshold = member_count / 2 + 1;
Collective::propose(origin, threshold, proposal)
}
// 批量投票(测试用)
pub fn batch_vote(
voters: Vec<AccountId>,
proposal_hash: T::Hash,
index: ProposalIndex,
approve: bool,
) -> DispatchResult {
for voter in voters {
Collective::vote(
RawOrigin::Signed(voter).into(),
proposal_hash,
index,
approve,
)?;
}
Ok(())
}
}
}
// 测试示例
#[cfg(test)]
mod tests {
use super::*;
use frame_support::assert_ok;
#[test]
fn test_collective_workflow() {
// 初始化测试环境
new_test_ext().execute_with(|| {
// 创建测试账户
let alice = 1;
let bob = 2;
let charlie = 3;
let dave = 4;
// 1. 初始化成员
assert_ok!(CollectiveExt::initialize_council(vec![alice, bob, charlie]));
// 2. 创建转账提案
let transfer_call = Box::new(Call::Balances(
pallet_balances::Call::transfer { dest: dave, value: 100 }
));
// 3. 提交提案(需要2/3成员批准)
assert_ok!(Collective::propose(
RawOrigin::Signed(alice).into(),
2, // 阈值
transfer_call
));
// 获取提案哈希和索引
let proposal_hash = Collective::proposals()[0].hash();
let proposal_index = 0;
// 4. 成员投票
assert_ok!(Collective::vote(
RawOrigin::Signed(bob).into(),
proposal_hash,
proposal_index,
true // 赞成
));
// 5. 模拟区块前进(让投票期结束)
run_to_block(MotionDuration::get());
// 6. 验证提案是否执行
assert_eq!(Balances::free_balance(dave), 100);
});
}
}
配置参数建议
parameter_types! {
pub const MotionDuration: BlockNumber = 7 * DAYS;
pub const MaxProposals: u32 = 100;
pub const MaxMembers: u32 = 100;
}
高级功能实现
- Prime成员设置与使用
// 设置prime成员
pub fn demo_prime_usage() {
let alice = account_key("Alice");
let bob = account_key("Bob");
// 1. 设置prime成员
Collective::set_prime(RawOrigin::Root.into(), alice.clone()).unwrap();
// 2. 创建提案
let proposal = Box::new(Call::System(frame_system::Call::remark { remark: vec![1, 2, 3] }));
Collective::propose(RawOrigin::Signed(bob.clone()).into(), 1, proposal).unwrap();
// 3. prime成员的投票将作为默认投票
// 不需要显式投票,提案会自动通过
}
- 自定义成员变更回调
// 实现ChangeMembers回调
impl pallet_collective::ChangeMembers<AccountId> for Runtime {
fn change_members_sorted(
incoming: &[AccountId],
outgoing: &[AccountId],
new: &[AccountId],
) {
// 记录成员变更日志
frame_system::Pallet::<Runtime>::note_offchain_event(
b"collective/members_changed"
);
// 更新相关的存储项
SomeOtherModule::update_members(new);
}
}
- 自定义投票策略
// 使用更复杂的投票策略
impl pallet_collective::Config for Runtime {
// ...其他配置...
type DefaultVote = pallet_collective::MoreThanMajorityThenPrimeDefaultVote;
}
实际应用建议
- DAO治理场景:
// 创建DAO提案
let dao_proposal = Box::new(Call::Dao(
pallet_dao::Call::allocate_funds {
project: account_key("ProjectX"),
amount: 10_000,
}
));
// 提交到collective
Collective::propose(
Origin::signed(dao_leader),
3, // 需要3/5成员同意
dao_proposal
);
- 多签钱包场景:
// 多签转账流程
let multi_sig_transfer = Box::new(Call::Balances(
pallet_balances::Call::transfer_multi_sig {
dest: account_key("Vendor"),
value: 5_000,
other_signers: vec![bob, charlie],
}
));
// 需要所有成员同意
let threshold = Collective::members().len() as u32;
Collective::propose(
Origin::signed(alice),
threshold,
multi_sig_transfer
);
注意事项
- 成员数量应保持合理以控制权重计算
- 投票持续时间(
MotionDuration
)应根据链上活动频率设置 - 提案阈值设置应考虑安全性和效率的平衡
- Prime成员可以帮助解决投票僵局
License: Apache-2.0
1 回复
Rust链上治理模块pallet-collective的使用:实现Substrate区块链多签账户管理与集体决策功能
概述
pallet-collective
是Substrate框架中的一个重要治理模块,它允许区块链网络实现集体决策和多签账户管理功能。这个模块模拟了现实世界中的董事会或委员会决策过程,非常适合用于链上治理场景。
主要功能
- 多账户集体决策机制
- 提案提交和投票系统
- 可配置的投票阈值
- 成员管理和权限控制
完整示例demo
下面是一个完整的pallet-collective使用示例,展示了如何从初始化到提案执行的完整流程:
// runtime/src/lib.rs
// 1. 引入必要依赖
use frame_support::{parameter_types, traits::Contains};
use frame_system::EnsureRoot;
use sp_runtime::traits::AccountIdConversion;
// 2. 定义参数类型
parameter_types! {
pub const MotionDuration: BlockNumber = 7 * DAYS; // 提案有效期为7天
pub const MaxProposals: u32 = 100; // 最大提案数100
pub const MaxMembers: u32 = 10; // 最大成员数10
}
// 3. 实现pallet-collective配置
impl pallet_collective::Config for Runtime {
type Event = Event;
type Proposal = Call; // 提案类型为Runtime Call
type DefaultVote = pallet_collective::PrimeDefaultVote; // 默认投票规则
type MotionDuration = MotionDuration; // 提案有效期
type MaxProposals = MaxProposals; // 最大提案数
type MaxMembers = MaxMembers; // 最大成员数
type WeightInfo = pallet_collective::weights::SubstrateWeight<Runtime>;
}
// 4. 创世块配置
#[cfg(feature = "std")]
impl pallet_collective::GenesisConfig<Runtime> {
pub fn new() -> Self {
// 初始成员集合
let initial_members = vec![
AccountId::from(1),
AccountId::from(2),
AccountId::from(3),
];
pallet_collective::GenesisConfig {
members: initial_members,
phantom: Default::default()
}
}
}
// 5. 测试用例
#[test]
fn test_collective_workflow() {
new_test_ext().execute_with(|| {
// 初始化成员
let members = vec![1, 2, 3];
Collective::initialize_members(&members);
// 创建提案
let proposal = Box::new(
Call::System(
frame_system::Call::remark {
remark: vec![1, 2, 3]
}
)
);
let proposal_len = proposal.using_encoded(|p| p.len() as u32);
// 提交提案 (需要至少2/3多数同意)
assert_ok!(Collective::propose(
RuntimeOrigin::signed(1),
2,
proposal.clone(),
proposal_len
));
// 获取提案哈希
let proposal_hash = BlakeTwo256::hash_of(&proposal);
// 成员2投票赞成
assert_ok!(Collective::vote(
RuntimeOrigin::signed(2),
proposal_hash,
0,
true
));
// 成员3投票赞成
assert_ok!(Collective::vote(
RuntimeOrigin::signed(3),
proposal_hash,
0,
true
));
// 执行提案
assert_ok!(Collective::close(
RuntimeOrigin::signed(1),
proposal_hash,
0,
Weight::max_value(),
proposal_len
));
});
}
// 6. 自定义投票规则实现
impl pallet_collective::Config for Runtime {
// 使用绝对多数决(超过50%同意)
type DefaultVote = pallet_collective::MoreThanMajorityThenPrimeDefaultVote;
// 其他配置保持不变...
}
实际应用场景
- 链上治理:DAO组织的决策过程
- 多签钱包:需要多个签名才能执行交易
- 基金会资金管理:重大资金动用需要集体批准
- 协议升级:关键升级需要委员会投票通过
最佳实践
- 合理设置集体成员数量,避免过大导致决策效率低下
- 根据安全要求选择适当的投票阈值
- 对重要操作设置较长的投票持续时间
- 定期轮换集体成员以提高去中心化程度
注意事项
- 提案执行时需要提供准确的权重预估
- 成员变动会影响现有提案的投票阈值计算
- 大尺寸提案可能消耗较多存储资源
- 需要妥善处理投票平局的情况
通过合理配置和使用pallet-collective
,你可以为Substrate区块链构建灵活强大的治理系统,满足各种多签管理和集体决策需求。