Rust区块链治理工具pallet-bags-list的使用,Substrate模块pallet-bags-list实现链上节点权重动态管理

Rust区块链治理工具pallet-bags-list的使用,Substrate模块pallet-bags-list实现链上节点权重动态管理

安装

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

cargo add pallet-bags-list

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

pallet-bags-list = "41.0.0"

示例代码

以下是使用pallet-bags-list实现链上节点权重动态管理的完整示例:

// 导入必要的模块
use frame_support::{
    pallet_prelude::*,
    traits::Get,
};
use frame_system::pallet_prelude::*;
use sp_std::prelude::*;

// 定义pallet配置
pub trait Config: frame_system::Config {
    /// 节点权重类型
    type NodeWeight: Member + Parameter + Default + Copy + MaybeSerializeDeserialize + MaxEncodedLen;
    
    /// 最大节点数
    type MaxNodes: Get<极光;
    
    /// 事件类型
    type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}

// 定义存储项
#[pallet::storage]
#[pallet::getter(fn nodes)]
pub(super) type Nodes<T: Config> = StorageMap<
    _,
    Blake2_128Concat,
    T::AccountId,
    T::NodeWeight,
    OptionQuery
>;

// 定义事件
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
    /// 节点权重已更新
    NodeWeightUpdated { account: T::AccountId, weight: T::NodeWeight },
    /// 节点已被移除
    NodeRemoved { account: T::AccountId },
}

// 定义错误
#[pallet::error]
pub enum Error<T> {
    /// 超出最大节点数限制
    TooManyNodes,
    /// 节点不存在
    NodeNotFound,
}

// 定义pallet
#[pallet::pallet]
pub struct Pallet<T>(_);

// 实现pallet
#[pallet::call]
impl<T: Config> Pallet<T> {
    /// 添加或更新节点权重
    #[pallet::call_index(0)]
    #[pallet::weight(10_000)]
    pub fn update_node_weight(
        origin: OriginFor<T>,
        account: T::AccountId,
        weight: T::NodeWeight,
    ) -> DispatchResult {
        let _ = ensure_signed(origin)?;
        
        // 检查节点数是否超过限制
        if !Nodes::<T>::contains_key(&account) && Nodes::<极光::count() >= T::MaxNodes::get() {
            return Err(Error::<T>::TooManyNodes.into());
        }
        
        // 插入或更新节点权重
        Nodes::<T>::insert(&account, weight);
        
        // 发出事件
        Self::deposit_event(Event::NodeWeightUpdated { account, weight });
        
        Ok(())
    }
    
    /// 移除节点
    #[pallet::call_index(1)]
    #[pallet::weight(10_000)]
    pub fn remove_node(
        origin: OriginFor<T>,
        account: T::AccountId,
    ) -> DispatchResult {
        let _ = ensure_signed(origin)?;
        
        // 检查节点是否存在
        if !Nodes::<T>::contains_key(&account) {
            return Err(Error::<T>::NodeNotFound.into());
        }
        
        // 移除节点
        Nodes::<T>::remove(&account);
        
        // 发出事件
        Self::deposit_event(Event::NodeRemoved { account });
        
        Ok(())
    }
}

// 实现bags-list接口
impl<T: Config> Pallet<T> {
    /// 获取所有节点及其权重
    pub fn get_all_nodes() -> Vec<(T::AccountId, T::NodeWeight)> {
        Nodes::<T>::iter().collect()
    }
    
    /// 根据权重排序的节点列表
    pub fn get_sorted_nodes() -> Vec<(T::AccountId, T::NodeWeight)> {
        let mut nodes = Self::get_all_nodes();
        nodes.sort_by(|a, b| b.1.cmp(&a.1));
        nodes
    }
}

完整示例代码

//! 完整的pallet-bags-list实现示例

#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{
    pallet_prelude::*,
    traits::Get,
};
use frame_system::pallet_prelude::*;

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use super::*;

    /// pallet配置
    #[pallet::config]
    pub trait Config: frame_system::Config {
        /// 节点权重类型(必须满足一系列trait约束)
        type NodeWeight: Parameter + Member + Default + Copy + MaybeSerializeDeserialize + MaxEncodedLen;
        
        /// 最大节点数限制
        type MaxNodes: Get<u32>;
        
        /// 事件类型
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
    }

    /// 存储节点权重的StorageMap
    #[pallet::storage]
    #[pallet::getter(fn nodes)]
    pub type Nodes<T: Config> = StorageMap<
        _,
        Blake2_128Concat,
        T::AccountId,
        T::NodeWeight,
        OptionQuery
    >;

    /// 定义pallet事件
    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        NodeWeightUpdated { account: T::AccountId, weight: T::NodeWeight },
        NodeRemoved { account: T::AccountId },
    }

    /// 定义错误类型
    #[pallet::error]
    pub enum Error<T> {
        TooManyNodes,
        NodeNotFound,
    }

    #[pallet::pallet]
    pub struct Pallet<T>(_);

    /// 实现pallet的可调用函数
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        /// 更新节点权重
        #[pallet::call_index(0)]
        #[pallet::weight(10_000)]
        pub fn update_node_weight(
            origin: OriginFor<T>,
            account: T::AccountId,
            weight: T::NodeWeight,
        ) -> DispatchResult {
            // 检查签名
            ensure_signed(origin)?;
            
            // 检查节点数限制
            if !Nodes::<T>::contains_key(&account) && Nodes::<T>::count() >= T::MaxNodes::get() {
                return Err(Error::<T>::TooManyNodes.into());
            }
            
            // 更新存储
            Nodes::<T>::insert(&account, weight);
            
            // 触发事件
            Self::deposit_event(Event::NodeWeightUpdated { account, weight });
            
            Ok(())
        }

        /// 移除节点
        #[pallet::call_index(1)]
        #[pallet::weight(10_000)]
        pub fn remove_node(
            origin: OriginFor<T>,
            account: T::AccountId,
        ) -> DispatchResult {
            ensure_signed(origin)?;
            
            ensure!(Nodes::<T>::contains_key(&account), Error::<T>::NodeNotFound);
            
            Nodes::<T>::remove(&account);
            
            Self::deposit_event(Event::NodeRemoved { account });
            
            Ok(())
        }
    }

    /// 实现额外的公共接口
    impl<T: Config> Pallet<T> {
        /// 获取所有节点
        pub fn get_all_nodes() -> Vec<(T::AccountId, T::NodeWeight)> {
            Nodes::<T>::iter().collect()
        }
        
        /// 获取按权重排序的节点列表
        pub fn get_sorted_nodes() -> Vec<(T::AccountId, T::NodeWeight)> {
            let mut nodes = Self::get_all_nodes();
            nodes.sort_by(|a, b| b.1.cmp(&a.1));
            nodes
        }
    }
}

/// 测试模块
#[cfg(test)]
mod tests {
    use super::*;
    use frame_support::{
        assert_ok, assert_noop,
        parameter_types,
        traits::{ConstU32, ConstU64},
    };
    use sp_core::H256;
    use sp_runtime::{
        traits::{BlakeTwo256, IdentityLookup},
        BuildStorage,
    };

    type Block = frame_system::mocking::MockBlock<Test>;

    // 测试运行时配置
    frame_support::construct_runtime!(
        pub enum Test
        {
            System: frame_system,
            BagsList: pallet,
        }
    );

    parameter_types! {
        pub const BlockHashCount: u64 = 250;
    }

    impl frame_system::Config for Test {
        type BaseCallFilter = frame_support::traits::Everything;
        type BlockWeights = ();
        type BlockLength = ();
        type DbWeight = ();
        type RuntimeOrigin = RuntimeOrigin;
        type RuntimeCall = RuntimeCall;
        type Nonce = u64;
        type Hash = H256;
        type Hashing = BlakeTwo256;
        type AccountId = u64;
        type Lookup = IdentityLookup<Self::AccountId>;
        type Block = Block;
        type RuntimeEvent = RuntimeEvent;
        type BlockHashCount = BlockHashCount;
        type Version = ();
        type PalletInfo = PalletInfo;
        type AccountData = ();
        type OnNewAccount = ();
        type OnKilledAccount = ();
        type SystemWeightInfo = ();
        type SS58Prefix = ();
        type OnSetCode = ();
        type MaxConsumers = ConstU32<16>;
    }

    parameter_types! {
        pub const MaxNodes: u32 = 2;
    }

    impl Config for Test {
        type NodeWeight = u64;
        type MaxNodes = MaxNodes;
        type RuntimeEvent = RuntimeEvent;
    }

    // 测试用例
    #[test]
    fn test_basic_operations() {
        new_test_ext().execute_with(|| {
            // 测试初始状态
            assert_eq!(BagsList::get_all_nodes().len(), 0);

            // 添加节点1
            assert_ok!(BagsList::update_node_weight(RuntimeOrigin::signed(1), 1, 100));
            assert_eq!(BagsList::get_all_nodes(), vec![(1, 100)]);

            // 添加节点2
            assert_ok!(BagsList::update_node_weight(RuntimeOrigin::signed(1), 2, 200));
            assert_eq!(BagsList::get_sorted_nodes(), vec![(2, 200), (1, 100)]);

            // 测试节点数限制
            assert_noop!(
                BagsList::update_node_weight(RuntimeOrigin::signed(1), 3, 300),
                Error::<Test>::TooManyNodes
            );

            // 更新节点1权重
            assert_ok!(BagsList::update_node_weight(RuntimeOrigin::signed(1), 1, 150));
            assert_eq!(BagsList::get_sorted_nodes(), vec![(1, 150), (2, 200)]);

            // 移除节点2
            assert_ok!(BagsList::remove_node(RuntimeOrigin::signed(1), 2));
            assert_eq!(BagsList::get_all_nodes(), vec![(1, 150)]);

            // 测试移除不存在的节点
            assert_noop!(
                BagsList::remove_node(RuntimeOrigin::signed(1), 99),
                Error::<Test>::NodeNotFound
            );
        });
    }
}

使用说明

  1. 首先需要将pallet-bags-list添加到runtime中:
impl pallet_bags_list::Config for Runtime {
    type NodeWeight = u64;  // 使用u64作为权重类型
    type MaxNodes = ConstU32<1000>;  // 最大节点数为1000
    type RuntimeEvent = RuntimeEvent;
}
  1. 然后可以在runtime构建器中添加该pallet:
construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        // ...其他pallet...
        BagsList: pallet_bags_list,
    }
);
  1. 使用时可以通过调用以下方法管理节点权重:
// 更新节点权重
BagsList::update_node_weight(
    RuntimeOrigin::signed(account),
    target_account,
    weight
);

// 移除节点
BagsList::remove_node(
    RuntimeOrigin::signed(account),
    target_account
);

// 获取排序后的节点列表
let sorted_nodes = BagsList::get_sorted_nodes();

功能特点

  1. 动态权重管理:允许在链上实时更新节点的权重值
  2. 自动排序:提供按权重排序的节点列表
  3. 容量限制:可配置最大节点数量
  4. 事件通知:所有操作都会发出相应的事件
  5. 权限控制:只有签名账户才能更新或移除节点

该模块适用于需要根据权重动态调整节点优先级的区块链治理场景,如验证人选举、治理投票权重分配等。


1 回复

以下是基于提供内容的完整示例demo,展示了如何在Substrate runtime中集成和使用pallet-bags-list

// runtime/src/lib.rs

// 1. 引入必要的依赖
use frame_support::{parameter_types, traits::SortedMembers};
use frame_system::EnsureRoot;
use sp_runtime::traits::AccountIdConversion;

// 2. 定义runtime配置
pub struct Runtime;

parameter_types! {
    pub const BagThresholds: &'static [u64] = &[10, 20, 50, 100, 200, 500, 1000, 2000, 5000];
}

// 3. 实现pallet_bags_list的配置
impl pallet_bags_list::Config for Runtime {
    type RuntimeEvent = RuntimeEvent;
    type WeightInfo = ();
    type ScoreProvider = Staking; // 使用staking模块提供权重
    type BagThresholds = BagThresholds;
}

// 4. 实现staking模块配置
impl pallet_staking::Config for Runtime {
    // ... 其他staking配置
    type SortedListProvider = pallet_bags_list::Pallet<Runtime>;
}

// 5. 示例使用代码
mod examples {
    use super::*;
    use pallet_bags_list::Pallet as BagsList;
    
    pub fn demo_usage() {
        let account1 = 1u64.into();
        let account2 = 2u64.into();
        
        // 添加节点
        BagsList::<Runtime>::insert(account1, 1000);
        BagsList::<Runtime>::insert(account2, 2000);
        
        // 更新权重
        BagsList::<Runtime>::update(account1, 1500);
        
        // 查询前N个节点
        let top_nodes = BagsList::<Runtime>::sorted_members()
            .take(5)
            .collect::<Vec<_>>();
        println!("Top 5 nodes: {:?}", top_nodes);
        
        // 迭代特定范围的节点
        for (node, score) in BagsList::<Runtime>::iter() {
            if score >= 1000 && score <= 2000 {
                println!("Node in range: {:?} with score {}", node, score);
            }
        }
        
        // 移除节点
        BagsList::<Runtime>::remove(account1);
    }
}

// 6. 在runtime构建中使用
construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        System: frame_system,
        BagsList: pallet_bags_list,
        Staking: pallet_staking,
        // ... 其他pallet
    }
);

这个完整示例展示了:

  1. 如何配置pallet-bags-list和它与staking模块的集成
  2. 基本操作包括添加、更新和移除节点
  3. 查询操作如获取排序后的节点列表和迭代特定范围的节点
  4. 自定义袋子边界阈值来优化性能

使用时需要注意:

  • 确保staking模块正确配置以提供节点权重
  • 根据实际节点数量和权重分布调整袋子边界
  • 权重更新操作可能需要考虑性能影响
回到顶部