三、入门Substrate之常用存储数据类型和它们相应的操作API
Substrate作为一个通用的区块链开发框架,提供了丰富的数据类型用于在链上存储数据。
运行时存储允许您将数据存储在区块链中,这些数据在块之间持久存在,并且可以从运行时逻辑中访问。存储应该是区块链运行时开发人员最关心的问题之一。精心设计的存储系统减少了网络中节点的负载,最终降低了区块链参与者的间接成本。换句话说,区块链运行时存储的基本原则是尽量减少其使用。
- Storage Value 用于存储任何单值类型,例如u8、u16
- Storage Map 用于存储键值哈希映射,例如余额到帐户的映射。
- Storage Double Map 用作具有两个键的存储映射的实现,以提供有效删除具有公共第一个键的所有条目的能力。
- Storage N Map - 用于存储具有任意数量键的哈希映射,可作为构建三重存储映射、四重存储映射等的基础。
1.StorageValue
1).整数类型
例如:存一个u32
//第一个参数为_固定,ValueQuery参数可填可不填,不填get获取就是Option包裹的u32,填上就直接返回u32
//pub(super) v2版 区块链存储始终在运行时之外公开 可见
#[pallet::storage]
#[pallet::getter(fn something)]
pub(super) type TestStorageValue<T> = StorageValue<_, u32,ValueQuery>;
//存入值
TestStorageValue::<T>::put(3);
// something() 和 TestStorageValue::<T>::get()的操作结果一样
//获取值
something()
TestStorageValue::<T>::get()
//数值运算存在溢出风险应该使用更加安全的api
let temp_value = TestStorageValue::<T>::get().checked_add(2).ok_or(Error::<T>::StorageOverflow)?;
KittiesCount::<T>::put(kitty_id + 1u32.into());
2).boolean
存入获取用法同上
3).Vec
//引入
use frame_support::inherent::Vec;
#[pallet::storage]
#[pallet::getter(fn get_vec_value)]
pub type TestVecValue<T> = StorageValue<_, Vec<u8>,ValueQuery>;
//和原生Rust操作一样
let mut test_vec = Vec::new();
test_vec.push(2);
test_vec.push(3);
test_vec.push(3);
TestVecValue::<T>::put(test_vec);
4).string &str
不支持,将字符串转成vec再存入值
//相互转换
let str_vec = b"test".to_vec();
let vec_str = String::from_utf8(str_vec).unwrap();
5).struct
//引入
use frame_support::codec::{Encode, Decode};
//定义
#[derive(Encode, Decode, Clone, PartialEq)]
pub struct Person{
pub number: u32,
pub name: Vec<u8>,
}
//定义StorageValue
#[pallet::storage]
#[pallet::getter(fn get_struct_value)]
pub type TestStructValue<T> = StorageValue<_, Person>;
//存入值
let person = Person{
number:1,
name:name,
};
TestStructValue::<T>::put(person);
2.StorageMap
键值对
//定义
#[pallet::storage]
#[pallet::getter(fn get_struct_map)]
pub type StructMap<T:Config> = StorageMap<_,Blake2_128Concat,T::AccountId,Person>;
// 插入一个元素
StructMap::<T>::insert(key, value);
// 通过key获取value
StructMap::<T>::get(key);
// 删除某个key对应的元素
StructMap::<T>::remove(key);
//是否包含key,返回bool
StructMap::<T>::contains_key(key);
// 覆盖或者修改某个key对应的元素
StructMap::<T>::insert(key, new_value);
StructMap::<T>::mutate(key, |old_value| old_value+1);
3.DoubleMap
暂未使用到,后续补充
4.配置一个常量
#[pallet::config]
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
//配置常量
#[pallet::constant]
type MaxCardSize: Get<u32>;
}
//获取它的值
T::MaxCardSize::get()
//在runtime的lib.rs中配置它
parameter_types! {
//设置值
pub const MaxCardSize: u32 = 1024 * 1024;
}
impl pallet_coming_id::Config for Runtime {
type Event = Event;
//添加上
type MaxCardSize = MaxCardSize;
}
5.创世配置
//先定义一个存储它的StorageValue
#[pallet::storage]
#[pallet::getter(fn high_admin_key)]
pub(super) type HighKey<T: Config> = StorageValue<_, T::AccountId, ValueQuery>;
//创世配置
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub high_admin_key: T::AccountId,
}
//要实现Default,不然报错
#[cfg(feature = "std")]
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
Self {
high_admin_key: Default::default(),
}
}
}
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
fn build(&self) {
<HighKey<T>>::put(&self.high_admin_key);
}
}
//注意Runtime中的lib.rs里面要改一下
//加一个 Config<T>
TemplateModule: pallet_template::{Pallet, Call,Config<T>, Storage, Event<T>},
//最后在Node中的chain_spec.rs配置一下 注意template_module和TemplateModule关系,TemplateModuleConfig也是一个注意点,默认就叫这个名字后面加上Config
template_module: TemplateModuleConfig {
// Assign network admin rights.
high_admin_key: root_key.clone(),
},
总结:以上就是最常用到底数据类型以及常见操作,因为笔者也在学习过程中,后面也会补充在项目中常常使用到的操作,如果想广泛学习方法建议打开文档自己学习。
在学习完,pallet开发中基本配置和常使用的存储数据类型之后,在下一篇文章中,会尝试开发一个pallet-erc20实例