技术标签: rust 区块链 Substrate开发
其实作为Substrate的开发者们,已经为使用框架者提供了类似八股文一样的宏来简化开发,目前你只需要记住这个配置是干什么用的就够了,如果想要自定义pallet,建议直接把模板中的lib.rs直接复制过去,进行修改即可,后续想要深入了解,可以去看substrate的源码。本文将会分析了pallet-template的文件结构,常用配置的作用,并仿照模板自定义一个交易函数、存储类型、事件、错误。
首先我们打开Substrate-node-template文件夹看看目录结构
我们找到根目录下的pallets->template
下面我们将lib.rs文件内容复制过来,一行一行进行解释分析
//这个 crate 不应使用 rust 的标准库
#![cfg_attr(not(feature = "std"), no_std)]
pub use pallet::*;
//引入mock
#[cfg(test)]
mod mock;
//引入tests
#[cfg(test)]
mod tests;
//引入benchmarking
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
//以下是构建自定义pallet的主体部分
#[frame_support::pallet]
pub mod pallet {
//使用依赖
//frame_support主要提供DispatchResult,其实就是一个Rusult,正确执行返回一个Ok(())即可
//frame_support还定义了一些常用的存储结构,trait
use frame_support::{dispatch::DispatchResult, pallet_prelude::*};
//引入frame_system中的一些常用的宏,例如* ensure_signed、ensure_none、ensure_root
use frame_system::pallet_prelude::*;
// 通过指定托盘所依赖的参数和类型来配置托
#[pallet::config]
pub trait Config: frame_system::Config {
//如果你想在托盘中使用事件,那么就添加该行
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
}
//必须存在的配置,copy过来即可
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
// 定义一个叫Something存储类型为StorageValue,里面存放u32类型,提供名字叫something的getter函数,用于获取Something中的值
#[pallet::storage]
#[pallet::getter(fn something)]
pub type Something<T> = StorageValue<_, u32>;
//定义事件 发出事件方法为deposit_event
#[pallet::event]
#[pallet::metadata(T::AccountId = "AccountId")]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
//事件名
SomethingStored(u32, T::AccountId),
}
// 定义错误,直接起名就可以
#[pallet::error]
pub enum Error<T> {
NoneValue,
StorageOverflow,
}
// 定义交易逻辑
#[pallet::call]
impl<T: Config> Pallet<T> {
//使用交易所需的weight,可以使用benchmarking生成,所以没生成之前weight随便填,例如改成#[pallet::weight(10_000)]
#[pallet::weight(10_000 + T::DbWeight::get().writes(1))]
//origin: OriginFor<T>是交易的调用者
pub fn do_something(origin: OriginFor<T>, something: u32) -> DispatchResult {
//确保他是一个正常的用户,并返回交易调用者
let who = ensure_signed(origin)?;
//StorageValue类型的方法,存入一个数据
<Something<T>>::put(something);
//发出事件
Self::deposit_event(Event::SomethingStored(something, who));
//成功调用
Ok(())
}
//使用交易所需的weight,可以使用benchmarking生成,所以没生成之前weight随便填,例如改成#[pallet::weight(10_000)]
//这里我把源码改成了#[pallet::weight(10_000)]
#[pallet::weight(10_000)]
pub fn cause_error(origin: OriginFor<T>) -> DispatchResult {
//确保他是一个正常的用户,并返回交易调用者
let _who = ensure_signed(origin)?;
//StorageValue类型的方法,读取数据
match <Something<T>>::get() {
// 拿到数据为空那么就抛出一个错误
None => Err(Error::<T>::NoneValue)?,
//如果拿到数据
Some(old) => {
// 那么就+1,如果超出u32的最大值,那么就抛出溢出错误
let new = old.checked_add(1).ok_or(Error::<T>::StorageOverflow)?;
//将+1后的新值,重新存入Something中
<Something<T>>::put(new);
//成功调用
Ok(())
},
}
}
}
}
下面我们仿照源码自定义
主要功能:存入一个小于100的值,超出则抛出GreaterThan100错误,存入Hello成功则发出HelloStored(u8, T::AccountId)事件
1).编写代码
#![cfg_attr(not(feature = "std"), no_std)]
pub use pallet::*;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[frame_support::pallet]
pub mod pallet {
use frame_support::{dispatch::DispatchResult, pallet_prelude::*};
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
#[pallet::storage]
#[pallet::getter(fn something)]
pub type Something<T> = StorageValue<_, u32>;
//定义一个类型为StorageValue的hello
#[pallet::storage]
#[pallet::getter(fn hello)]
pub type Hello<T> = StorageValue<_, u8>;
#[pallet::event]
#[pallet::metadata(T::AccountId = "AccountId")]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
SomethingStored(u32, T::AccountId),
//HelloStored事件
HelloStored(u8, T::AccountId),
}
#[pallet::error]
pub enum Error<T> {
NoneValue,
StorageOverflow,
//大于100错误
GreaterThan100,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(10_000 + T::DbWeight::get().writes(1))]
pub fn do_something(origin: OriginFor<T>, something: u32) -> DispatchResult {
let who = ensure_signed(origin)?;
<Something<T>>::put(something);
Self::deposit_event(Event::SomethingStored(something, who));
Ok(())
}
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]
pub fn cause_error(origin: OriginFor<T>) -> DispatchResult {
let _who = ensure_signed(origin)?;
match <Something<T>>::get() {
None => Err(Error::<T>::NoneValue)?,
Some(old) => {
let new = old.checked_add(1).ok_or(Error::<T>::StorageOverflow)?;
<Something<T>>::put(new);
Ok(())
},
}
}
//定义hello_substrats
#[pallet::weight(10_000)]
pub fn hello_substrate(origin: OriginFor<T>,num: u8) -> DispatchResult {
let _who = ensure_signed(origin)?;
//确保值小于100
ensure!(num<100,Error::<T>::GreaterThan100);
//存入数据
<Hello<T>>::put(num);
//发出事件
Self::deposit_event(Event::HelloStored(num, _who));
Ok(())
}
}
}
2).重新编译
cargo build --release
3).运行节点
./target/release/node-template --dev --tmp
4).使用Polkdot浏览器进行交互
此时我们查看交易就发现多了一个helloSubstrate
因为我们设置的必须小于100,这时我们存入一个100,就会抛出错误
我们存入一个99,交易成功,并发出一个HelloStored事件
总结:
本文分析了pallet-template的文件结构,常用配置的作用,并仿照模板自定义一个交易函数、存储类型、事件、错误。
下一篇文章将会介绍Substrate中的常见数据类型以及如何正确使用
mybatis springboot gradle generator pageHelpergeneratorConfig.xml<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generato...
//ZTree初始化所执行的方法 /* 先在callback 方法中设置如下,初始化树时执行onAsyncSuccess 方法 callback: { onCheck: YnOnClick, onAsyncSuccess:onAsyncSuccess ..._jquery-ztree 初始化选中节点
在python中需要长期保存的本地数据都以文件的方式存在,所以获取本地数据其实就是从文件中读取数据。打开文件的方法如下:参数一是必须的,表示文件的存储路径。参数二是文件的打开模式,是可选的,默认值是 r ,表示以读模式打开。参数三表示缓冲区大小, -1 表示使用系统默认的缓冲区大小,0 表示不使用缓冲,1 表示缓冲一行,大于1的值表示缓冲指定大小。为了读写速度快一些,请使用缓冲。open( )函数..._python中open打开本地视频获取信息
引言datatables是基于jQuery的HTML表格插件,通过各种参数配置可以自定义出对应的表格功能。同时datatables有许多plug可以扩展功能。表格导出Excel是很常见的需求,有两种实现方式,前端导出以及使用POI后台生成Excel文件返回文件流到前台下载。前端导出有很多种,有ActiveX、flash、html5,兼容性各不相同。下面分享实际使用POI和excelHtml..._h5 excel插件
前言:之前总结了如何获取当前界面正在运行的APP包名,也就是上一个博客。这里做一下补充。UsageStatsManager是用来统计app使用情况的类,用于获取包含特定时间范围的应用包的使用情况统计信息;在Android api21(即Android5.0引入);系统API稳定性好,Android5.0及以后版本都支持,不存在版本兼容问题。UsageStatsManager源码下面看如何使用..._android获取当前运行app useagestats
一、 什么是jmeter Jmeter是免费开源的性能测试工具(同时也可以用作功能测试,http协议debug工具).在如今越来越注重知识产权的今天, 公司越来越不愿意冒着巨大的风险去使用盗版的商业性能测试工具. 但如果不使用盗版, 昂贵的License费用也不是每一个中小型公司,甚至是大型公司愿意投入的. 所以很多公司甚至还在铤而走险的买少量的license,然后大范围的使用.jmeter这
问题描述:在一次测试中,直接在 @JavascriptInterface 方法中调用了 WebView.loadUrl("javascript:xxx()");,结果 js 方法没有响应。当时以为是调用频繁或者页面刷新导致无法接收到消息,进一步测试发现在 Locat 中有输出日志:即 java.lang.RuntimeException: java.lang.Throwable: A Web..._@javascriptinterface
package com.annet.upload.core.utils;import java.io.File;import java.io.FileOutputStream;import java.io.RandomAccessFile;import java.util.Arrays;import org.apache.commons.io.IOUtils;import org.slf4j_sevenzipjbinding解压7z
Unity3D中两种阴影的实现传统的ShadowMapShadowMap说起来十分简单,把摄像机和光源的位置重叠,那么场景中该光源的阴影区域就是那些摄像机看不到的地方,主要应用在前向渲染路径中。具体实现分以下几个步骤:如果有平行光开启了阴影,Unity就会为该光源计算它的ShadowMap(只会计算一个平行光),这张ShadowMap其实就是深度图,记录了从该光源的位置出发、能看到的场景中距离它最..._unity里面模型缩小就有阴影放大就没有
找源找的好辛苦,做个记录,备份一下deb http://mirrors.ustc.edu.cn/ubuntu-ports/ disco main multiverse restricted universedeb http://mirrors.ustc.edu.cn/ubuntu-ports/ disco-backports main multiverse restricted univers..._ubuntu 19.04 arm
原博文2016-06-30 14:31 −python使用random函数来生成随机数,常用的方法有: import random #生成0-1之间的数(不包括0和1) random.random() #生成1-5之间的随机整数(包括1和5) random.randint(1, 5) #生成1-10之间的整数(包括1但不包括10) r...相关推荐2019-12-15 21:31 −imp...
钢结构如何算量?钢结构字母表示的含义,你了解多少?工程圈联盟施工中的钢结构1。算量最基本的就是看图纸,土建的人都烦钢构图纸的太乱,其实我也有这种看法,因为平法并没有用在其上面,图样还保留了一前土建制图的原则,所以做为老人看比较习惯(101图集出之前的人),后来像我这样人看钢结构图纸真的看不习惯,不过没有办法,还是要习惯的,我们知道麻烦,但任何事情都有规律的,钢结构的详图结点相当的多,但这些变化真的..._钢结构图纸焊缝长度有什么用