SETP6,开发一个存证模块

简介

开发

下载3.0.0版本的Substrate-node-template 代码

建立存证模块

  • 终端下进入项目目录的 pallets 文件夹后输入如下命令:
cd pallets
cargo new --lib poe 
  • 为了简单,COPY template/Cargo.toml 到 poe/Cargo.toml 上做简单修改。
name = 'pallet-poe'

(可选) 添加 main.rs 的依赖关系

  • 我一般会这样做,这样会方便某些 IDE 更好的预分析代码结构:
  • 修改 node/Cargo.toml
[dependencies]
...
# 添加进去,加入存证的依赖关系,方便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 模块,这里面定义了一些基础类型,比如这里用到的 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 宏创建 pallet
#[frame_support::pallet]
pub mod pallet {
    // 引入需要的包
    use frame_support::{
        dispatch::DispatchResultWithPostInfo,
        pallet_prelude::*,
    };
    // 比较粗暴的引用 frame_system 所有宏函数,和系统类型信息
    use frame_system::pallet_prelude::*;
    use sp_std::vec::Vec;

    //创建配置接口,通过 config 宏完成
    //继承自系统模块的 Config 接口,只有一个
    #[pallet::config]
    pub trait Config: frame_system::Config {
        // 只有一个关联类型就是 Event,并且约束
        // 可以从本模块的Event 类型进行转换,并且它的类型是一个系统模块的Event 类型。
        type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
    }

    // 定义一个结构体类型,来承载整个功能模块,使用 pallet::pallet 这个宏进行定义
    #[pallet::pallet]
    // 表示这个模块依赖的存储单元,一级存储单元依赖的 trait
    #[pallet::generate_store(pub (super) trait Store)]
    pub struct Pallet<T>(_);

    // 通过 storage 宏来定义存储类型,用来存储存证
    #[pallet::storage]
    // 这里定义的getter方法可以通过前段接口进行调用 my_proofs 方法来查询连上的状态,也就是说没必要单独写一个读取的接口。
    #[pallet::getter(fn my_proofs)]
    pub type Proofs<T: Config> = StorageMap<
        _,
        Blake2_128Concat,
        Vec<u8>, // 存证的哈希值
        (T::AccountId, T::BlockNumber) // 值时两个元素的tuple,第一个是AccountId, 第二个存储区块高度。
    >;

    // 通过 Event 定义一个时间存储类型,这是一个枚举。
    #[pallet::event]
    // 生成一个 转换后的 metadata 方便前段接收
    #[pallet::metadata(T::AccountId = "AccountId")]
    // 生成一个帮助性的方法,方便这个方法进行触发。
    #[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> {
        // 定义一个错误信息,存证已经存在
        ProofAlreadyExist,
        ClaimNotExist,
        NotClaimOwner,
    }

    // 定义一个 hooks ,如果有初始化区块的信息可以放到这里面,如果没有这个也必须要加上否则会报错
    #[pallet::hooks]
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}

    // 构建可调用函数,通过 call 这个宏
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        // 为了测试方便起见,weight 给入 0 值,实际应用中需要根据实际情况进行预估。
        #[pallet::weight(0)]
        pub fn create_claim(
            // 这个参数表示交易的发送方
            origin: OriginFor<T>,
            // 表示存证的哈希值,对应 38行。
            claim: Vec<u8>,
        ) -> DispatchResultWithPostInfo { // 返回值是一个Result类型的别名它会包含一些weight的信息,这是通过use引入进来的
            // TODO:: 写入创建存证的逻辑。
            // 验证签名信息是否合法
            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()),
            );

            // 发送事件
            Self::deposit_event(Event::ClaimCreated(sender, claim));
            // 返回结果信息,并进行类型转换。
            Ok(().into())
        }

        // 建立一个吊销存证的方法
        #[pallet::weight(0)]
        pub fn revoke_claim(
            origin: OriginFor<T>,
            claim: Vec<u8>,
        ) -> DispatchResultWithPostInfo {
            // 先验证 origin
            let sender = ensure_signed(origin)?;
            let (owner, _) = Proofs::<T>::get(&claim).ok_or(Error::<T>::ClaimNotExist)?;
            // 判断发送者和存证所有者是否是同一个人
            ensure!(owner == sender, Error::<T>::NotClaimOwner);
            // 删除存证
            Proofs::<T>::remove(&claim);
            // 发送存证Revoked事件
            Self::deposit_event(Event::ClaimRevoked(sender, claim));
            // 返回函数成功结果
            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>},
    }
);

测试

结束

  • 感谢阅读
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容