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"
);
});
}
}
Rust身份管理模块pallet-identity的使用:Substrate区块链中的去中心化身份验证与属性管理
概述
pallet-identity
是Substrate区块链框架中的一个核心模块,用于实现去中心化的身份验证和属性管理系统。它允许用户在区块链上注册身份信息,并管理相关属性,为构建复杂的身份验证系统提供了基础。
主要功能
- 身份注册:用户可以在链上创建和注册身份
- 属性管理:为身份添加各种属性信息
- 验证人系统:通过注册的验证人来确认身份信息的真实性
- 押金机制:防止垃圾注册的经济激励机制
基本使用方法
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(())
}
注意事项
- 使用
pallet-identity
需要支付押金,防止垃圾注册 - 身份信息存储在链上,请考虑隐私问题
- 属性数据大小有限制,避免存储大量数据
- 验证人系统需要合理设计治理机制
pallet-identity
为Substrate区块链提供了强大的身份管理基础,可以在此基础上构建复杂的身份验证和权限管理系统。