簡介
開發(fā)
下載3.0.0版本的Substrate-node-template 代碼
建立存證模塊
- 終端下進(jìn)入項(xiàng)目目錄的 pallets 文件夾后輸入如下命令:
cd pallets
cargo new --lib poe
- 為了簡單,COPY template/Cargo.toml 到 poe/Cargo.toml 上做簡單修改。
name = 'pallet-poe'
(可選) 添加 main.rs 的依賴關(guān)系
- 我一般會這樣做,這樣會方便某些 IDE 更好的預(yù)分析代碼結(jié)構(gòu):
- 修改 node/Cargo.toml
[dependencies]
...
# 添加進(jìn)去,加入存證的依賴關(guān)系,方便IDE找到程序入口
pallet-poe = { path ='../pallets/poe' , version='3.0.0' }
...
修改 pallets/poe/Cargo.toml
[package]
authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
description = 'FRAME pallet template for defining custom runtime logic.'
edition = '2018'
homepage = 'https://substrate.dev'
license = 'Unlicense'
name = 'pallet-poe'
repository = 'https://github.com/substrate-developer-hub/substrate-node-template/'
version = '3.0.0'
[package.metadata.docs.rs]
targets = ['x86_64-unknown-linux-gnu']
# alias "parity-scale-code" to "codec"
[dependencies.codec]
default-features = false
features = ['derive']
package = 'parity-scale-codec'
version = '2.0.0'
[dependencies]
frame-support = { default-features = false, version = '3.0.0' }
frame-system = { default-features = false, version = '3.0.0' }
#需要注意引入 sp-std 模塊,這里面定義了一些基礎(chǔ)類型,比如這里用到的 vec::Vec
sp-std = { default-features = false, version = '3.0.0' }
[dev-dependencies]
serde = { version = "1.0.119" }
sp-core = { default-features = false, version = '3.0.0' }
sp-io = { default-features = false, version = '3.0.0' }
sp-runtime = { default-features = false, version = '3.0.0' }
[features]
default = ['std']
std = [
'codec/std',
'frame-support/std',
'frame-system/std',
]
修改 pallets/poe/src/lib.rs
#![cfg_attr(not(feature = "std"), no_std)]
pub use pallet::*;
// 首先通過 frame_support::pallet 宏創(chuàng)建 pallet
#[frame_support::pallet]
pub mod pallet {
// 引入需要的包
use frame_support::{
dispatch::DispatchResultWithPostInfo,
pallet_prelude::*,
};
// 比較粗暴的引用 frame_system 所有宏函數(shù),和系統(tǒng)類型信息
use frame_system::pallet_prelude::*;
use sp_std::vec::Vec;
//創(chuàng)建配置接口,通過 config 宏完成
//繼承自系統(tǒng)模塊的 Config 接口,只有一個
#[pallet::config]
pub trait Config: frame_system::Config {
// 只有一個關(guān)聯(lián)類型就是 Event,并且約束
// 可以從本模塊的Event 類型進(jìn)行轉(zhuǎn)換,并且它的類型是一個系統(tǒng)模塊的Event 類型。
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
}
// 定義一個結(jié)構(gòu)體類型,來承載整個功能模塊,使用 pallet::pallet 這個宏進(jìn)行定義
#[pallet::pallet]
// 表示這個模塊依賴的存儲單元,一級存儲單元依賴的 trait
#[pallet::generate_store(pub (super) trait Store)]
pub struct Pallet<T>(_);
// 通過 storage 宏來定義存儲類型,用來存儲存證
#[pallet::storage]
// 這里定義的getter方法可以通過前段接口進(jìn)行調(diào)用 my_proofs 方法來查詢連上的狀態(tài),也就是說沒必要單獨(dú)寫一個讀取的接口。
#[pallet::getter(fn my_proofs)]
pub type Proofs<T: Config> = StorageMap<
_,
Blake2_128Concat,
Vec<u8>, // 存證的哈希值
(T::AccountId, T::BlockNumber) // 值時兩個元素的tuple,第一個是AccountId, 第二個存儲區(qū)塊高度。
>;
// 通過 Event 定義一個時間存儲類型,這是一個枚舉。
#[pallet::event]
// 生成一個 轉(zhuǎn)換后的 metadata 方便前段接收
#[pallet::metadata(T::AccountId = "AccountId")]
// 生成一個幫助性的方法,方便這個方法進(jìn)行觸發(fā)。
#[pallet::generate_deposit(pub (super) fn deposit_event)]
pub enum Event<T: Config> {
ClaimCreated(T::AccountId, Vec<u8>),
ClaimRevoked(T::AccountId, Vec<u8>),
}
// 通過 error 宏定義一個錯誤信息
#[pallet::error]
pub enum Error<T> {
// 定義一個錯誤信息,存證已經(jīng)存在
ProofAlreadyExist,
ClaimNotExist,
NotClaimOwner,
}
// 定義一個 hooks ,如果有初始化區(qū)塊的信息可以放到這里面,如果沒有這個也必須要加上否則會報(bào)錯
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
// 構(gòu)建可調(diào)用函數(shù),通過 call 這個宏
#[pallet::call]
impl<T: Config> Pallet<T> {
// 為了測試方便起見,weight 給入 0 值,實(shí)際應(yīng)用中需要根據(jù)實(shí)際情況進(jìn)行預(yù)估。
#[pallet::weight(0)]
pub fn create_claim(
// 這個參數(shù)表示交易的發(fā)送方
origin: OriginFor<T>,
// 表示存證的哈希值,對應(yīng) 38行。
claim: Vec<u8>,
) -> DispatchResultWithPostInfo { // 返回值是一個Result類型的別名它會包含一些weight的信息,這是通過use引入進(jìn)來的
// TODO:: 寫入創(chuàng)建存證的邏輯。
// 驗(yàn)證簽名信息是否合法
let sender = ensure_signed(origin)?;
// 判斷存證信息是否存在
ensure!(!Proofs::<T>::contains_key(&claim), Error::<T>::ProofAlreadyExist);
// 插入存證
Proofs::<T>::insert(
&claim,
(sender.clone(), frame_system::Pallet::<T>::block_number()),
);
// 發(fā)送事件
Self::deposit_event(Event::ClaimCreated(sender, claim));
// 返回結(jié)果信息,并進(jìn)行類型轉(zhuǎn)換。
Ok(().into())
}
// 建立一個吊銷存證的方法
#[pallet::weight(0)]
pub fn revoke_claim(
origin: OriginFor<T>,
claim: Vec<u8>,
) -> DispatchResultWithPostInfo {
// 先驗(yàn)證 origin
let sender = ensure_signed(origin)?;
let (owner, _) = Proofs::<T>::get(&claim).ok_or(Error::<T>::ClaimNotExist)?;
// 判斷發(fā)送者和存證所有者是否是同一個人
ensure!(owner == sender, Error::<T>::NotClaimOwner);
// 刪除存證
Proofs::<T>::remove(&claim);
// 發(fā)送存證Revoked事件
Self::deposit_event(Event::ClaimRevoked(sender, claim));
// 返回函數(shù)成功結(jié)果
Ok(().into())
}
}
}
配置 runtime/Cargo.toml
[dependencies]
#... 此處省略
# local dependencies
pallet-template = { path = '../pallets/template', default-features = false, version = '3.0.0' }
# 引入我們定義的 poe 模塊
pallet-poe = { path = '../pallets/poe', default-features = false, version = '3.0.0' }
#... 此處省略
std = [
#... 此處省略
'pallet-template/std',
'pallet-poe/std', # 引入POE的 STD
#... 此處省略
]
修改 runtime/src/lib.rs
---- 省略 ----
/// Configure the template pallet in pallets/template.
impl pallet_template::Config for Runtime {
type Event = Event;
}
/// 定義我們的配置模塊接口
impl pallet_poe::Config for Runtime {
type Event = Event;
}
---- 省略 ----
// Create the runtime by composing the FRAME pallets that were previously configured.
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
--- 省略 ---
// Include the custom logic from the template pallet in the runtime.
TemplateModule: pallet_template::{Module, Call, Storage, Event<T>},
// 將自定義的POE模塊加入runtime中。
PoeModule: pallet_poe::{Module, Call, Storage, Event<T>},
}
);
測試
結(jié)束