Rust区块链索引管理库pallet-indices的使用:高效账户索引与Substrate链上ID管理

Rust区块链索引管理库pallet-indices的使用:高效账户索引与Substrate链上ID管理

索引是地址的简短形式。该模块处理为新创建的账户分配索引。

许可证:Apache-2.0

安装

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

cargo add pallet-indices

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

pallet-indices = "42.0.0"

完整示例代码

以下是一个更完整的pallet-indices使用示例,展示了账户索引的生命周期管理:

// 导入必要的模块和库
use frame_support::{
    decl_module, decl_storage, decl_event, decl_error, 
    dispatch, ensure, traits::Currency
};
use frame_system::{self as system, ensure_signed, ensure_root};
use sp_runtime::traits::{StaticLookup, LookupError, Zero};
use pallet_indices::{self as indices, Index};

/// 配置模块的trait
pub trait Trait: system::Trait + indices::Trait {
    /// 事件类型
    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
    /// 货币类型
    type Currency: Currency<Self::AccountId>;
}

// 存储定义
decl_storage! {
    trait Store for Module<T: Trait> as IndexDemo {
        /// 账户到值的映射
        AccountData get(fn account_data): 
            map hasher(blake2_128_concat) T::AccountId => u128;
        
        /// 系统保留的索引
        ReservedIndices get(fn reserved_indices): Vec<Index>;
    }
}

// 事件定义
decl_event!(
    pub enum Event<T> 
    where 
        AccountId = <T as system::Trait>::AccountId,
        Index = indices::Index,
    {
        /// 新索引已分配
        NewIndexAssigned(AccountId, Index),
        /// 索引已冻结
        IndexFrozen(Index),
        /// 索引已解冻
        IndexThawed(Index),
        /// 索引已转移
        IndexTransferred(Index, AccountId),
        /// 数据已更新
        DataUpdated(AccountId, u128),
    }
);

// 错误定义
decl_error! {
    pub enum Error for Module<T: Trait> {
        /// 账户已有索引
        AlreadyIndexed,
        /// 索引无效
        InvalidIndex,
        /// 余额不足
        InsufficientBalance,
    }
}

// 模块实现
decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        type Error = Error<T>;
        
        fn deposit_event() = default;

        /// 为账户分配新索引
        #[weight = 10_000]
        pub fn assign_index(
            origin,
            who: <T::Lookup as StaticLookup>::Source
        ) -> dispatch::DispatchResult {
            let sender = ensure_signed(origin)?;
            
            // 解析目标账户
            let who = T::Lookup::lookup(who)?;
            
            // 检查账户是否已有索引
            if indices::Module::<T>::lookup_index(&who).is_ok() {
                return Err(Error::<T>::AlreadyIndexed.into());
            }
            
            // 分配新索引
            let index = indices::Module::<T>::claim(sender.clone(), who.clone())?;
            
            // 发出事件
            Self::deposit_event(RawEvent::NewIndexAssigned(who, index));
            
            Ok(())
        }

        /// 冻结指定索引
        #[weight = 10_000]
        pub fn freeze_index(origin, index: Index) -> dispatch::DispatchResult {
            ensure_root(origin)?;
            
            // 冻结索引
            indices::Module::<T>::freeze(index)?;
            
            // 发出事件
            Self::deposit_event(RawEvent::IndexFrozen(index));
            
            Ok(())
        }

        /// 更新账户数据并检查索引
        #[weight = 10_000]
        pub fn update_data(
            origin, 
            value: u128
        ) -> dispatch::DispatchResult {
            let who = ensure_signed(origin)?;
            
            // 检查账户是否有索引
            let index = indices::Module::<T>::lookup_index(&who)?;
            
            // 检查索引是否被冻结
            ensure!(
                !indices::Module::<T>::is_frozen(&index),
                Error::<T>::InvalidIndex
            );
            
            // 更新存储
            <AccountData<T>>::insert(&who, value);
            
            // 发出事件
            Self::deposit_event(RawEvent::DataUpdated(who, value));
            
            Ok(())
        }

        /// 保留系统索引
        #[weight = 10_000]
        pub fn reserve_system_index(origin, index: Index) -> dispatch::DispatchResult {
            ensure_root(origin)?;
            
            // 保留索引
            <ReservedIndices>::mutate(|v| v.push(index));
            
            Ok(())
        }
    }
}

// 实现一些辅助函数
impl<T: Trait> Module<T> {
    /// 检查索引是否被保留
    pub fn is_index_reserved(index: &Index) -> bool {
        <ReservedIndices>::get().contains(index)
    }
    
    /// 获取账户数据
    pub fn get_account_data(who: &T::AccountId) -> Option<u128> {
        <AccountData<T>>::get(who)
    }
}

功能说明

  1. 完整的索引生命周期管理:包含索引分配、冻结、解冻等操作
  2. 权限控制:部分功能限制为root账户使用
  3. 状态检查:包含索引有效性检查、保留状态检查等
  4. 存储管理:展示了如何存储和管理与索引关联的数据
  5. 事件系统:详细的事件记录机制

进阶用法扩展

// 获取账户的索引
let account_id = // 获取账户ID;
match indices::Module::<T>::lookup_index(&account_id) {
    Ok(index) => {
        log::info!("Account {} has index {}", account_id, index);
    },
    Err(_) => {
        log::warn!("Account {} has no index assigned", account_id);
    }
}

// 批量分配索引
for account in new_accounts.iter() {
    let _ = indices::Module::<T>::claim(sender.clone(), account.clone());
}

// 检查并解冻索引
if indices::Module::<T>::is_frozen(&index) {
    indices::Module::<T>::thaw(index)?;
}

// 管理员强制转移索引
indices::Module::<T>::force_assign(admin, index, new_account)?;

这个完整示例展示了pallet-indices在实际应用中的综合使用方式,包括索引管理、权限控制和数据存储等核心功能。


1 回复

以下是基于您提供的pallet-indices内容整理的完整示例demo,先展示内容中的示例,然后提供完整的实现代码:

内容中提供的示例回顾

  1. Runtime配置示例
impl pallet_indices::Config for Runtime {
    type AccountIndex = u32;
    type Currency = Balances;
    type Deposit = ConstU128<1>;
    type Event = Event;
    type WeightInfo = ();
}
  1. 索引分配示例
let call = pallet_indices::Call::claim { index: desired_index };
let origin = Origin::signed(caller);
RuntimeCall::Indices(call).dispatch(origin)?;
  1. 索引查询示例
// 通过账户查索引
let index = pallet_indices::Module::<Runtime>::lookup_index(account_id);
// 通过索引查账户
let account_id = pallet_indices::Module::<Runtime>::lookup_account(index);

完整示例Demo

// runtime/src/lib.rs

// 1. 引入必要依赖
use frame_support::{pallet_prelude::*, traits::Currency};
use sp_runtime::traits::StaticLookup;

// 2. 配置Runtime
pub type AccountIndex = u32;

impl pallet_indices::Config for Runtime {
    type AccountIndex = AccountIndex;
    type Currency = Balances; // 使用Balances作为货币类型
    type Deposit = ConstU128<100>; // 设置索引押金为100
    type Event = Event;
    type WeightInfo = pallet_indices::weights::SubstrateWeight<Runtime>;
}

// 3. 创建测试模块
#[cfg(test)]
mod tests {
    use super::*;
    use frame_support::assert_ok;
    use sp_core::sr25519;
    use sp_runtime::{AccountId32, DispatchResult};
    
    // 测试账户
    fn test_account(id: u8) -> AccountId32 {
        AccountId32::new([id; 32])
    }
    
    #[test]
    fn test_indices_workflow() -> DispatchResult {
        // 初始化测试环境
        let mut ext = sp_io::TestExternalities::new_empty();
        ext.execute_with(|| {
            // 创建测试账户
            let alice = test_account(1);
            let bob = test_account(2);
            
            // 4. 测试索引分配
            let desired_index = 1u32;
            assert_ok!(
                Indices::claim(
                    RuntimeOrigin::signed(alice.clone()),
                    desired_index
                )
            );
            
            // 验证分配结果
            assert_eq!(
                Indices::lookup_index(&alice),
                Some(desired_index)
            );
            assert_eq!(
                Indices::lookup_account(desired_index),
                Some(alice.clone())
            );
            
            // 5. 测试索引转让
            assert_ok!(
                Indices::transfer(
                    RuntimeOrigin::signed(alice.clone()),
                    bob.clone(),
                    desired_index
                )
            );
            
            // 验证转让结果
            assert_eq!(
                Indices::lookup_index(&bob),
                Some(desired_index)
            );
            assert_eq!(
                Indices::lookup_account(desired_index),
                Some(bob.clone())
            );
            
            // 6. 测试索引回收
            assert_ok!(
                Indices::free(
                    RuntimeOrigin::signed(bob.clone()),
                    desired_index
                )
            );
            
            // 验证回收结果
            assert_eq!(
                Indices::lookup_index(&bob),
                None
            );
            assert_eq!(
                Indices::lookup_account(desired_index),
                None
            );
        });
        
        Ok(())
    }
}

// 7. 批量索引管理实现
pub struct IndexManager;
impl IndexManager {
    /// 批量分配连续索引
    pub fn batch_assign(accounts: &[AccountId]) -> DispatchResult {
        for (i, account) in accounts.iter().enumerate() {
            let index = i as u32;
            Indices::claim(
                RuntimeOrigin::signed(account.clone()),
                index
            )?;
        }
        Ok(())
    }
    
    /// 清理未使用的索引
    pub fn cleanup_unused() {
        for (index, account) in Indices::enum_indices() {
            if Balances::free_balance(&account).is_zero() {
                let _ = Indices::free(
                    RuntimeOrigin::signed(account),
                    index
                );
            }
        }
    }
}

关键功能说明

  1. 测试覆盖场景

    • 索引分配(claim)
    • 索引查询(lookup)
    • 索引转让(transfer)
    • 索引回收(free)
  2. 批量管理工具

    • batch_assign 实现批量分配连续索引
    • cleanup_unused 自动回收零余额账户的索引
  3. 最佳实践实现

    • 使用u32作为索引类型(适合大多数场景)
    • 设置合理押金(示例中为100)
    • 提供自动清理机制

这个完整示例展示了从Runtime配置到实际业务使用的完整流程,包含测试用例和工具类实现,可以直接集成到Substrate链中使用。

回到顶部