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
);
});
}
}
使用说明
- 首先需要将pallet-bags-list添加到runtime中:
impl pallet_bags_list::Config for Runtime {
type NodeWeight = u64; // 使用u64作为权重类型
type MaxNodes = ConstU32<1000>; // 最大节点数为1000
type RuntimeEvent = RuntimeEvent;
}
- 然后可以在runtime构建器中添加该pallet:
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ...其他pallet...
BagsList: pallet_bags_list,
}
);
- 使用时可以通过调用以下方法管理节点权重:
// 更新节点权重
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 回复
以下是基于提供内容的完整示例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
}
);
这个完整示例展示了:
- 如何配置
pallet-bags-list
和它与staking模块的集成 - 基本操作包括添加、更新和移除节点
- 查询操作如获取排序后的节点列表和迭代特定范围的节点
- 自定义袋子边界阈值来优化性能
使用时需要注意:
- 确保staking模块正确配置以提供节点权重
- 根据实际节点数量和权重分布调整袋子边界
- 权重更新操作可能需要考虑性能影响