Rust链上治理模块pallet-collective的使用,实现Substrate区块链多签账户管理与集体决策功能

Rust链上治理模块pallet-collective的使用,实现Substrate区块链多签账户管理与集体决策功能

概述

Collective系统允许一组账户ID的成员通过两种专门来源的调度调用表达他们的集体意愿。成员资格可以通过两种方式提供:

  1. 直接使用Root可调度的set_members函数
  2. 间接通过实现ChangeMembers trait

主要功能

  1. 成员管理

    • 成员数量应保持在MaxMembers以下
    • 可以设置"prime"成员来帮助基于链配置确定默认投票行为
  2. 投票机制

    • 投票通过包含提案(dispatchable)和所需批准数的动议进行
    • 动议对成员开放投票至少MotionDuration指定的时间
    • 如果达到所需批准数,动议关闭并执行
    • 如果投票期间未达到批准数,任何账户可以调用close强制结束动议
  3. 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;
}

高级功能实现

  1. 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成员的投票将作为默认投票
    // 不需要显式投票,提案会自动通过
}
  1. 自定义成员变更回调
// 实现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);
    }
}
  1. 自定义投票策略
// 使用更复杂的投票策略
impl pallet_collective::Config for Runtime {
    // ...其他配置...
    type DefaultVote = pallet_collective::MoreThanMajorityThenPrimeDefaultVote;
}

实际应用建议

  1. 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
);
  1. 多签钱包场景:
// 多签转账流程
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
);

注意事项

  1. 成员数量应保持合理以控制权重计算
  2. 投票持续时间(MotionDuration)应根据链上活动频率设置
  3. 提案阈值设置应考虑安全性和效率的平衡
  4. Prime成员可以帮助解决投票僵局

License: Apache-2.0


1 回复

Rust链上治理模块pallet-collective的使用:实现Substrate区块链多签账户管理与集体决策功能

概述

pallet-collective是Substrate框架中的一个重要治理模块,它允许区块链网络实现集体决策和多签账户管理功能。这个模块模拟了现实世界中的董事会或委员会决策过程,非常适合用于链上治理场景。

主要功能

  1. 多账户集体决策机制
  2. 提案提交和投票系统
  3. 可配置的投票阈值
  4. 成员管理和权限控制

完整示例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;
    
    // 其他配置保持不变...
}

实际应用场景

  1. 链上治理:DAO组织的决策过程
  2. 多签钱包:需要多个签名才能执行交易
  3. 基金会资金管理:重大资金动用需要集体批准
  4. 协议升级:关键升级需要委员会投票通过

最佳实践

  1. 合理设置集体成员数量,避免过大导致决策效率低下
  2. 根据安全要求选择适当的投票阈值
  3. 对重要操作设置较长的投票持续时间
  4. 定期轮换集体成员以提高去中心化程度

注意事项

  1. 提案执行时需要提供准确的权重预估
  2. 成员变动会影响现有提案的投票阈值计算
  3. 大尺寸提案可能消耗较多存储资源
  4. 需要妥善处理投票平局的情况

通过合理配置和使用pallet-collective,你可以为Substrate区块链构建灵活强大的治理系统,满足各种多签管理和集体决策需求。

回到顶部