SETP6,開發(fā)一個存證模塊

簡介

開發(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é)束

  • 感謝閱讀
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容