使用Substrate开发区块链存证应用V2.0

使用Substrate开发区块链存证应用V2.0

jasonruan 2020.12.02

1 前言

本文是《使用Substrate开发区块链存证dApp》一文的更新,在一台全新服务器上,从零起步,采用最新的v2.0.0版本开发一个自定义的区块链存证dApp

本文内容参考:https://substrate.dev/docs/en/tutorials/build-a-dapp/

2 前置准备

2.1 vim安装

大家请根据自己习惯选择自己合拍的IDE,这里采用vim

[Jason@RUAN:~/Soft]$ yum install -y git ncurses-devel.x86_64
[Jason@RUAN:~/Soft]$ git clone https://github.com/vim/vim.git
[Jason@RUAN:~/Soft/vim] (master)$ git checkout -b v8.2.2020
切换到一个新分支 'v8.2.2020'
[Jason@RUAN:~/Soft/vim] (v8.2.2020)$ ./configure  --with-features=huge --enable-python3interp --prefix=/root/Soft/ALL
[Jason@RUAN:~/Soft/vim] (v8.2.2020)$ make -j2 && make install

2.2 rust安装

Substrate是由rust语言开发,首先需要安装rust环境。

2.2.1 软件安装

  • Rust的安装比较简单,执行如下一条命令即可,该命令将自动完成软件包的下载、安装、环境变量设置:
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • 安装成功后,会显示如下日志
  stable-x86_64-unknown-linux-gnu installed - rustc 1.48.0 (7eac88abb 2020-11-16)
Rust is installed now. Great!
  • 安装完成后,在~/.cargo/bin目录可以看到相关命令行工具
.cargo/bin/
├── cargo
├── cargo-clippy
├── cargo-fmt
├── cargo-miri
├── clippy-driver
├── rls
├── rustc
├── rustdoc
├── rustfmt
├── rust-gdb
├── rust-lldb
└── rustup

2.2.2 环变设置

  • 执行下面命令,即将export PATH="$HOME/.cargo/bin:$PATH",追加到~/.bashrc
$ cat ~/.cargo/env >> ~/.bashrc
  • 执行下面命令,使得添加的环境变量生效
$ . ~/.bashrc
  • 可执行如下命令查看安装版本
$ rustc --version
rustc 1.48.0 (7eac88abb 2020-11-16)

2.2.3 配套安装

(1) Racer安装

Racer是一个由Rust爱好者提供的Rust自动补全和语法分析工具,被用来提供基本的补全功能和自定义跳转功能。本身完全由Rust写成,补全功能比较完善。

  • 安装命令

需要切换到nightly版,否则安装会报错:error[E0554]: #![feature] may not be used on the stable release channel
请先执行下面命令,切换到nightly版本后,再进行安装:
$ rustup install nightly
nightly-x86_64-unknown-linux-gnu installed - rustc 1.50.0-nightly (1c389ffef 2020-11-24)$ rustup default nightly
info: using existing install for 'nightly-x86_64-unknown-linux-gnu'
info: default toolchain set to 'nightly-x86_64-unknown-linux-gnu'
nightly-x86_64-unknown-linux-gnu unchanged - rustc 1.50.0-nightly (1c389ffef 2020-11-24)$ rustc --version
rustc 1.50.0-nightly (1c389ffef 2020-11-24)

$ cargo install racer
......
    Finished release [optimized] target(s) in 4m 21s
  Installing /root/.cargo/bin/racer
   Installed package `racer v2.1.40` (executable `racer`)
  • 查看版本
$ racer -V
racer 2.1.40

(2) 源码下载

为了对Rust标准库进行补全,Racer需要获取Rust源码路径。通过rustup获取源码的好处是rustup update可以随时获取最新代码

  • 获取源码
$ rustup component add rust-src
info: downloading component 'rust-src'
info: installing component 'rust-src'
  • 更新源码
$ rustup update
info: checking for self-updates

   stable-x86_64-unknown-linux-gnu unchanged - rustc 1.45.2 (d3fb005a3 2020-07-31)
  nightly-x86_64-unknown-linux-gnu unchanged - rustc 1.47.0-nightly (6c8927b0c 2020-07-26)

info: cleaning up downloads & tmp directories
  • 环变设置

.bashrc中添加以下内容:

export RUST_SRC_PATH="$(rustc --print sysroot)/lib/rustlib/src/rust/src"

2.3 yarn安装

Substrate前端模板工程(front-end-template)是使用yarn进行包管理的,在此我们进行安装。

安装步骤参考:https://classic.yarnpkg.com/en/docs/install/

$ curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
[yarn]
name=Yarn Repository
baseurl=https://dl.yarnpkg.com/rpm/
enabled=1
gpgcheck=1
gpgkey=https://dl.yarnpkg.com/rpm/pubkey.gpg

$ curl --silent --location https://rpm.nodesource.com/setup_12.x | sudo bash -

$ sudo yum install yarn

$ yarn --version
1.22.5

3 存证dApp后端节点开发

存证dApp后端节点是基于node-template来开发,它是一个基于FRAMESubstrate后端节点,可以在其基础上,进行修改以便快速搭建属于自己的Substrate网络。

3.1 node-template安装

  • 版本

v2.0.0

  • 下载
[Jason@RUAN:~/Blockchain]$ git clone git@github.com:substrate-developer-hub/substrate-node-template.git

[Jason@RUAN:~/Blockchain/substrate-node-template] (master)$ git checkout -b v2.0.0 v2.0.0
切换到一个新分支 'v2.0.0'

[Jason@RUAN:~/Blockchain/substrate-node-template] (v2.0.0)$ 
  • 编译

使用当前nightly最新版本会存在问题,需要执行下面命令,避免后续编译错误:
Jason@RUAN:~/Blockchain/substrate-node-template$ rustup target add wasm32-unknown-unknown --toolchain nightly-2020-10-05
Jason@RUAN:~/Blockchain/substrate-node-template$ rustup default nightly-2020-10-05
Jason@RUAN:~/Blockchain/substrate-node-template$ rustc --version
rustc 1.49.0-nightly (beb5ae474 2020-10-04)
Jason@RUAN:~/Blockchain/substrate-node-template$ yum install -y llvm-devel clang-devel

[Jason@RUAN:~/Blockchain/substrate-node-template] (v2.0.0)$ cargo build --release
    Finished release [optimized] target(s) in 38m 22s
  • 编译错误及处理

编译错误1
错误描述 Finished release [optimized] target(s) in 2m 51s
Running `/root/Yikuai/substrate-node-template/target/release/wbuild-runner/node-template-runtime3424067592371620269/target/x86\\_64-unknown-linux-gnu/release/wasm-build-runner-impl`
Rust WASM toolchain not installed, please install it!
warning: build failed, waiting for other jobs to finish...
error: build failed解决办法$ rustup target add wasm32-unknown-unknown --toolchain nightly
info: downloading component 'rust-std' for 'wasm32-unknown-unknown'
info: installing component 'rust-std' for 'wasm32-unknown-unknown'
info: Defaulting to 500.0 MiB unpack ram编译错误2
错误描述warning: couldn't execute `llvm-config --prefix` (error: No such file or directory (os error 2))
warning: set the LLVM\\_CONFIG\\_PATH environment variable to the full path to a valid `llvm-config` executable (including the executable itself)

error: failed to run custom build command for `librocksdb-sys v6.7.4`解决办法$ yum install -y llvm-devel编译错误3
错误描述 Compiling librocksdb-sys v6.7.4
error: failed to run custom build command for `librocksdb-sys v6.7.4`

Caused by:
process didn't exit successfully: `/root/Yikuai/substrate-node-template/target/release/build/librocksdb-sys-1bb53efdfd682ab6/build-script-build` (exit code: 101)
--- stdout
cargo:rerun-if-changed=build.rs

--- stderr
thread 'main' panicked at 'Unable to find libclang: "couldn\\'t find any valid shared libraries matching: [\\'libclang.so\\', \\'libclang-\\*.so\\', \\'libclang.so.\\*\\', \\'libclang-\\*.so.\\*\\'], set the `LIBCLANG\\_PATH` environment variable to a path where one of these files can be found (invalid: [])"', /root/.cargo/registry/src/github.com-1ecc6299db9ec823/bindgen-0.53.3/src/lib.rs:1956:31解决办法$ yum install -y clang-devel
```**编译错误4**
**错误描述**
```bash
Compiling sp-arithmetic v2.0.0
error[E0282]: type annotations needed
--> /root/.cargo/registry/src/github.com-1ecc6299db9ec823/sp-arithmetic-2.0.0/src/fixed\\_point.rs:541:9
|
541 | let accuracy = P::ACCURACY.saturated\\_into();
| ^^^^^^^^ consider giving `accuracy` a type解决办法[Jason@RUAN:~]$ rustup install nightly-2020-10-05
nightly-2020-10-05-x86\\_64-unknown-linux-gnu installed - rustc 1.49.0-nightly (beb5ae474 2020-10-04)

[Jason@RUAN:~]$ rustup target add wasm32-unknown-unknown --toolchain nightly-2020-10-05
[Jason@RUAN:~]$ rm -rf target
[Jason@RUAN:~]$ WASM\\_BUILD\\_TOOLCHAIN=nightly-2020-10-05 cargo build --release

3.2 存证pallet开发

Substrate运行时由FRAME pallets组成。这些pallets可以被认为是定义你的区块链能够做什么的一个个独立的逻辑单元。
Substrate已经提供了许多预置pallets,用于基于FRAME的运行时。如下图所示:

image.png

例如,FRAME中包含一个balancespallet,这个pallet通过管理系统中所有账户余额来控制你的区块链系统中的基础货币。如果你想向你的区块链系统中添加智能合约功能,你只需要包含合约**pallet**即可。

本节我们就是要开发一个存证pallet,并将其添加到我们自定义的区块链中。

3.2.1 创建poe pallet工程目录

pos => Proof Of Existence

[Jason@RUAN:~/Blockchain/substrate-node-template/pallets] (v2.0.0)$ cargo new --lib poe
[Jason@RUAN:~/Blockchain/substrate-node-template/pallets/poe] (v2.0.0)$ tree
.
├── Cargo.toml
└── src
    └── lib.rs

3.2.2 代码框架

在新生成lib.rs文件中,填写以下代码框架,这也是从宏观角度来讲,Substrate pallet可以拆分成的6个部分:

// 1. Imports
use frame_support::{decl_module, decl_storage, decl_event, decl_error, dispatch};
use frame_system::{self as system, ensure_signed};

// 2. Pallet Configuration
pub trait Trait: system::Trait { /* --snip-- */ }

// 3. Pallet Storage Items
decl_storage! { /* --snip-- */ }

// 4. Pallet Events
decl_event! { /* --snip-- */ }

// 5. Pallet Errors
decl_error! { /* --snip-- */ }

// 6. Callable Pallet Functions
decl_module! { /* --snip-- */ }

3.2.3 添加依赖

(1)完善引用

#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{
    decl_module, decl_storage, decl_event, decl_error, ensure, StorageMap
};
use frame_system::{self as system, ensure_signed};
use sp_std::vec::Vec;

(2)完善Cargo.toml文件

pallets/template/Cargo.toml拷贝至pallets/poe目录,按以下方式进行新增和修改:

[package]
authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
description = 'FRAME pallet poe'                               		                       # Modify
edition = '2018'
homepage = 'https://substrate.dev'
license = 'Unlicense'
name = 'pallet-poe'                                             					                   	# Modify
repository = 'https://github.com/substrate-developer-hub/substrate-node-template/'
version = '2.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 = '1.3.4'

[dependencies]
frame-support = { default-features = false, version = '2.0.0' }
frame-system = { default-features = false, version = '2.0.0' }
sp-std = { default-features = false, version = '2.0.0' }                       # Add

[dev-dependencies]
sp-core = { default-features = false, version = '2.0.0' }
sp-io = { default-features = false, version = '2.0.0' }
sp-runtime = { default-features = false, version = '2.0.0' }

[features]
default = ['std']
std = [
    'codec/std',
    'frame-support/std',
    'frame-system/std',
    'sp-std/std',                                                                                                   # Add
]

3.2.4 配置pallet

每一个pallet都有一个配置trait

// 2. Pallet Configuration
pub trait Trait: system::Trait {
    /// The overarching event type.
    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
} 

3.2.5 定义事件

事件:可以展示pallet成功被调用的时间和信息。

// 4. Pallet Events 
decl_event! {       
    pub enum Event<T> where AccountId = <T as system::Trait>::AccountId {
        /// Event emitted when a proof has been claimed.
        ClaimCreated(AccountId, Vec<u8>),
        /// Event emitted when a claim is revoked by the owner.
        ClaimRevoked(AccountId, Vec<u8>),
    }
}

我们的存证palet,包含了以下事件:

  • ClaimCreated:存证创建
  • ClaimRevoked:存证撤销

事件可以包含一些附加数据,例如:

  • AccountId:谁触发了事件
  • Vec<u8>:存储或撤销的存证数据

3.2.6 定义错误

错误:可以展示pallet调用失败的时间,及失败原因。

// 5. Pallet Errors
decl_error! {
    pub enum Error for Module<T: Trait> {
        /// This proof has already been claimed
        ProofAlreadyClaimed,
        /// The proof does not exist, so it cannot be revoked
        NoSuchProof,                                                                                                    
        /// The proof is claimed by another account, so caller can't revoke it
        NotProofOwner,
    }
}

3.2.7 定义存储

要添加一个新的存证到我们的区块链上,就是要将其存储到我们的pallet的存储里面。在这里创建我们的存储结构。

// 3. Pallet Storage Items
decl_storage! { 
    trait Store for Module<T: Trait> as TemplateModule {
        /// The storage item for our proofs.
        /// It maps a proof to the user who made the claim and when they made it.
        Proofs: map hasher(blake2_128_concat) Vec<u8> => (T::AccountId, T::BlockNumber);                                                                                          
    }   
}  

3.2.8 实现接口

// 6. Callable Pallet Functions
decl_module! {
    /// The module declaration.
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        // Initializing errors
        // this includes information about your errors in the node's metadata.
        // it is needed only if you are using errors in your pallet
        type Error = Error<T>;

        // A default function for depositing events
        fn deposit_event() = default;

        /// Allow a user to claim ownership of an unclaimed proof
        #[weight = 10_000]
        fn create_claim(origin, proof: Vec<u8>) {
            // Verify that the incoming transaction is signed and store who the
            // caller of this function is.
            let sender = ensure_signed(origin)?;

            // Verify that the specified proof has not been claimed yet or error with the message
            ensure!(!Proofs::<T>::contains_key(&proof), Error::<T>::ProofAlreadyClaimed);

            // Call the `system` pallet to get the current block number
            let current_block = <system::Module<T>>::block_number();

            // Store the proof with the sender and the current block number
            Proofs::<T>::insert(&proof, (&sender, current_block));

            // Emit an event that the claim was created
            Self::deposit_event(RawEvent::ClaimCreated(sender, proof));
        }

        /// Allow the owner to revoke their claim
        #[weight = 10_000]
        fn revoke_claim(origin, proof: Vec<u8>) {
            // Determine who is calling the function
            let sender = ensure_signed(origin)?;

            // Verify that the specified proof has been claimed
            ensure!(Proofs::<T>::contains_key(&proof), Error::<T>::NoSuchProof);

            // Get owner of the claim
            let (owner, _) = Proofs::<T>::get(&proof);

            // Verify that sender of the current call is the claim owner
            ensure!(sender == owner, Error::<T>::NotProofOwner);

            // Remove claim from storage
            Proofs::<T>::remove(&proof);

            // Emit an event that the claim was erased
            Self::deposit_event(RawEvent::ClaimRevoked(sender, proof));
        }
    }
}

3.2.9 完善runtime配置

  • 修改runtime/Cargo.toml
pallet-poe = { path = '../pallets/poe', default-features = false, version = '2.0.0' }                     # <-- 增加行 

std = [
    'codec/std',
    'serde',
    'frame-executive/std',
    'frame-support/std',
    'frame-system/std',
    'frame-system-rpc-runtime-api/std',
    'pallet-aura/std',
    'pallet-balances/std',
    'pallet-grandpa/std',
    'pallet-randomness-collective-flip/std',
    'pallet-sudo/std',
    'pallet-template/std',
    'pallet-poe/std',                                                                                                                                             # <-- 增加行      
    'pallet-timestamp/std',
    'pallet-transaction-payment/std',
    'pallet-transaction-payment-rpc-runtime-api/std',
    'sp-api/std',
    'sp-block-builder/std',
    'sp-consensus-aura/std',
    'sp-core/std',
    'sp-inherents/std',
    'sp-offchain/std',
    'sp-runtime/std',
    'sp-session/std',
    'sp-std/std',
    'sp-transaction-pool/std',
    'sp-version/std',
]
  • 修改runtime/src/lib.rs
//  增加代码块
impl pallet_poe::Trait 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
	{
		System: frame_system::{Module, Call, Config, Storage, Event<T>},
		RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},
		Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent},
		Aura: pallet_aura::{Module, Config<T>, Inherent},
		Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event},
		Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
		TransactionPayment: pallet_transaction_payment::{Module, Storage},
		Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
		// Include the custom logic from the template pallet in the runtime.
		TemplateModule: pallet_template::{Module, Call, Storage, Event<T>},
		PoeModule: pallet_poe::{Module, Call, Storage, Event<T>},					                // <-- 增加代码行
	}
);

3.3 node-template节点编译

完成存证pallet的开发后,需要重新编译节点。

[Jason@RUAN:~/Blockchain/substrate-node-template] (v2.0.0)$ cargo build --release
   Compiling node-template-runtime v2.0.0 (/root/Blockchain/substrate-node-template/runtime)
   Compiling pallet-poe v2.0.0 (/root/Blockchain/substrate-node-template/pallets/poe)
   Compiling node-template v2.0.0 (/root/Blockchain/substrate-node-template/node)
    Finished release [optimized] target(s) in 12m 18s

3.4 node-template节点启动

参数说明:
--dev:solo模式启动
--tmp:数据会存入临时文件夹中,方便测试,无需使用purge-chain去清理数据文件
--ws-external:开启websocket端口
--rpc-external:开启rpc端口
--rpc-cors:开启跨域请求

[Jason@RUAN:~/Blockchain/substrate-node-template] (v2.0.0)$ ./target/release/node-template --dev --tmp --ws-external --rpc-external --rpc-cors=all
Dec 06 23:25:11.106  INFO Substrate Node    
Dec 06 23:25:11.106  INFO ✌️  version 2.0.0-655bfdc-x86_64-linux-gnu    
Dec 06 23:25:11.106  INFO ❤️  by Substrate DevHub <https://github.com/substrate-developer-hub>, 2017-2020    
Dec 06 23:25:11.106  INFO ? Chain specification: Development    
Dec 06 23:25:11.106  INFO ?  Node name: nippy-songs-2834    
Dec 06 23:25:11.106  INFO ? Role: AUTHORITY    
Dec 06 23:25:11.106  INFO ? Database: RocksDb at /tmp/substratePezRNP/chains/dev/db    
Dec 06 23:25:11.106  INFO ⛓  Native runtime: node-template-1 (node-template-1.tx1.au1)    
Dec 06 23:25:11.276  INFO ? Initializing Genesis block/state (state: 0xc7ad…6e3c, header-hash: 0xc36d…0566)    
Dec 06 23:25:11.277  INFO ? Loading GRANDPA authority set from genesis on what appears to be first startup.    
Dec 06 23:25:11.299  INFO ⏱  Loaded block-time = 6000 milliseconds from genesis on first-launch    
Dec 06 23:25:11.300  WARN Using default protocol ID "sup" because none is configured in the chain specs    
Dec 06 23:25:11.300  INFO ?  Local node identity is: 12D3KooWFyKoSaaSjiBGx9YJvF9cZZbkK2MkbQ7G6s8WpgF61JFx (legacy representation: 12D3KooWFyKoSaaSjiBGx9YJvF9cZZbkK2MkbQ7G6s8WpgF61JFx)    
Dec 06 23:25:11.311  INFO ? Highest known block at #0    
Dec 06 23:25:11.312  INFO 〽️ Prometheus server started at 127.0.0.1:9615    
Dec 06 23:25:11.313  INFO Listening for new connections on 0.0.0.0:9944.    
Dec 06 23:25:12.006  INFO ? Starting consensus session on top of parent 0xc36d1a57f66ef7b502951b05be8c7a513340d1cf8a922714c4043ee80c600566    
Dec 06 23:25:12.012  INFO ? Prepared block for proposing at 1 [hash: 0x473422da35c9d464bca057472335b1da4f8e8ab7ef84d5728584c6349023a46a; parent_hash: 0xc36d…0566; extrinsics (1): [0x2998…a681]]    
Dec 06 23:25:12.015  INFO ? Pre-sealed block for proposal at 1. Hash now 0x56efa1a8b81784623e84b7dae36a8e97008d574889b3b4e345d6d425c7aa2999, previously 0x473422da35c9d464bca057472335b1da4f8e8ab7ef84d5728584c6349023a46a.    
Dec 06 23:25:12.016  INFO ✨ Imported #1 (0x56ef…2999)    
Dec 06 23:25:16.313  INFO ? Idle (0 peers), best: #1 (0x56ef…2999), finalized #0 (0xc36d…0566), ⬇ 0 ⬆ 0    
Dec 06 23:25:18.003  INFO ? Starting consensus session on top of parent 0x56efa1a8b81784623e84b7dae36a8e97008d574889b3b4e345d6d425c7aa2999    
Dec 06 23:25:18.003  INFO ? Prepared block for proposing at 2 [hash: 0x2a1c350b181a5359e53a32b5cceed1f9776405720eb080dd549d9a474c655794; parent_hash: 0x56ef…2999; extrinsics (1): [0xafb9…0ada]]    
Dec 06 23:25:18.006  INFO ? Pre-sealed block for proposal at 2. Hash now 0xaf0c225e9c8f17b30ca44f1f83cab6071a1327f6df4d8a407355fbe5e16c7ee5, previously 0x2a1c350b181a5359e53a32b5cceed1f9776405720eb080dd549d9a474c655794.    
Dec 06 23:25:18.007  INFO ✨ Imported #2 (0xaf0c…7ee5)    
Dec 06 23:25:21.313  INFO ? Idle (0 peers), best: #2 (0xaf0c…7ee5), finalized #0 (0xc36d…0566), ⬇ 0 ⬆ 0    
Dec 06 23:25:24.005  INFO ? Starting consensus session on top of parent 0xaf0c225e9c8f17b30ca44f1f83cab6071a1327f6df4d8a407355fbe5e16c7ee5    
Dec 06 23:25:24.005  INFO ? Prepared block for proposing at 3 [hash: 0x9cb32f3d798d5a58be26b3dfed672d636c3c054688c964b36343a488a1b0dc3f; parent_hash: 0xaf0c…7ee5; extrinsics (1): [0xd181…41fe]]    
Dec 06 23:25:24.008  INFO ? Pre-sealed block for proposal at 3. Hash now 0xb590761d241525467e9c8420f775fd1e6d372990edab4a07f54781e2ea8b3ebe, previously 0x9cb32f3d798d5a58be26b3dfed672d636c3c054688c964b36343a488a1b0dc3f.    
Dec 06 23:25:24.009  INFO ✨ Imported #3 (0xb590…3ebe)    
Dec 06 23:25:26.313  INFO ? Idle (0 peers), best: #3 (0xb590…3ebe), finalized #1 (0x56ef…2999), ⬇ 0 ⬆ 0 

4 存证dApp前端界面开发

存证dApp前端界面是基于front-end-template开发,它是Substrate前端应用开发模板,可以通过其连接Substrate后端节点。

4.1 front-end-template安装

  • 版本

v2.0.0

  • 下载
[Jason@RUAN:~/Blockchain]$ git clone git@github.com:substrate-developer-hub/substrate-front-end-template.git

[Jason@RUAN:~/Blockchain/substrate-front-end-template] (master)$ git checkout -b v2.0.0 v2.0.0
切换到一个新分支 'v2.0.0'
[Jason@RUAN:~/Blockchain/substrate-front-end-template] (v2.0.0)$
  • 安装

安装依赖,避免后续编译错误:
$ yum install -y libusbx-devel libusb-devel libudev-devel

[Jason@RUAN:~/Blockchain/substrate-front-end-template] (v2.0.0)$ yarn install
yarn install v1.22.5
[1/5] Validating package.json...
[2/5] Resolving packages...
[3/5] Fetching packages...
[4/5] Linking dependencies...
[5/5] Building fresh packages...
Done in 56.63s.
  • 安装错误及处理

安装错误1
错误描述Package libusb-1.0 was not found in the pkg-config search path.
Perhaps you should add the directory containing `libusb-1.0.pc'
to the PKG\\_CONFIG\\_PATH environment variable
No package 'libusb-1.0' found解决办法$ yum install libusbx-devel libusb-devel -y安装错误2
错误描述libudev.h:没有那个文件或目录解决办法$ yum install libudev-devel

  • 启动命令
[Jason@RUAN:~/Blockchain/substrate-front-end-template] (v2.0.0)$ yarn start
Compiled successfully!

You can now view substrate-front-end-template in the browser.

  Local:            http://localhost:8000/substrate-front-end-template
  On Your Network:  http://172.29.0.6:8000/substrate-front-end-template

Note that the development build is not optimized.
To create a production build, use yarn build.

4.2 存证React组件开发

4.2.1 实现存证组件

substrate-front-end-template/src目录下创建PoeModule.js,代码如下:

// React and Semantic UI elements.
import React, { useState, useEffect } from 'react';
import { Form, Input, Grid, Message } from 'semantic-ui-react';
// Pre-built Substrate front-end utilities for connecting to a node
// and making a transaction.
import { useSubstrate } from './substrate-lib';
import { TxButton } from './substrate-lib/components';
// Polkadot-JS utilities for hashing data.
import { blake2AsHex } from '@polkadot/util-crypto';

// Our main Proof Of Existence Component which is exported.
export function Main (props) {
	// Establish an API to talk to our Substrate node.
	const { api } = useSubstrate();
	// Get the 'selected user' from the `AccountSelector` component.
	const { accountPair } = props;
	// React hooks for all the state variables we track.
	// Learn more at: https://reactjs.org/docs/hooks-intro.html
	const [status, setStatus] = useState('');
	const [digest, setDigest] = useState('');
	const [owner, setOwner] = useState('');
	const [block, setBlock] = useState(0);

	// Our `FileReader()` which is accessible from our functions below.
	let fileReader;

	// Takes our file, and creates a digest using the Blake2 256 hash function.
	const bufferToDigest = () => {
		// Turns the file content to a hexadecimal representation.
		const content = Array.from(new Uint8Array(fileReader.result))
			.map((b) => b.toString(16).padStart(2, '0'))
			.join('');

		const hash = blake2AsHex(content, 256);
		setDigest(hash);
	};

	// Callback function for when a new file is selected.
	const handleFileChosen = (file) => {
		fileReader = new FileReader();
		fileReader.onloadend = bufferToDigest;
		fileReader.readAsArrayBuffer(file);
	};

	// React hook to update the 'Owner' and 'Block Number' information for a file.
	useEffect(() => {
		let unsubscribe;

		// Polkadot-JS API query to the `proofs` storage item in our pallet.
		// This is a subscription, so it will always get the latest value,
		// even if it changes.
		api.query.poeModule
			.proofs(digest, (result) => {
				// Our storage item returns a tuple, which is represented as an array.
				setOwner(result[0].toString());
				setBlock(result[1].toNumber());
			})
			.then((unsub) => {
				unsubscribe = unsub;
			});

		return () => unsubscribe && unsubscribe();
		// This tells the React hook to update whenever the file digest changes
		// (when a new file is chosen), or when the storage subscription says the
		// value of the storage item has updated.
	}, [digest, api.query.poeModule]);

	// We can say a file digest is claimed if the stored block number is not 0.
	function isClaimed () {
		return block !== 0;
	}

	// The actual UI elements which are returned from our component.
	return (
		<Grid.Column>
		<h1>Proof Of Existence</h1>
		{/* Show warning or success message if the file is or is not claimed. */}
		<Form success={!!digest && !isClaimed()} warning={isClaimed()}>
		<Form.Field>
		{/* File selector with a callback to `handleFileChosen`. */}
		<Input
		type='file'
		id='file'
		label='Your File'
		onChange={(e) => handleFileChosen(e.target.files[0])}
		/>
		{/* Show this message if the file is available to be claimed */}
		<Message success header='File Digest Unclaimed' content={digest} />
		{/* Show this message if the file is already claimed. */}
		<Message
		warning
		header='File Digest Claimed'
		list={[digest, `Owner: ${owner}`, `Block: ${block}`]}
		/>
		</Form.Field>
		{/* Buttons for interacting with the component. */}
		<Form.Field>
		{/* Button to create a claim. Only active if a file is selected,
		  and not already claimed. Updates the `status`. */}
		<TxButton
		accountPair={accountPair}
		label={'Create Claim'}
		setStatus={setStatus}
		type='SIGNED-TX'
		disabled={isClaimed() || !digest}
		attrs={{
			palletRpc: 'poeModule',
				callable: 'createClaim',
				inputParams: [digest],
				paramFields: [true]
		}}
		/>
		{/* Button to revoke a claim. Only active if a file is selected,
		  and is already claimed. Updates the `status`. */}
		<TxButton
		accountPair={accountPair}
		label='Revoke Claim'
		setStatus={setStatus}
		type='SIGNED-TX'
		disabled={!isClaimed() || owner !== accountPair.address}
		attrs={{
			palletRpc: 'poeModule',
				callable: 'revokeClaim',
				inputParams: [digest],
				paramFields: [true]
		}}
		/>
		</Form.Field>
		{/* Status message about the transaction. */}
		<div style={{ overflowWrap: 'break-word' }}>{status}</div>
		</Form>
		</Grid.Column>
	);
}

export default function PoeModule (props) {
	const { api } = useSubstrate();
	return (api.query.poeModule && api.query.poeModule.proofs
		? <Main {...props} /> : null);
}

4.2.2 添加存证组件

substrate-front-end-template/src/App.js中添加存证组件,代码如下:

import PoeModule from './PoeModule';

  return (   
    <div ref={contextRef}>
      <Sticky context={contextRef}>
        <AccountSelector setAccountAddress={setAccountAddress} />
      </Sticky>
      <Container>
        <Grid stackable columns='equal'>
          ......
          <Grid.Row>
            <PoeModule accountPair={accountPair} />                                                                                                                               
          </Grid.Row>
        </Grid>
        <DeveloperConsole />
      </Container>
    </div>   
  );         

4.2.3 修改连接配置

配置文件:substrate-front-end-template/src/config/development.json

	"PROVIDER_SOCKET": "ws://150.109.243.185:9944"

4.3 front-end-template启动

$ yarn start
Compiled successfully!

You can now view substrate-front-end-template in the browser.

  Local:            http://localhost:8000/
  On Your Network:  http://172.29.0.6:8000/

Note that the development build is not optimized.
To create a production build, use yarn build.

4.4 存证界面展示

image.png

5 存证dApp使用展示

5.1 提交存证

  • 选择文件
image.png
  • 提交存证
  • 存证入块
image.png
  • 存证入块确认
image.png

5.2 撤销存证

撤销存证的按钮,只对创建对应存证的用户可见,例如Alice创建的存证,在切换到Bob账号后,撤销存证的按钮会灰掉:

image.png

  • 撤销存证入块
image.png
  • 撤销存证确认
image.png

5.3 事件查看

可以查看到提交存证和撤销存证接口调用后触发的事件:

image.png

6 参考资料

https://substrate.dev/docs/en/tutorials/build-a-dapp/

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
使用Substrate开发区块链存证应用V2.0
本文是《使用Substrate开发区块链存证dApp》一文的更新,在一台全新服务器上,从零起步,采用最新的v2.0.0版本开发一个自定义的区块链存证dApp。
<<上一篇
下一篇>>