这篇文章介绍如何基于vector实现存储集合,并将runtime模块编程的四大宏:decl_storage! decl_event! decl_error! decl_module!整合使用。 Rust标准库的Vec Vec使用示例: let mut vec = Vec::new(); vec.push(1); vec.push(2); vec[0] = 7; for x in &vec { println!("{}", x); } //输出:7 2 编写模块代码 集合(Set)是一种数据结构,用于存储没有重复项的数据。 Substrate的存储API没有提供显式声明集合的方法,但可以通过vector或map来实现它们。 Substrate可使用Rust标准库的Vec(Struct std::vec::Vec)作为连续的可增长数组类型,写作Vec<T>。 修改pallets/template/src/lib.rs的代码如下: #![cfg_attr(not(feature = "std"), no_std)] use frame_support::{decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure}; use frame_system::{self as system, ensure_signed}; use sp_std::prelude::*; pub const MAX_MEMBERS: usize = 16; pub trait Trait: system::Trait { type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>; } decl_storage! { trait Store for Module<T: Trait> as VecSet { Members get(fn members): Vec<T::AccountId>; } } decl_event!( pub enum Event<T> where AccountId = <T as system::Trait>::AccountId { MemberAdded(AccountId), MemberRemoved(AccountId), } ); decl_error! { pub enum Error for Module<T: Trait> { AlreadyMember, NotMember, MembershipLimitReached, } } decl_module! { pub struct Module<T: Trait> for enum Call where origin: T::Origin { fn deposit_event() = default; type Error = Error<T>; #[weight = 10_000] pub fn add_member(origin) -> DispatchResult { let new_member = ensure_signed(origin)?; let mut members = Members::<T>::get(); ensure!(members.len() < MAX_MEMBERS, Error::<T>::MembershipLimitReached); match members.binary_search(&new_member) { Ok(_) => Err(Error::<T>::AlreadyMember.into()), Err(index) => { members.insert(index, new_member.clone()); Members::<T>::put(members); Self::deposit_event(RawEvent::MemberAdded(new_member)); Ok(()) } } } #[weight = 10_000] fn remove_member(origin) -> DispatchResult { let old_member = ensure_signed(origin)?; let mut members = Members::<T>::get(); match members.binary_search(&old_member) { Ok(index) => { members.remove(index); Members::<T>::put(members); Self::deposit_event(RawEvent::MemberRemoved(old_member)); Ok(()) }, Err(_) => Err(Error::<T>::NotMember.into()), } } } } 下面详细分析模块代码。 配置依赖库 模块代码使用了Rust标准库的Vec,需要在Cargo.toml中配置依赖库,在[dependencies]下添加: sp-std = { version = '2.0.0', default-features = false } 在模块代码中导入: use sp_std::prelude::*; sp_std::prelude模块包含了一些Rust常用的组件: 设置集合最大成员数量 pub const MAX_MEMBERS: usize = 16; 这里使用到了usize类型,是自适应无符号数字类型,大小依赖运行程序的计算机架构,64位架构为64位,32位架构为32位。 声明事件 pub trait Trait: system::Trait { type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>; } 声明事件的语法是固定的,不同的pallet只需要复制这部分代码即可,这里声明一个通用事件。 定义存储 decl_storage! { trait Store for Module<T: Trait> as VecSet { Members get(fn members): Vec<T::AccountId>; } } 定义Vec集合名称为Members,Vec中保存AccountId。 定义事件 decl_event!( pub enum Event<T> where AccountId = <T as system::Trait>::AccountId { MemberAdded(AccountId), MemberRemoved(AccountId), } ); 使用枚举(enum)定义事件类型: MemberAdded:添加成员后触发;MemberRemoved:删除成员后触发;定义错误处理机制 decl_error! { pub enum Error for Module<T: Trait> { AlreadyMember, NotMember, MembershipLimitReached, } } 使用枚举(enum)定义可能出现的错误类型: AlreadyMember:无法新增成员,因为已经是;NotMember:无法删除成员,因为还不是;MembershipLimitReached:无法新增成员,因为已达到成员数量上限;新增成员 模块中定义了一个add_member函数来新增成员到Members集合,添加成员时会做一些条件检查以确保安全。 add_member函数的定义如下: pub fn add_member(origin) -> DispatchResult { let new_member = ensure_signed(origin)?; let mut members = Members::<T>::get(); ensure!(members.len() < MAX_MEMBERS, Error::<T>::MembershipLimitReached); match members.binary_search(&new_member) { Ok(_) => Err(Error::<T>::AlreadyMember.into()), Err(index) => { members.insert(index, new_member.clone()); Members::<T>::put(members); Self::deposit_event(RawEvent::MemberAdded(new_member)); Ok(()) } } } 这里使用了Rust的match语法,检查列表是否已存在潜在的新成员,以避免添加重复成员。 Vec列表始终是有序的,可以使用二分搜索算法(binary_search)来查找成员,如果查找到该成员已存在则返回错误,如果查找到该成员不存在则调用insert方法插入该成员到Vec列表,然后触发一个事件。 删除成员 删除成员时先在列表中查找调用者,如果不存在则不做任何事,如果存在则二分搜索算法会返回其索引,可将其删除。 fn remove_member(origin) -> DispatchResult { let old_member = ensure_signed(origin)?; let mut members = Members::<T>::get(); match members.binary_search(&old_member) { Ok(index) => { members.remove(index); Members::<T>::put(members); Self::deposit_event(RawEvent::MemberRemoved(old_member)); Ok(()) }, Err(_) => Err(Error::<T>::NotMember.into()), } } 编译 编译命令如下: cd substrate-node-template cargo +nightly-2020-08-23 build --release 使用了nightly-2020-08-23这个较稳定的cargo nightly版本以避免编译过程中出现bug。 测试 启动node-template ./target/release/node-template --dev 打开https://polkadot.js.org/apps,切换网络为DEVELOPMENT-Local Node: 选项卡选择开发者-交易,“提交下面的外部信息”选择templateModule,会自动获取到定义的函数addMember()和removeMember(): 点击右下角“提交交易”按钮,点击“签名并提交”: 就会调用decl_module!中定义的add_member函数,并触发事件MemberAdded。 选项卡选择网络-浏览-链信息,在右侧可以看到最新触发的事件: 可以看到触发的事件是模块代码中定义的templateModule.MemberAdded。 选项卡选择开发者-链状态-存储,“查询所选状态”选择templateModule,会自动出现存储对象members(): Vec<AccountId>,点击右侧加号,可以看到ALICE账户的AccountId已被添加进Vec集合。 再使用BOB账户进行相同操作,Vec集合的成员如下: 还有更多操作如删除成员、触发错误处理机制等,读者可自行探索。 —- 编译者/作者:松果 玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。 |
【Substrate开发教程】19 - Substrate Runtime模块编程的四大宏的整合使用
2020-11-03 松果 来源:区块链网络
- 上一篇:11/3午间操作策略供参考
- 下一篇:慕岩说币:比特币以太坊午间操作策略仅供参考
LOADING...
相关阅读:
- QitmeerNetwork大事件:社区福利来袭2020-11-03
- 马云:数字货币可能会重新定义货币2020-11-03
- 亲近政府,远离政治,徐明星事件启示。感恩币乎。暴走北纬一直在加2020-11-03
- MakerDAO加入韩国科技巨头Kakao的区块链项目2020-11-02
- 10月共发生安全事件29起虚拟货币投资诈骗事件频发2020-11-02