Rust区块链插件库pallet-collator-selection的使用,实现波卡平行链节点选择与治理功能

Rust区块链插件库pallet-collator-selection的使用,实现波卡平行链节点选择与治理功能

安装

在项目目录中运行以下Cargo命令:

cargo add pallet-collator-selection

或者在Cargo.toml中添加以下行:

pallet-collator-selection = "23.0.0"

示例代码

以下是如何使用pallet-collator-selection实现波卡平行链节点选择与治理功能的完整示例:

// 引入必要的依赖和模块
use frame_support::{
    traits::{Currency, OnInitialize},
    weights::Weight,
};
use frame_system::Config as SystemConfig;
use pallet_collator_selection::{Config, Error};
use sp_runtime::traits::Convert;
use sp_std::prelude::*;

// 定义Runtime配置
pub trait Config: frame_system::Config {
    /// 用于抵押的货币类型
    type Currency: Currency<Self::AccountId>;
    
    /// 事件类型
    type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
    
    /// 将金额转换为权重
    type CurrencyToVote: Convert<BalanceOf<Self>, VoteWeight>;
    
    /// 候选人数量上限
    #[pallet::constant]
    type MaxCandidates: Get<u32>;
    
    /// 候选人最低抵押金额
    #[pallet::constant]
    type MinCollatorStake: Get<BalanceOf<Self>>;
    
    /// 最大候选人数
    #[pallet::constant]
    type MaxInvulnerables: Get<u32>;
    
    /// 踢出不活跃的收集者
    type KickThreshold: Get<Self::BlockNumber>;
}

// 实现收集者选择模块
#[frame_support::pallet]
pub mod pallet {
    use super::*;
    
    #[pallet::config]
    pub trait Config: frame極system::Config {
        // 同上配置
    }
    
    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);
    
    #[pallet::storage]
    #[pallet::getter(fn candidates)]
    pub type Candidates<T: Config> = StorageValue<_, Vec<T::AccountId>, ValueQuery>;
    
    #[pallet::storage]
    #[pallet::getter(fn invulnerables)]
    pub type Invulnerables<T: Config> = StorageValue<_, Vec<T::AccountId>, ValueQuery>;
    
    #[pallet::event]
    #[pallet::metadata(T::AccountId = "AccountId")]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        /// 新的收集者被添加
        NewInvulnerable(T::AccountId),
        /// 收集者被移除
        InvulnerableRemoved(T::AccountId),
        /// 新的候选人
        CandidateAdded(T::AccountId),
        /// 候选人被移除
        CandidateRemoved(T::AccountId),
    }
    
    #[pallet::error]
    pub enum Error<T> {
        /// 账户已经是收集者
        AlreadyInvulnerable,
        /// 账户已经是候选人
        AlreadyCandidate,
        /// 账户不是收集者
        NotInvulnerable,
        /// 账户不是候选人
        NotCandidate,
        /// 候选人数量已达上限
        TooManyCandidates,
        /// 抵押金额不足
        InsufficientStake,
    }
    
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        /// 添加固定收集者
        #[pallet::weight(10_000)]
        pub fn add_invulnerable(origin: OriginFor<T>, account: T::AccountId) -> DispatchResult {
            ensure_root(origin)?;
            
            let mut invulnerables = Self::invulnerables();
            if invulnerables.contains(&account) {
                return Err(Error::<T>::AlreadyInvulnerable.into());
            }
            
            invulnerables.push(account.clone());
            <Invulnerables<T>>::put(invulnerables);
            
            Self::deposit_event(Event::NewInvulnerable(account));
            Ok(().into())
        }
        
        /// 移除固定收集者
        #[pallet::weight极10_000)]
        pub fn remove_invulnerable(origin: OriginFor<T>, account: T::AccountId) -> DispatchResult {
            ensure_root(origin)?;
            
            let mut invulnerables = Self::invulnerables();
            if !invulnerables.contains(&account) {
                return Err(Error::<T>::NotInvulnerable.into());
            }
            
            invulnerables.retain(|a| a != &account);
            <Invulnerables<T>>::put(invulnerables);
            
            Self::deposit_event(Event::InvulnerableRemoved(account));
            Ok(().into())
        }
        
        /// 注册为候选人
        #[pallet::weight(10_000)]
        pub fn register_as_candidate(origin: OriginFor<T>) -> DispatchResult {
            let who = ensure_signed(origin)?;
            
            // 检查是否已经是候选人
            let mut candidates = Self::candidates();
            if candidates.contains(&who) {
                return Err(Error::<T>::AlreadyCandidate.into());
            }
            
            // 检查候选人数量限制
            if candidates.len() >= T::MaxCandidates::get() as usize {
                return Err(Error::<T>::TooManyCandidates.into());
            }
            
            // 检查抵押金额是否足够
            let stake = T::Currency::free_balance(&who);
            if stake < T::MinCollatorStake::get() {
                return Err(Error::<极T>::InsufficientStake.into());
            }
            
            candidates.push(who.clone());
            <Candidates<T>>::put(candidates);
            
            Self::deposit_event(Event::CandidateAdded(who));
            Ok(().into())
        }
        
        /// 退出候选人
        #[pallet::weight(10_000)]
        pub fn leave_candidates(origin: OriginFor<T>) -> DispatchResult {
            let who = ensure_signed(origin)?;
            
            let mut candidates = Self::candidates();
            if !candidates.contains(&who) {
                return Err(Error::<T>::NotCandidate.into());
            }
            
            candidates.retain(|a| a != &who);
            <Candidates<T>>::put(candidates);
            
            Self::deposit_event(Event::CandidateRemoved(who));
            Ok(().into())
        }
    }
    
    #[pallet::hooks]
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
        fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
            // 在此可以执行定期清理或更新收集者列表的逻辑
            0
        }
    }
}

// 实现收集者选择逻辑
impl<T: Config> pallet_collator_selection::Config for Runtime {
    type Event = Event;
    type Currency = Balances;
    type CurrencyToVote = ();
    type MaxCandidates = MaxCandidates;
    type MinCollatorStake = MinCollatorStake;
    type MaxInvulnerables = MaxInvulnerables;
    type KickThreshold = KickThreshold;
}

// 在Runtime构建器中集成模块
construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        // 其他模块...
        CollatorSelection: pallet_collator_selection::{Module, Call, Storage, Event<T>},
    }
);

功能说明

  1. 固定收集者(Invulnerables):由治理(root)直接指定的永久收集者,通常用于链的初始阶段
  2. 候选人(Candidates):通过抵押代币成为候选收集者的账户,可以被选为活跃收集者
  3. 抵押要求:候选人需要满足最低抵押金额要求
  4. 动态调整:治理可以随时添加或移除固定收集者
  5. 自动选择:系统会根据抵押金额等因素自动选择活跃收集者

使用场景

  1. 链启动时添加初始收集者:
CollatorSelection::add_invulnerable(origin, account);
  1. 用户注册为收集者候选人:
CollatorSelection::register_as_candidate(origin);
  1. 治理移除不活跃收集者:
CollatorSelection::remove_invulnerable(origin, account);
  1. 候选人退出:
CollatorSelection::leave_candidates(origin);

这个示例展示了如何在波卡平行链中实现收集者(节点)的选择与治理功能,包括固定收集者的管理、候选人的注册与退出等核心功能。


1 回复

Rust区块链插件库pallet-collator-selection的使用指南

概述

pallet-collator-selection是Substrate框架中的一个重要模块,专门用于波卡(Polkadot)平行链中的验证人(collator)选择与管理。该模块实现了平行链节点的选择机制和治理功能,是构建波卡平行链的关键组件。

主要功能

  1. 管理平行链验证人集合
  2. 实现验证人选择算法
  3. 提供验证人治理功能
  4. 处理验证人权益和奖励

使用方法

1. 引入依赖

[dependencies]
pallet-collator-selection = { git = "https://github.com/paritytech/substrate", branch = "polkadot-vX.Y.Z" }

2. 配置Runtime

impl pallet_collator_selection::Config for Runtime {
    type Event = Event;
    type Currency = Balances;
    type UpdateOrigin = EnsureRoot<AccountId>;
    type KickThreshold = KickThreshold;
    type ValidatorId = <Self as frame_system::Config>::AccountId;
    type ValidatorIdOf = pallet_collator_selection::IdentityCollator;
    type WeightInfo = ();
}

3. 添加到Runtime

construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        CollatorSelection: pallet_collator_selection::{Module, Call, Storage, Event<T>},
    }
);

完整示例代码

// 1. 定义Runtime配置
pub struct Runtime;
pub type AccountId = u64;
pub type Balance = u128;

// 模拟一些基础类型
mod frame_system {
    pub trait Config {
        type AccountId;
    }
}

mod pallet_balances {
    pub trait Config {
        type Balance;
    }
}

// 2. 实现必要的trait
impl frame_system::Config for Runtime {
    type AccountId = AccountId;
}

impl pallet_balances::Config for Runtime {
    type Balance = Balance;
}

// 3. 定义参数类型
parameter_types! {
    pub const KickThreshold: u32 = 5;
}

// 4. 实现collator-selection配置
impl pallet_collator_selection::Config for Runtime {
    type Event = ();
    type Currency = pallet_balances;
    type UpdateOrigin = frame_system::EnsureRoot<AccountId>;
    type KickThreshold = KickThreshold;
    type ValidatorId = AccountId;
    type ValidatorIdOf = pallet_collator_selection::IdentityCollator;
    type WeightInfo = ();
}

// 5. 测试用例
#[test]
fn test_collator_selection() {
    use pallet_collator_selection::{Call, Pallet as CollatorSelection};
    
    // 初始化测试环境
    let mut ext = sp_io::TestExternalities::new_empty();
    
    ext.execute_with(|| {
        // 设置初始验证人
        let initial_collators = vec![1, 2, 3]; // 账户ID
        
        // 设置候选人保证金
        let _ = Call::<Runtime>::set_candidacy_bond(100);
        
        // 设置期望候选人数量
        let _ = Call::<Runtime>::set_desired_candidates(3);
        
        // 设置不可移除的验证人
        let _ = Call::<Runtime>::set_invulnerables(initial_collators.clone());
        
        // 查询验证人
        let collators = CollatorSelection::<Runtime>::invulnerables();
        assert_eq!(collators, initial_collators);
        
        // 账户4申请成为候选人
        let _ = Call::<Runtime>::register_as_candidate();
        
        // 查询候选人
        let candidates = CollatorSelection::<Runtime>::candidates();
        assert!(candidates.len() > 0);
        
        // 移除验证人(需要root权限)
        let _ = Call::<Runtime>::remove_invulnerable(1);
    });
}

事件监听示例

// 监听collator-selection事件
pub fn handle_collator_events(event: pallet_collator_selection::Event<Runtime>) {
    match event {
        pallet_collator_selection::Event::NewInvulnerable(collator) => {
            println!("新的验证人加入: {:?}", collator);
        },
        pallet_collator_selection::Event::InvulnerableRemoved(collator) => {
            println!("验证人被移除: {:?}", collator);
        },
        _ => {}
    }
}

最佳实践

  1. 合理设置desired_candidates数量,平衡去中心化和性能
  2. 实现适当的抵押机制防止女巫攻击
  3. 定期轮换验证人以增强安全性
  4. 监控验证人性能并移除表现不佳的节点
回到顶部