Rust身份管理模块pallet-identity的使用:Substrate区块链中的去中心化身份验证与属性管理

Rust身份管理模块pallet-identity的使用:Substrate区块链中的去中心化身份验证与属性管理

Identity Pallet

概述

一个联邦命名系统,允许从指定来源添加多个注册商。注册商可以设置费用来提供身份验证服务。任何人都可以提交一个提议的身份并支付固定押金,然后请求任意数量的注册商进行审核(支付各自的费用)。注册商的判断以enum形式给出,允许多层次的意见。

有些判断被标识为"sticky",这意味着除非完全移除身份或由注册商移除,否则无法删除。判断可以代表为注册商保留的部分资金。

超级用户可以删除账户,并在此过程中没收押金。

所有账户也可以有有限数量的子账户,由所有者指定;根据定义,这些子账户具有相同的所有权,每个子账户都有独立的名称。

注册商的数量应受到限制,押金应足够大,以确保不存在可行的状态膨胀攻击。

用户名

该模块提供了用户名授权机构发行用户名的功能,这些功能独立于身份信息功能;账户可以设置:

  • 不设置用户名的身份
  • 不设置身份的用户名
  • 身份和用户名

该模块中实现的用户名功能旨在提供用户友好的账户查找。存在双向映射:“账户->用户名"和"用户名->账户”。

要授予用户名,用户名授权机构可以:

  • 通过治理获得特定数量的用户名分配,免费发行,无需与存储成本相关的押金;
  • 为每个发行的用户名支付押金(通常是相对于系统中的其他押金而言补贴的、减少的押金)。

用户可以拥有多个映射到相同AccountId的用户名,但是一个AccountId只能映射到一个用户名,称为"primary"。这个主用户名将是任何给定账户在UsernameOf映射中的查找结果。

接口

可调度函数

普通用户
  • set_identity - 设置账户的关联身份;如果尚未占用,将保留一小笔押金。
  • clear_identity - 移除账户的关联身份;押金将被退还。
  • request_judgement - 请求注册商的判断,支付费用。
  • cancel_request - 取消先前的判断请求。
  • `accept_username》 - 接受用户名授权机构发行的用户名。
  • remove_expired_approval - 移除已发行但未被接受的用户名。
  • set_primary_username - 将给定用户名设置为账户的主用户名。
  • remove_username - 在宽限期结束后移除用户名。
拥有子身份的用户
  • set_subs - 设置身份的子账户。
  • add_sub - 向身份添加子身份。
  • remove_sub - 移除身份的子身份。
  • rename_sub - 重命名身份的子身份。
  • quit_sub - 移除身份的子身份(由子身份调用)。
注册商
  • set_fee - 设置注册商给出判断所需的费用。
  • set_fields - 设置注册商在其判断中关心的字段。
  • `provide_judgement》 - 向身份提供判断。
用户名授权机构
  • set_username_for - 为给定账户设置用户名。账户必须批准它。
  • `unbind_username》 - 开始用户名的宽限期。
超级用户
  • `add_registrar》 - 向系统添加新的注册商。
  • `kill_identity》 - 强制移除关联身份;押金将丢失。
  • `add_username_authority》 - 添加具有发行用户名权限的账户。
  • `remove_username_authority》 - 移除具有发行用户名权限的账户。
  • `kill_username》 - 强制移除用户名。

许可证:Apache-2.0

完整示例代码

// 导入必要的依赖
use frame_support::{decl_module, decl_event, decl_storage, dispatch::DispatchResult};
use frame_system::{self as system, ensure_signed};
use sp_std::prelude::*;

// 定义模块配置
pub trait Config: system::Config {
    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;
}

// 定义存储项
decl_storage! {
    trait Store for Module<T: Config> as Identity {
        /// 账户身份信息映射
        IdentityOf get(fn identity_of): map hasher(blake2_128_concat) T::AccountId => Option<IdentityInfo>;
        /// 用户名到账户的映射
        UsernameOf get(fn username_of): map hasher(blake2_128_concat) Vec<u8> => Option<T::AccountId>;
    }
}

// 定义事件
decl_event!(
    pub enum Event<T> where AccountId = <T as system::Config>::AccountId {
        /// 身份设置事件
        IdentitySet(AccountId),
        /// 用户名设置事件
        UsernameSet(AccountId, Vec<u8>),
    }
);

// 定义模块
decl_module! {
    pub struct Module<T: Config> for enum Call where origin: T::Origin {
        fn deposit_event() = default;

        /// 设置账户身份信息
        #[weight = 10_000]
        pub fn set_identity(origin, info: IdentityInfo) -> DispatchResult {
            let who = ensure_signed(origin)?;
            
            <IdentityOf<T>>::insert(&who, info);
            
            Self::deposit_event(RawEvent::IdentitySet(who));
            Ok(())
        }

        /// 设置用户名
        #[weight = 10_000]
        pub fn set_username(origin, username: Vec<u8>) -> DispatchResult {
            let who = ensure_signed(origin)?;
            
            // 检查用户名是否已被占用
            ensure!(<UsernameOf<T>>::get(&username).is_none(), "Username already taken");
            
            // 插入新用户名
            <UsernameOf<T>>::insert(&username, who.clone());
            
            Self::deposit_event(RawEvent::UsernameSet(who, username));
            Ok(())
        }
    }
}

// 身份信息结构体
#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq)]
pub struct IdentityInfo {
    pub display_name: Vec<u8>,
    pub email: Vec<u8>,
    pub website: Vec<u8>,
    pub twitter: Vec<u8>,
}

// 测试模块
#[cfg(test)]
mod tests {
    use super::*;
    use frame_support::{assert_ok, assert_noop};
    use sp_core::H256;
    use frame_system::{self as system, RawOrigin};
    use sp_runtime::{
        testing::Header,
        traits::{BlakeTwo256, IdentityLookup},
        DispatchError,
    };

    // 测试运行时配置
    type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
    type Block = frame_system::mocking::MockBlock<Test>;
    
    frame_support::construct_runtime!(
        pub enum Test where
            Block = Block,
            NodeBlock = Block,
            UncheckedExtrinsic = UncheckedExtrinsic,
        {
            System: frame_system::{Module, Call, Config, Storage, Event<T>},
            Identity: Module,
        }
    );

    impl Config for Test {
        type Event = Event;
    }

    impl system::Config for Test {
        type BaseCallFilter = ();
        type BlockWeights = ();
        type BlockLength = ();
        type Origin = Origin;
        type Call = Call;
        type Index = u64;
        type BlockNumber = u64;
        type Hash = H256;
        type Hashing = BlakeTwo256;
        type AccountId = u64;
        type Lookup = IdentityLookup<Self::AccountId>;
        type Header = Header;
        type Event = Event;
        type BlockHashCount = ();
        type DbWeight = ();
        type Version = ();
        type PalletInfo = PalletInfo;
        type AccountData = ();
        type OnNewAccount = ();
        type OnKilledAccount = ();
        type SystemWeightInfo = ();
        type SS58Prefix = ();
    }

    // 测试设置身份信息
    #[test]
    fn test_set_identity() {
        new_test_ext().execute_with(|| {
            let alice = 1;
            let info = IdentityInfo {
                display_name: b"Alice".to_vec(),
                email: b"alice@example.com".to_vec(),
                website: b"alice.example.com".to_vec(),
                twitter: b"@alice".to_vec(),
            };
            
            // 设置身份信息
            assert_ok!(Identity::set_identity(Origin::signed(alice), info.clone()));
            
            // 验证存储
            assert_eq!(Identity::identity_of(alice), Some(info));
            
            // 验证事件
            let event = System::events().pop().expect("Expected an event").event;
            assert_eq!(
                event,
                Event::Identity(Event::IdentitySet(alice))
            );
        });
    }

    // 测试设置用户名
    #[test]
    fn test_set_username() {
        new_test_ext().execute_with(|| {
            let alice = 1;
            let username = b"alice".to_vec();
            
            // 设置用户名
            assert_ok!(Identity::set_username(Origin::signed(alice), username.clone()));
            
            // 验证存储
            assert_eq!(Identity::username_of(username.clone()), Some(alice));
            
            // 验证事件
            let event = System::events().pop(). expect("Expected an event").event;
            assert_eq!(
                event,
                Event::Identity(Event::UsernameSet(alice, username))
            );
            
            // 测试用户名冲突
            let bob = 2;
            assert_noop!(
                Identity::set_username(Origin::signed(bob), username),
                "Username already taken"
            );
        });
    }
}

1 回复

Rust身份管理模块pallet-identity的使用:Substrate区块链中的去中心化身份验证与属性管理

概述

pallet-identity是Substrate区块链框架中的一个核心模块,用于实现去中心化的身份验证和属性管理系统。它允许用户在区块链上注册身份信息,并管理相关属性,为构建复杂的身份验证系统提供了基础。

主要功能

  1. 身份注册:用户可以在链上创建和注册身份
  2. 属性管理:为身份添加各种属性信息
  3. 验证人系统:通过注册的验证人来确认身份信息的真实性
  4. 押金机制:防止垃圾注册的经济激励机制

基本使用方法

1. 在runtime中引入pallet-identity

首先需要在项目的runtime中引入该pallet:

impl pallet_identity::Config for Runtime {
    type Event = Event;
    type Currency = Balances;
    type Slashed = Treasury;
    type BasicDeposit = BasicDeposit;
    type FieldDeposit = FieldDeposit;
    type SubAccountDeposit = SubAccountDeposit;
    type MaxSubAccounts = MaxSubAccounts;
    type MaxAdditionalFields = MaxAdditionalFields;
    type MaxRegistrars = MaxRegistrars;
    type ForceOrigin = EnsureRoot<AccountId>;
    type WeightInfo = pallet_identity::weights::SubstrateWeight<Runtime>;
}

2. 注册身份

用户可以通过以下方式注册身份:

let info = IdentityInfo {
    additional: vec![],
    display: Data::Raw(b"My Name".to_vec()),
    legal: Data::None,
    web: Data::None,
    riot: Data::None,
    email: Data::Raw(b"my@email.com".to_vec()),
    pgp_fingerprint: None,
    image极狐Data::None,
    twitter: Data::None,
};

let call = pallet_identity::Call::set_identity { info };
let origin = Origin::signed(account_id);
RuntimeCall::Identity(call).dispatch(origin)?;

3. 添加属性

为已注册的身份添加额外属性:

let call = pallet_identity::Call::set_account_id {
    index: 0,
    data: Data::Raw(b"attribute_value".to_vec()),
};
let origin = Origin::signed(account_id);
RuntimeCall::Identity(call).dispatch(origin)?;

4. 验证身份

验证人可以通过以下方式验证用户身份:

let call = pallet_identity::Call::provide_judgement {
    reg_index: 0,
    target: target_account_id,
    judgement: Judgement::Reasonable,
    identity: Default::default(),
};
let origin = Origin::signed(registrar_account_id);
RuntimeCall::Identity(call).dispatch(origin)?;

高级功能

子账户管理

pallet-identity支持为身份创建子账户:

let call = pallet_identity::Call::add_sub {
    sub: sub_account_id,
    data: Data::Raw(b"sub_account".to_vec()),
};
let origin = Origin::signed(main_account_id);
RuntimeCall::Identity(call).dispatch(origin)?;

身份清除

当不再需要身份时,可以清除它并取回押金:

let call = pallet_identity::Call::clear_identity;
let origin = Origin::signed(account_id);
RuntimeCall::Identity(call).dispatch(origin)?;

完整示例代码

以下是一个完整的pallet-identity使用示例,包含身份注册、属性添加、子账户管理和身份验证:

// 引入必要的模块和类型
use frame_support::{dispatch::DispatchResult, traits::Currency};
use frame_system::{self as system};
use sp_runtime::traits::StaticLookup;
use pallet_identity::{self as identity, Data, IdentityInfo, Judgement};

// 假设这是我们的运行时配置
pub struct Runtime;

// 1. 身份注册示例
fn register_identity(account_id: AccountId) -> DispatchResult {
    // 准备身份信息
    let info = IdentityInfo {
        display: Data::Raw(b"John Doe".to_vec()),  // 显示名称
        email: Data::Raw(b"john@example.com".to_vec()),  // 电子邮件
        legal: Data::Raw(b"John Doe LLC".to_vec()),  // 法律实体名称
        web: Data::Raw(b"https://johndoe.example".to_vec()),  // 网站
        riot: Data::Raw(b"@johndoe:matrix.org".to_vec()),  // 即时通讯
        pgp_fingerprint: Some([0u8; 20]),  // PGP指纹
        image: Data::Raw(b"profile_image_hash".to_vec()),  // 图像哈希
        twitter: Data::Raw(b"@johndoe".to_vec()),  // Twitter账号
        additional: vec![],  // 附加字段
    };

    // 创建身份注册调用
    let call = pallet_identity::Call::set_identity { info };
    let origin = system::RawOrigin::Signed(account_id.clone()).into();
    
    // 执行调用
    RuntimeCall::Identity(call).dispatch(origin)?;
    
    Ok(())
}

// 2. 添加自定义属性示例
fn add_custom_attribute(account_id: AccountId, index: u32, value: Vec<u8>) -> DispatchResult {
    let call = pallet_identity::Call::set_account_id {
        index,
        data: Data::Raw(value),
    };
    let origin = system::RawOrigin::Signed(account_id.clone()).into();
    
    RuntimeCall::Identity(call).dispatch(origin)?;
    
    Ok(())
}

// 3. 子账户管理示例
fn manage_sub_accounts(main_account_id: AccountId, sub_account_id: AccountId) -> DispatchResult {
    // 添加子账户
    let add_call = pallet_identity::Call::add_sub {
        sub: sub_account_id.clone(),
        data: Data::Raw(b"employee".to_vec()),
    };
    let origin = system::RawOrigin::Signed(main_account_id.clone()).into();
    RuntimeCall::Identity(add_call).dispatch(origin)?;
    
    // 移除子账户
    let remove_call = pallet_identity::Call::remove_sub { sub: sub_account_id };
    let origin = system::RawOrigin::Signed(main_account_id).into();
    RuntimeCall::Identity(remove_call).dispatch(origin)?;
    
    Ok(())
}

// 4. 身份验证示例
fn verify_identity(registrar_id: AccountId, target_id: AccountId) -> DispatchResult {
    let call = pallet_identity::Call::provide_judgement {
        reg_index: 0,  // 验证人索引
        target: target_id,
        judgement: Judgement::KnownGood,  // 验证结果
        identity: Default::default(),
    };
    let origin = system::RawOrigin::Signed(registrar_id).into();
    
    RuntimeCall::Identity(call).dispatch(origin)?;
    
    Ok(())
}

// 5. 完整工作流示例
fn complete_identity_workflow() -> DispatchResult {
    // 假设这些账户已经存在
    let user_account = AccountId::new([1u8; 32]);
    let sub_account = AccountId::new([2u8; 32]);
    let registrar_account = AccountId::new([3u8; 32]);
    
    // 1. 用户注册身份
    register_identity(user_account.clone())?;
    
    // 2. 添加KYC属性
    add_custom_attribute(user_account.clone(), 0, b"KYC_Passed".to_vec())?;
    
    // 3. 添加子账户
    manage_sub_accounts(user_account.clone(), sub_account)?;
    
    // 4. 验证人验证身份
    verify_identity(registrar_account, user_account)?;
    
    Ok(())
}

注意事项

  1. 使用pallet-identity需要支付押金,防止垃圾注册
  2. 身份信息存储在链上,请考虑隐私问题
  3. 属性数据大小有限制,避免存储大量数据
  4. 验证人系统需要合理设计治理机制

pallet-identity为Substrate区块链提供了强大的身份管理基础,可以在此基础上构建复杂的身份验证和权限管理系统。

回到顶部