substrate中各种常用宏的讲解

8 篇文章 0 订阅
1 篇文章 0 订阅

欢迎转载,转载请注名 《霜之小刀》,邮箱lihn1011@163.com

在学习substrate的过程中,遇到各种各样的宏,看也看不懂,读宏的实现实在太难,索性终于找到了介绍各个runtime中宏如何使用的说明,以下是我的翻译和理解
由于我个人能力问题,一定会有各种各样的疏漏,如有错误还望留言指出,非常感谢

pallet 属性宏允许定义一个在construct_runtime!中使用的pallet
它由模块定义:

#[pallet]
pub mod pallet {
...
}

在模块中宏解析一些具有以下属性的项目#[pallet::*],有些属性是强制,有些是可选的

这些属性是被不可实例化的pallets的语法解析的
如果要看pallet如何实例化工作,可以看下边这个例子

注意可以使用frame_support和frame_system中的pallet_perlude导入各种类型。

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

Config特性#[pallet::config]是需要强制导入的

这个特性是用来定义pallet中范型的特征的
必须像下面的方式进行定义

#[pallet::config]
pub trait Config: frame_system::Config + $optionally_some_other_supertraits
$optional_where_clause
{
...
}

例如.一个常规的Configtrait具有supertraitfrmae_system::Config, 还可以可选的加入其他的supertrait以及where子句

关联类型Event是被保留的,但是定义必须绑定From<Event>IsType<<Self as frame_system::Config>::Event>,
详情参照 #[pallet::event]
如果要放入关联类型Get到metedatas,使用属性宏#[pallet::constant],例如

#[pallet::config]
pub trait Config: frame_system::Config {
   	#[pallet::constant]
   	type Foo: Get<u32>;
}

要绕过frame_system::Config的supertrait检查,需要使用
#[pallet::disable_frame_system_supertrait_check]的属性宏,例如

#[pallet::config]
#[pallet::disable_frame_system_supertrait_check]
pub trait Config: pallet_timestamp::Config {}

宏展开:

使用#[pallet::constant]对pallet中的常数metadata进行宏展开

Pallet 结构的占位符:#[pallet::pallet]这是强制必须要有的

这个占位符的结构是用来实现pallet的一些信息
必须想下面的方式进行定义:

#[pallet::pallet]
pub struct Pallet<T>(_);

例如.定义一个拥有范型而没有where约束的Pallet结构体
创建一个拥有关联所有storage数据的Storetrait。
#[pallet::generate_store($vis trait Store)], 例如:

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

更确切的说,store trait包含了各个storage类型的关联。他通过实现Pallet来为pallet结构体提供访问storage的能力
当定义一个Foo的storage时,就可以通过Pallet进行访问 如:
<Pallet as Store>::Foo.

如果要创建一个完整的storage信息(用于Pov计算),可以使用属性宏
#[pallet::set_storage_max_encoded_len], 如:

#[pallet::pallet]
#[pallet::set_storage_max_encoded_len]
pub struct Pallet<T>(_);

这要求所有的storage实现trait[traits::StorageInfoTrait],然后所有的keys和value类型必须绑定[pallet_prelude::MaxEncodedLen].

扩展:

宏会添加以下属性到结构的定义中

#[derive(
	frame_support::CloneNoBound,
	frame_support::EqNoBound,
	frame_support::PartialEqNoBound,
	frame_support::RuntimeDebugNoBound,
)]

且会替换_类型为PhantomData<T>.

它在pallet上实现了:

  • [traits::GetPalletVersion]
  • ModuleErrorMetadata: 使用了错误的签名或者没有元数据.

It declare type Module type alias for Pallet, used by [construct_runtime].
他为Pallet声明了type Module类型的别名, 在[construct_runtime]中使用
It implements [traits::PalletInfoAccess] on Pallet to ease access to pallet informations
它在Pallet上实现了[traits::PalletInfoAccess], 能够轻松的访问pallet由[frame_support::traits::PalletInfo]提供的信息
(这个实现使用了关联类型frame_system::Config::PalletInfo)

他实现了为Pallet实现了[traits::StorageInfoTrait],可以获取所有storage去的信息

如果generate_store的属性被设置,则宏会创建Storetrait的实现。

如果属性中设置了set_storage_max_encoded_len 那么宏将会调用
[traits::StorageInfoTrait]为每个pallet中的所有storage实现[traits::StorageInfoTrait]
否则将会使用[traits::PartialStorageInfoTrait]为storage实现[traits::PartialStorageInfoTrait]

钩子: #[pallet::hooks] 可选

Implementation of Hooks on Pallet allowing to define some specific pallet logic.
Pallet上实现了Hooks,允许定义一些pallet的逻辑

必须向下面这样定义

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> $optional_where_clause {
}

例如. 一个绑定了范型T:Config的traitHooks<BlockNumberFor<T>>Pallet<T>的带where限定的实现

如果没有#[pallet::hooks]存在,则默认自动对应生成如下的代码

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}

宏展开:

宏实现了使用Hooks实现了OnInitialize, OnIdle, OnFinalize, OnRuntimeUpgrade,
OffchainWorker, IntegrityTest特性
注意 OnRuntimeUpgrade的实现是通过Hooks::on_runtime_upgrade以及一些附加逻辑实现的
例如写pallet版本到storage的逻辑

Call: #[pallet::call] 可选的

对pallet可调用部分的实现

他必须定义如下:

#[pallet::call]
impl<T: Config> Pallet<T> {
	/// $some_doc
	#[pallet::weight($ExpressionResultingInWeight)]
	pub fn $fn_name(
		origin: OriginFor<T>,
		$some_arg: $some_type,
		// or with compact attribute: #[pallet::compact] $some_arg: $some_type,
		...
	) -> DispatchResultWithPostInfo { // or `-> DispatchResult`
		...
	}
	...
}

例如. 一个对于Pallet<T>带范型T:Config和where约束的标准实现
每个可调用的函数需要用#[pallet::weight($expr)]定义一个weight
函数的地一个参数必须是origin: OriginFor<T>, 用以#[pallet::compact]实现紧凑编码,函数必须返回
DispatchResultWithPostInfo或者DispatchResult.

所有的参数都必须实现Debug, PartialEq, Eq, Decode, Encode, Clone.
frame_support::pallet_perlude中的traitMember即可简单的实现
如果不存在#[pallet::call],默认的将会自动生成如下代码

#[pallet::call]
impl<T: Config> Pallet<T> {}

警告: 修改这些可调用的对象,改变他们的顺序,删除一些必须小心.事实上浙江会修改外部runtime的调用类型(每个
pallet都有一个枚举变量),这些外部runtime的调用是可以存储到链上的(例如在pallet-scheduler)中,这可能会需要移植

宏展开

这个宏会创建一个Call的枚举 为每个可调用函数创建一个变量。这个枚举会实现
Clone, Eq, PartialEq, Debug (在not("std")不需要实现), Encode,
Decode, GetDispatchInfo, GetCallName, UnfilteredDispatchable.

这个宏在`Pallet`的实现,`Callable` trait和函数`call_functions`会返回 可调用的metadatas。

扩展内容: #[pallet::extra_constants] 可选

允许定义一些扩展内容放入常量metadata。

必须像下面的方式进行定义

#[pallet::extra_constants]
impl<T: Config> Pallet<T> where $optional_where_clause {
	/// $some_doc
	$vis fn $fn_name() -> $some_return_type {
		...
	}
	...
}

例如. 一个拥有可选的where限定与0个函数参数,0个范型,和一些返回值的标准的rust实现块

宏展开

这个宏添加一些扩展常熟到pallet的常数metadata中

错误: #[pallet::error] 可选

允许定义错误类型,用于在调用发生错误时返回。
这些错误类型的信息将会被加入metadata中

必须想下面这样定义

#[pallet::error]
pub enum Error<T> {
	/// $some_optional_doc
	$SomeFieldLessVariant,
	...
}

例。一个带有范型T且包含有变量的rust标准的枚举类型Error
范型T一定不能绑定任何数据和任何where限定。绑定和where限制不会在任何情况下被用到。

宏展开

这个宏实现了Debug trait 以及使用变量位置实现了as_u8,通过变量注释文档实现了as_str

实现了 From<Error<T>> for &'static str.也就是把错误转换成字符串
实现了From<Error<T>> for DispatchError.把错误转换成一个DispatchError的Result

Pallet实现了 ModuleErrorMetadata 定义了pallet的 ErrorMetadata

事件: #[pallet::event] 可选

允许定义pallet的事件,pallet的事件会在存储block的时候进行保存(在下一个区块中移除)。

定义方式如下

#[pallet::event]
#[pallet::metadata($SomeType = "$Metadata", $SomeOtherType = "$Metadata", ..)] // 可选
#[pallet::generate_deposit($visibility fn deposit_event)] // 可选
pub enum Event<$some_generic> $optional_where_clause {
	/// Some doc
	$SomeName($SomeType, $YetanotherType, ...),
	...
}

例.定义一个名为Event的枚举,支持没有范型,或者为T或T:Config的范型,where限定语句可有可无。

每个元素都必须实现 Clone, Eq, PartialEq, Encode, Decode, and Debug (on std only).
为了方便实现可以直接绑定frame_support::pallet_prelude中的Member trait。

变量的注释文档和元素类型都会被放入metadata中
属性#[pallet::metadata(..)]可以将某些类型指定要放置的metadata

metadata中要放置的类型使用如下方式定义

  • 如果在#[pallet::metadata(..)]中找到对类型的定义则会使用相应的metadata
  • 否则将会进行字符串化

示例:

#[pallet::event]
#[pallet::metadata(u32 = "SpecialU32")]
pub enum Event<T: Config> {
	Proposed(u32, T::AccountId),
}

这个写入事件的变量的metadata将会是"SpecialU32""T::AccountId".

属性 #[pallet::generate_deposit($visibility fn deposit_event)] 将会在Pallet中为deposit事件
创建一个辅助函数

注意:为了能够实例化pallet,时间必须拥有T和I的范型

宏展开:

宏将会为枚举类型Event添加如下属性

  • #[derive(frame_support::CloneNoBound)],
  • #[derive(frame_support::EqNoBound)],
  • #[derive(frame_support::PartialEqNoBound)],
  • #[derive(codec::Encode)],
  • #[derive(codec::Decode)],
  • #[derive(frame_support::RuntimeDebugNoBound)]

宏实现了空元组()的 From<Event<..>> .

宏为Event实现了返回EventMetadata的metadata函数

如果有 #[pallet::generate_deposit] 则宏会为Pallet实现fn deposit_event.

Storage: #[pallet::storage] 可选

允许在runtime的storage中定义一些抽象的storage数据。同时设置他们的metadata。
这个属性可以被多次使用

定义方式如下:

#[pallet::storage]
#[pallet::getter(fn $getter_name)] // 可选
$vis type $StorageName<$some_generic> $optional_where_clause
	= $StorageType<$generic_name = $some_generics, $other_name = $some_other, ...>;

or with unnamed generic

#[pallet::storage]
#[pallet::getter(fn $getter_name)] // 可选
$vis type $StorageName<$some_generic> $optional_where_clause
	= $StorageType<_, $some_generics, ...>;

I.e. 他的类型必须被定义为带有范型 T or T: Config且属于StorageValue, StorageMap
StorageDoubleMap (定义在 frame_support)
storage中的范型参数可以通过两种方式给出: 命名的和非命名的.
对于命名的范型参数: 每个参数的名称就是定义在storage中的名称
struct:

  • [pallet_prelude::StorageValue] 期望参数为 Value 和可选的 QueryKindOnEmpty,
  • [pallet_prelude::StorageMap] 期望参数为 Hasher, Key, Value 和可选的 QueryKind
    OnEmpty,
  • [pallet_prelude::StorageDoubleMap] 期望参数为Hasher1, Key1, Hasher2, Key2, Value
    可选的 QueryKindOnEmpty.

对于未命名的范型参数: 他们的地一个参数必须是 _ 因为他将被宏替换,且其他的范型必须是一个普通的rust范型定义。

宏使用PalletInfo::name::<Pallet<..>>()创建范型Prefix和storage类型的名称
例如. pallet中runtime的名称为"MyExample" 那么他的storagetype Foo<T> = ...使用的prefix为Twox128(b"MyExample") ++ Twox128(b"Foo").
可选参数 #[pallet::getter(fn $my_getter_fn_name)] 允许在Pallet中定义一个获取函数。

例如:

#[pallet::storage]
#[pallet::getter(fn my_storage)]
pub(super) type MyStorage<T> = StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;

或者

#[pallet::storage]
#[pallet::getter(fn my_storage)]
pub(super) type MyStorage<T> = StorageMap<_, Blake2_128Concat, u32, u32>;

可选属性 #[cfg(..)]允许对storage条件进行编译

E.g:

#[cfg(feature = "my-feature")]
#[pallet::storage]
pub(super) type MyStorage<T> = StorageValue<Value = u32>;

所有的cfg属性都会被自动复制到storage项目创建的时候,例如getter, storage prefix和metadata等。
注意:如果QueryKind范型参数在这个阶段依然是范型或者正在使用一些别名,则getter可能会失败,这种情况下需要手动实现getter

注意: 范型的`Haser`必须实现了[`StorageHasher`]特性(不然完全无法使用).我们使用[`StorageHasker::METADATA`]

用在storage数据的metadata上。这样就可以对数据的hasher进行支持

宏展开

宏为每一个storage item创建一个结构体,命名为
_GeneratedPrefixForStorage$NameOfStorage, 且使用pallet和storage名称实现
StorageInstance,然后它将用于他的地一个范型的别名

对于命名了的范型,宏将对其进行重新排序,然后移除名称

宏在Pallet上实现了storage_metadata函数,基于storage item的类型实现了metadate:

  • 对于value类型的storage, value的类型被拷贝进了metadata
  • 对于map类型的storage, values 和 key的类型被拷贝进了metadata
  • 对于double map类型的storage, key1,key2,value1,value2的类型被拷贝进了metadata

Type value: #[pallet::type_value] 可选

辅助实现结构提的 Get trait. 为方便使用storage类型
该属性可以多次使用

按如下方式使用

#[pallet::type_value]
fn $MyDefaultName<$some_generic>() -> $default_type $optional_where_clause { $expr }

I.e.: a function definition with generics none or T: Config and a returned type.
例如,一个定义了范型为none或者T:Config且具有一个返回类型的函数
E.g.:

#[pallet::type_value]
fn MyDefault<T: Config>() -> T::Balance { 3.into() }

注意: 该属性与 #[pallet::storage]一起使用,用于定义一些storage中特殊的默认值

宏展开

Macro renames the function to some internal name, generate a struct with the original name of
the function and its generic, and implement Get<$ReturnType> by calling the user defined
function.
宏重命名函数为一些内部名称,创建一个原始名称函数和范型的结构体。 然后通过调用该函数实现Get<$ReturnType>trait.

创世配置: #[pallet::genesis_config] 可选

允许定义pallet的创世配置

会被定义为enum或者struct
It needs to be public and implement trait GenesisBuild with #[pallet::genesis_build].
他需要是public的,且通过#[pallet::genesis_build]实现GenesisBuild trait
范型类型必须是 none 或 TT: Config.

例:

#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
	_myfield: BalanceOf<T>,
}

宏展开

宏展开后将会添加如下属性:

  • #[cfg(feature = "std")]
  • #[derive(Serialize, Deserialize)]
  • #[serde(rename_all = "camelCase")]
  • #[serde(deny_unknown_fields)]
  • #[serde(bound(serialize = ""))]
  • #[serde(bound(deserialize = ""))]

创世构建: #[pallet::genesis_build] 可选

允许定义如何构建genesis_configuration

使用方式如下

#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig<$maybe_generics> {
	fn build(&self) { $expr }
}

I.e. a rust trait implementation with generic T: Config, of trait GenesisBuild<T> on type
GenesisConfig with generics none or T.
例如.一个有着T:Config范型的GenesisBuild<T>的rust trait的关于GenesisConfig带范型T或不带范型的实现

例:

#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig {
	fn build(&self) {}
}

宏展开

宏展开后将会添加如下属性

  • #[cfg(feature = "std")]

宏将会实现 sp_runtime::BuildModuleGenesisStorage,使用 () 作为第二个范型参数给不可实例化的pallets.

Inherent: #[pallet::inherent] 可选

允许为pallet提供一些固有的东东:

使用方式如下:

#[pallet::inherent]
impl<T: Config> ProvideInherent for Pallet<T> {
	// ... regular trait implementation
}

I.e. a trait implementation with bound T: Config, of trait ProvideInherent for type
Pallet<T>, and some optional where clause.
例. 对Pallet<T>实现一个绑定T:Config范型以及一些where限定的ProvideInherent trait。

宏展开

宏当前并不是用此信息,但是将来可能要把信息提供给construct_runtime.

Validate unsigned: #[pallet::validate_unsigned] 可选

允许pallet去验证一些未签名的交易

用法如下:

#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
	// ... regular trait implementation
}

例. 一个实现了Pallet<T>的绑定了范型T:Config和部分where约束的 ValidateUnsigned trait.

注意:sp_runtime::traits::SignedExtension也能够用于对事物的验证添加一些特殊逻辑。

宏展开

Macro make currently no use of this information, but it might use this information in the
future to give information directly to construct_runtime.
宏对当前信息没什么用,但是将来会用这类信息提供给construct_runtime.

Origin: #[pallet::origin] 可选

允许为pallet定义一些origin.

必须是类型别名或者枚举或者结构体,且必须是public的

例:

#[pallet::origin]
pub struct Origin<T>(PhantomData<(T)>);

警告: 修改origin会改变外部的runtime origin.这个外部的origin能够被存储在链上(例如在pallet-scheduler中).
所以修改必须小心,因为可能需要移植.

注意: 对于可实例化的pallet,origin必须绑定T和I的范型

在可实例化的pallet

一个可实例化的pallet是通过Config进行范化的,例如Config<I>.他允许runtime同步哦不同类型的范型实例化多个pallet。
这是范型I的唯一目的
但是因为PalletInfo要求Pallet占位符是静态的,所以只要使用PalletInfo就必须绑定'static
而且为了让可实例化的pallet可用作没有实例化的普通pallet,最终要得一点就是绑定=()给所有类型

因此 实现绑定类似 impl<T: Config<I>, I: 'static>, 和类型看起来像
SomeType<T, I=()>SomeType<T: Config<I>, I: 'static = ()>.

没有实例的pallet实例.

pub use pallet::*; // 在crate命名空间为`construct_runtime!`重新导出

#[frame_support::pallet]
// 注意: pallet的名字实在`construct_runtime`中提供的,用于pallet存储的唯一标识符。所以pallet中不需要定义
pub mod pallet {
	use frame_support::pallet_prelude::*; // 导入在pallet中使用的变量类型
	use frame_system::pallet_prelude::*; // 导入一些系统辅助类型

	type BalanceOf<T> = <T as Config>::Balance;

	// 定义pallet范型参数
	// 这个宏会解析`#[pallet::constant]`属性 且使用他去为pallet的常熟创建metadata
	#[pallet::config]
	pub trait Config: frame_system::Config {
		#[pallet::constant] // 将常熟放入metadata
		type MyGetParam: Get<u32>;
		type Balance: Parameter + From<u8>;
		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
	}

	// 定义一些附加的常熟,并将其放入常熟metadata
	#[pallet::extra_constants]
	impl<T: Config> Pallet<T> {
		/// 一些表述
		fn exra_constant_name() -> u128 { 4u128 }
	}

	// 定义pallet结构的占位符,在其上实现各个pallet的功能
	#[pallet::pallet]
	#[pallet::generate_store(pub(super) trait Store)]
	pub struct Pallet<T>(_);

	// 定义pallet的钩子
	#[pallet::hooks]
	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
		fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
			unimplemented!();
		}

		// 还可以实现类似: on_finalize, on_runtime_upgrade, offchain_worker, ...
		// 详见 `Hooks` trait
	}

	// 定义一个可调用的结构和实现dispatchables
	//
	// 警告:每个函数的参数都必须实现了:Clone, Debug, Eq, PartialEq,Codec.
	//
	// 宏解析函数参数上的`#[pallet::compact]`且相应的实现`Call`的编解码
	#[pallet::call]
	impl<T: Config> Pallet<T> {
		/// 这里写要放入metadata中的注释
		#[pallet::weight(0)] // 定义这个调用方法的weight (函数参数也在范围内)
		pub fn toto(
			origin: OriginFor<T>,
			#[pallet::compact] _foo: u32,
		) -> DispatchResultWithPostInfo {
			let _ = origin;
			unimplemented!();
		}
	}

	// 定义pallet的枚举类型 `Error`  (这个是可选的).
	// 这个宏使用文档注释为每个变量创建metadata
	#[pallet::error]
	pub enum Error<T> {
		/// 要放入metadata的文档注释
		InsufficientProposersBalance,
	}

	// 定义pallet的枚举型事件 (this is optional).
	//
	// 警告: 每个使用的变量类型都必须实现: Clone, Debug, Eq, PartialEq, Codec.
	//
	// 这个宏创建事件的metadata,并且实现derive Clone, Debug, Eq, PartialEq and Codec
	#[pallet::event]
	// 通过给定的类型为附加参数制定使用的metadata
	#[pallet::metadata(BalanceOf<T> = "Balance", u32 = "Other")]
	// 创建一个函数用来存储事件
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
	pub enum Event<T: Config> {
		/// 要写入metadata的注释
		// `<T as frame_system::Config>::AccountId` 并没有定义在metadata表中, 
		// 因此元数据就是`<T as frame_system::Config>::AccountId`
		Proposed(<T as frame_system::Config>::AccountId),
		/// 文档
		// 这个的metadata表应当是`Balance`
		Spending(BalanceOf<T>),
		// 这个的metadata将会由`Other`metadata表中的Other定义
		Something(u32),
	}

	// 定义一个实现了`frame_support::traits::Get<T::Balance>`的结构体 (可选).
	#[pallet::type_value]
	pub(super) fn MyDefault<T: Config>() -> T::Balance { 3.into() }

   	// 定义一个storage的item,这个定义可以存在很多个(可选)
	//
	// 其类型必须是 `StorageValue`, `StorageMap` or `StorageDoubleMap`.之一
	// 这个宏创建了前缀类型,以及替换了地一个范型`_`
	//
	// 宏为storage使用的类型传见metadata的扩展
	// * 对于 storage value ,value的类型将会被复制到metadata中
	// * 对于storage map,value的类型和key的类型都会被复制到metadata中
	// * 对于storage double map,所有的value和key的类型都会被复制到metadata中
	//
	// NOTE: The generic `Hasher` must implement the `StorageHasher` trait (or the type is not
	// usable at all). We use [`StorageHasher::METADATA`] for the metadata of the hasher of the
	// storage item. Thus generic hasher is supported.
	// 注意:范型`Hasher`必须实现`StorageHasher` trait(不然无法使用)
	#[pallet::storage]
	pub(super) type MyStorageValue<T: Config> =
		StorageValue<Value = T::Balance, QueryKind = ValueQuery, OnEmpty = MyDefault<T>>;

	// 其他的类型声明
	#[pallet::storage]
	#[pallet::getter(fn my_storage)]
	pub(super) type MyStorage<T> =
		StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;

	// 生命创世config (可选).
	//
	// 宏允许是结构体或者枚举;他会检测范型是否一致
	//
	// 类型必须实现`Defalut` trait
	#[pallet::genesis_config]
	#[derive(Default)]
	pub struct GenesisConfig {
		_myfield: u32,
	}

	// 声明创世builder. (这个只有生命了创世config才需要生命)
	#[pallet::genesis_build]
	impl<T: Config> GenesisBuild<T> for GenesisConfig {
		fn build(&self) {}
	}

	// 声明pallet的origin (这个是可选的).
	//
	// 这个宏接受别名或者结构体或者枚举类型,他会检查范型是否一致
	#[pallet::origin]
	pub struct Origin<T>(PhantomData<T>);

	// 生命 validate_unsigned的实现 (可选的).
	#[pallet::validate_unsigned]
	impl<T: Config> ValidateUnsigned for Pallet<T> {
		type Call = Call<T>;
		fn validate_unsigned(
			source: TransactionSource,
			call: &Self::Call
		) -> TransactionValidity {
			Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
		}
	}

	// 声明pallet中 inherent的提供者  (这是可选的).
	#[pallet::inherent]
	impl<T: Config> ProvideInherent for Pallet<T> {
		type Call = Call<T>;
		type Error = InherentError;

		const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;

		fn create_inherent(_data: &InherentData) -> Option<Self::Call> {
			unimplemented!();
		}

		fn is_inherent(_call: &Self::Call) -> bool {
			unimplemented!();
		}
	}

	// 实现ProvideInherent trait的标准rust代码

	#[derive(codec::Encode, sp_runtime::RuntimeDebug)]
	#[cfg_attr(feature = "std", derive(codec::Decode))]
	pub enum InherentError {
	}

	impl sp_inherents::IsFatalError for InherentError {
		fn is_fatal_error(&self) -> bool {
			unimplemented!();
		}
	}

	pub const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = *b"testpall";
}

有实例的pallet示例

pub use pallet::*;

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

	type BalanceOf<T, I = ()> = <T as Config<I>>::Balance;

	#[pallet::config]
	pub trait Config<I: 'static = ()>: frame_system::Config {
		#[pallet::constant]
		type MyGetParam: Get<u32>;
		type Balance: Parameter + From<u8>;
		type Event: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::Event>;
	}

	#[pallet::extra_constants]
	impl<T: Config<I>, I: 'static> Pallet<T, I> {
		/// Some description
		fn exra_constant_name() -> u128 { 4u128 }
	}

	#[pallet::pallet]
	#[pallet::generate_store(pub(super) trait Store)]
	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);

	#[pallet::hooks]
	impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
	}

	#[pallet::call]
	impl<T: Config<I>, I: 'static> Pallet<T, I> {
		/// Doc comment put in metadata
		#[pallet::weight(0)]
		pub fn toto(origin: OriginFor<T>, #[pallet::compact] _foo: u32) -> DispatchResultWithPostInfo {
			let _ = origin;
			unimplemented!();
		}
	}

	#[pallet::error]
	pub enum Error<T, I = ()> {
		/// doc comment put into metadata
		InsufficientProposersBalance,
	}

	#[pallet::event]
	#[pallet::metadata(BalanceOf<T> = "Balance", u32 = "Other")]
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
	pub enum Event<T: Config<I>, I: 'static = ()> {
		/// doc comment put in metadata
		Proposed(<T as frame_system::Config>::AccountId),
		/// doc
		Spending(BalanceOf<T, I>),
		Something(u32),
	}

	#[pallet::type_value]
	pub(super) fn MyDefault<T: Config<I>, I: 'static>() -> T::Balance { 3.into() }

	#[pallet::storage]
	pub(super) type MyStorageValue<T: Config<I>, I: 'static = ()> =
		StorageValue<Value = T::Balance, QueryKind = ValueQuery, OnEmpty = MyDefault<T, I>>;

	#[pallet::storage]
	#[pallet::getter(fn my_storage)]
	pub(super) type MyStorage<T, I = ()> =
		StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;

	#[pallet::genesis_config]
	#[derive(Default)]
	pub struct GenesisConfig {
		_myfield: u32,
	}

	#[pallet::genesis_build]
	impl<T: Config<I>, I: 'static> GenesisBuild<T, I> for GenesisConfig {
		fn build(&self) {}
	}

	#[pallet::origin]
	pub struct Origin<T, I = ()>(PhantomData<(T, I)>);

	#[pallet::validate_unsigned]
	impl<T: Config<I>, I: 'static> ValidateUnsigned for Pallet<T, I> {
		type Call = Call<T, I>;
		fn validate_unsigned(
			source: TransactionSource,
			call: &Self::Call
		) -> TransactionValidity {
			Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
		}
	}

	#[pallet::inherent]
	impl<T: Config<I>, I: 'static> ProvideInherent for Pallet<T, I> {
		type Call = Call<T, I>;
		type Error = InherentError;

		const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;

		fn create_inherent(_data: &InherentData) -> Option<Self::Call> {
			unimplemented!();
		}

		fn is_inherent(_call: &Self::Call) -> bool {
			unimplemented!();
		}
	}

	// Regular rust code needed for implementing ProvideInherent trait

	#[derive(codec::Encode, sp_runtime::RuntimeDebug)]
	#[cfg_attr(feature = "std", derive(codec::Decode))]
	pub enum InherentError {
	}

	impl sp_inherents::IsFatalError for InherentError {
		fn is_fatal_error(&self) -> bool {
			unimplemented!();
		}
	}

	pub const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = *b"testpall";
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值