substrate进阶(3)-pallet结构之storage、event、error

本文主要介绍pallet的storage

1. 概述

1.1 storage模板:

1
2
3
#[pallet::storage]
#[pallet::getter(fn something)]
pub type MyStorage<T: Config> = StorageValue<_, u32>;
  1. 第一行:#[pallet::storage]是定义的storage时的固定写法,表示下面定义了一个storage。在定义storage时,无论你怎么使用,都必须写这一行。
  2. 第二行,#[pallet::getter(fn something)],在有些定义storage时会使用这一行,有些不会。这一行的意思是自动为下面定义的storage生成一个getter函数,函数的名字就是something
  3. 第三行,存储定义

1.2 error类型模板

在runtime代码执行时,代码必须是“非抛出的”,或者说不应该panic,应该是优雅的处理错误,所以在写pallet代码时,允许自定义错误类型,当错误发生时,可以返回定义的错误类型。这里的Error类型是指运行时在执行调度函数(也就是交易函数)时返回的错误。因为在调度函数执行时,返回的结果为DispatchResult类型,当执行结果错误时,返回DispatchError。

1
2
3
4
5
6
7
8
9
10
#[pallet::error]
pub enum Error<T> {
//错误类型1
InvalidParameter,
//错误类型2
OutOfSpace,
...
//错误类型3
InvalidFee,
}

在函数中返回错误的方式:

1
2
3
4
5
6
7
8
9
pub fn xx_function(origin: OriginFor<T>, ...)
-> DispatchResultWithPostInfo {
//...
if 返回错误条件成立 {
return Error::<T>::错误类型;
}
//...
Ok(().into())
}

1.3 event类型模板

1
2
3
4
5
6
7
#[pallet::event]
#[pallet::generate_deposit(pub (super) fn deposit_event)]
pub enum Event<T: Config> {
SetClass(u32),
SetStudentInfo(u32, u128),
SetDormInfo(u32, u32, u32),
}

2. 案例

案例:记录某个年纪各个寝室每个床位的学生姓名 将会使用到StorageValueStorageMapStorageDoubleMap几种存储类型
使用模板项目substrate-node-template 完成该案例:

1
2
3
4
5
6
7
8
9
10
11
# 进入项目根目录
cd ./pallets
cp -rf template use-storage
cd use-storage
vim ./Cargo.toml
# [package]中,变更:name="pallet-use-storage"

cd src
rm -rf benchmarking.rs
rm -rf mock.rs
rm -rf tests.rs

2.1 use-storage/src/lib.rs编辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

#[pallet::pallet]
#[pallet::generate_store(pub (super) trait Store)]
pub struct Pallet<T>(_);

#[pallet::config]
pub trait Config: frame_system::Config {
//声明事件
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
}

#[pallet::storage]
#[pallet::getter(fn my_class)]
pub type Class<T: Config> = StorageValue<_, u32, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn students_info)]
pub type StudentsInfo<T: Config> = StorageMap<_, Blake2_128Concat, u32, u128, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn dorm_info)]
pub type DormInfo<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
u32, //dorm number
Blake2_128Concat,
u32, //bed number
u32, // student number
ValueQuery,
>;

#[pallet::error]
pub enum Error<T> {
// Class 只允许设置一次
SetClassDuplicate,
// 相同学号的只允许设置一次名字
SetStudentsInfoDuplicate,
// 相同床位只允许设置一次
SetDormInfoDuplicate,
}

#[pallet::event]
#[pallet::generate_deposit(pub (super) fn deposit_event)]
pub enum Event<T: Config> {
SetClass(u32),
SetStudentInfo(u32, u128),
SetDormInfo(u32, u32, u32),
}

#[pallet::call]
impl<T: Config> Pallet<T> {
//存储班级信息
#[pallet::weight(0)]
pub fn set_class_info(origin: OriginFor<T>, class: u32) -> DispatchResultWithPostInfo {
ensure_root(origin)?; //需要root权限才能设置
if Class::<T>::exists() {
return Err(Error::<T>::SetClassDuplicate.into())
}
Class::<T>::put(class);
Self::deposit_event(Event::SetClass(class));
Ok(().into())
}

//存储学生信息,存储学生的学号和姓名的对应关系,使用StorageMap存储
#[pallet::weight(0)]
pub fn set_student_info(
origin: OriginFor<T>,
student_number: u32,
student_name: u128,
) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
if StudentsInfo::<T>::contains_key(student_number) {
return Err(Error::<T>::SetStudentsInfoDuplicate.into())
}
StudentsInfo::<T>::insert(&student_number, &student_name);
Self::deposit_event(Event::SetStudentInfo(student_number, student_name));
Ok(().into())
}

//存储寝室信息,储寝室号、床号、学号之间的对应关系,使用StorageDoubleMap存储。
#[pallet::weight(0)]
pub fn set_dorm_info(
origin: OriginFor<T>,
dorm_number: u32,
bed_number: u32,
student_number: u32,
) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
if DormInfo::<T>::contains_key(dorm_number, bed_number) {
return Err(Error::<T>::SetDormInfoDuplicate.into())
}
DormInfo::<T>::insert(&dorm_number, &bed_number, &student_number);
Self::deposit_event(Event::SetDormInfo(dorm_number, bed_number, student_number));
Ok(().into())
}
}
}

2.2 ./runtime/Cargo.toml添加依赖

1
2
3
4
5
6
7
[dependencies]
pallet-use-storage = { version = "4.0.0-dev", default-features = false, path = "../pallets/use-storage" }
[features]
# ...
std = [
"pallet-use-storage/std",
]

2.3 ./runtime/src/lib.rs只加入pallet

construct_runtime!(//...)前代码:

1
2
3
impl pallet_use_storage::Config for Runtime {
type Event = Event;
}

construct_runtime!(//...)最后加上:

1
2
3
4
5
6
7
8
9
10
11
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
//...
//添加下面这行
UseStorage: pallet_use_storage,
}
);

3. 运行

编译并启动节点:

1
2
cargo build
./target/debug/node-template --dev

打开polkadot-js-app 连接节点 进入:开发者-交易
进入:开发者-超级管理
进入:开发者-链状态
可以按需处理useStorage相关方法,当输入内容错误时候,则会返回错误信息(error的应用)

总结

本文编辑完毕

  • Copyrights © 2017-2023 Jason
  • Visitors: | Views:

谢谢打赏~

微信