欢迎转载,转载请注名 《霜之小刀》,邮箱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
{
...
}
例如.一个常规的Config
trait具有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数据的Store
trait。
#[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的属性被设置,则宏会创建Store
trait的实现。
如果属性中设置了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
和可选的QueryKind
或OnEmpty
, - [
pallet_prelude::StorageMap
] 期望参数为Hasher
,Key
,Value
和可选的QueryKind
或
OnEmpty
, - [
pallet_prelude::StorageDoubleMap
] 期望参数为Hasher1
,Key1
,Hasher2
,Key2
,Value
和
可选的QueryKind
或OnEmpty
.
对于未命名的范型参数: 他们的地一个参数必须是 _
因为他将被宏替换,且其他的范型必须是一个普通的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 或 T
或 T: 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";
}