二、入门Substrate之Substrate-node-template模板中的pallet-template进行源码分析_暴龙战士wnb的博客-程序员宅基地

技术标签: rust  区块链  Substrate开发  

二、入门Substrate之Substrate-node-template模板中的pallet-template进行源码分析

其实作为Substrate的开发者们,已经为使用框架者提供了类似八股文一样的宏来简化开发,目前你只需要记住这个配置是干什么用的就够了,如果想要自定义pallet,建议直接把模板中的lib.rs直接复制过去,进行修改即可,后续想要深入了解,可以去看substrate的源码。本文将会分析了pallet-template的文件结构,常用配置的作用,并仿照模板自定义一个交易函数、存储类型、事件、错误。

1.文件结构

首先我们打开Substrate-node-template文件夹看看目录结构

我们找到根目录下的pallets->template

image-20211006173635347

  • lib.rs:自定义pallet的主文件,大部分逻辑都在这里
  • mock.rs:为测试文件提供一些配置
  • test.rs:测试文件
  • benchmarking.rs:此文件用于生成发交易使用的weight,在后面的文章中会专门介绍,暂不讨论

2.源码分析

下面我们将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(())
				},
			}
		}
	}
}

3.实践环节

下面我们仿照源码自定义

  • hello_substrate函数,
  • GreaterThan100错误
  • HelloStored(u32, T::AccountId)事件
  • Hello的StorageValue类型

主要功能:存入一个小于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

image-20211006182832722

4).使用Polkdot浏览器进行交互

此时我们查看交易就发现多了一个helloSubstrate

image-20211006182946612

因为我们设置的必须小于100,这时我们存入一个100,就会抛出错误

image-20211006183611356

我们存入一个99,交易成功,并发出一个HelloStored事件

image-20211006183704006

总结:

本文分析了pallet-template的文件结构,常用配置的作用,并仿照模板自定义一个交易函数、存储类型、事件、错误。

下一篇文章将会介绍Substrate中的常见数据类型以及如何正确使用

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_45332753/article/details/120627981

智能推荐

mybatis springboot gradle-程序员宅基地

mybatis springboot gradle generator pageHelpergeneratorConfig.xml<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generato...

ZTree 设置初始化时勾选某个节点_jquery-ztree 初始化选中节点-程序员宅基地

//ZTree初始化所执行的方法 /* 先在callback 方法中设置如下,初始化树时执行onAsyncSuccess 方法 callback: { onCheck: YnOnClick, onAsyncSuccess:onAsyncSuccess ..._jquery-ztree 初始化选中节点

python 获取本地视频信息_(2条消息) python 本地数据获取-程序员宅基地

在python中需要长期保存的本地数据都以文件的方式存在,所以获取本地数据其实就是从文件中读取数据。打开文件的方法如下:参数一是必须的,表示文件的存储路径。参数二是文件的打开模式,是可选的,默认值是 r ,表示以读模式打开。参数三表示缓冲区大小, -1 表示使用系统默认的缓冲区大小,0 表示不使用缓冲,1 表示缓冲一行,大于1的值表示缓冲指定大小。为了读写速度快一些,请使用缓冲。open( )函数..._python中open打开本地视频获取信息

关于HTML表格导出的两种方式,使用datatables插件excelHtml5和Apache POI_h5 excel插件-程序员宅基地

引言datatables是基于jQuery的HTML表格插件,通过各种参数配置可以自定义出对应的表格功能。同时datatables有许多plug可以扩展功能。表格导出Excel是很常见的需求,有两种实现方式,前端导出以及使用POI后台生成Excel文件返回文件流到前台下载。前端导出有很多种,有ActiveX、flash、html5,兼容性各不相同。下面分享实际使用POI和excelHtml..._h5 excel插件

获取Android当前运行的activity之UsageStatsManager_android获取当前运行app useagestats-程序员宅基地

前言:之前总结了如何获取当前界面正在运行的APP包名,也就是上一个博客。这里做一下补充。UsageStatsManager是用来统计app使用情况的类,用于获取包含特定时间范围的应用包的使用情况统计信息;在Android api21(即Android5.0引入);系统API稳定性好,Android5.0及以后版本都支持,不存在版本兼容问题。UsageStatsManager源码下面看如何使用..._android获取当前运行app useagestats

Jmeter之http测试及脚本录制-程序员宅基地

一、 什么是jmeter Jmeter是免费开源的性能测试工具(同时也可以用作功能测试,http协议debug工具).在如今越来越注重知识产权的今天, 公司越来越不愿意冒着巨大的风险去使用盗版的商业性能测试工具. 但如果不使用盗版, 昂贵的License费用也不是每一个中小型公司,甚至是大型公司愿意投入的. 所以很多公司甚至还在铤而走险的买少量的license,然后大范围的使用.jmeter这

随便推点

关于在 @JavascriptInterface 方法中发现异常引发的思考-程序员宅基地

问题描述:在一次测试中,直接在 @JavascriptInterface 方法中调用了 WebView.loadUrl(&quot;javascript:xxx()&quot;);,结果 js 方法没有响应。当时以为是调用频繁或者页面刷新导致无法接收到消息,进一步测试发现在 Locat 中有输出日志:即 java.lang.RuntimeException: java.lang.Throwable: A Web..._@javascriptinterface

Java解压.7z格式压缩包_sevenzipjbinding解压7z-程序员宅基地

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放大物体阴影消失_Unity3D中两种默认阴影的实现-程序员宅基地

Unity3D中两种阴影的实现传统的ShadowMapShadowMap说起来十分简单,把摄像机和光源的位置重叠,那么场景中该光源的阴影区域就是那些摄像机看不到的地方,主要应用在前向渲染路径中。具体实现分以下几个步骤:如果有平行光开启了阴影,Unity就会为该光源计算它的ShadowMap(只会计算一个平行光),这张ShadowMap其实就是深度图,记录了从该光源的位置出发、能看到的场景中距离它最..._unity里面模型缩小就有阴影放大就没有

ARM Ubuntu 19.04 中科大源_ubuntu 19.04 arm-程序员宅基地

找源找的好辛苦,做个记录,备份一下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

python生成规定随机数_python使用random函数生成随机数-程序员宅基地

原博文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图集出之前的人),后来像我这样人看钢结构图纸真的看不习惯,不过没有办法,还是要习惯的,我们知道麻烦,但任何事情都有规律的,钢结构的详图结点相当的多,但这些变化真的..._钢结构图纸焊缝长度有什么用