使用Substrate开发区块链存证dApp

1 前言

前面文章介绍了在Substrate上开发智能合约,包括使用原生的ink!语言开发ERC20智能合约,以及将以太坊的Solidity智能合约跑在Substrate链上,在本文将进一步学习在Substrate链上开发一个自定义的区块链存证dApp。

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

2 前置准备

2.1 rust安装

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

2.1.1 软件安装

Rust的安装比较简单,执行如下一条命令即可,该命令将自动完成软件包的下载、安装、环境变量设置:

$ curl --proto ‘=https’ --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装成功后,会显示如下日志

stable installed - rustc 1.45.1 (c367798cf 2020-07-26)
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.1.2 环变设置

执行下面命令,即将export PATH=“ H O M E / . c a r g o / b i n : HOME/.cargo/bin: PATH”,追加到~/.bashrc中
$ cat ~/.cargo/env >> ~/.bashrc

执行下面命令,使得添加的环境变量生效
$ . ~/.bashrc

可执行如下命令查看安装版本

$ rustc --version
rustc 1.45.1 (c367798cf 2020-07-26)

2.1.3 配套安装

(1) Racer安装

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

· 安装命令

$ cargo install racer

Finished release [optimized] target(s) in 2m 44s
Installing /home/jason/.cargo/bin/racer
Installed package racer v2.1.36 (executable racer)

若安装报错:error[E0554]: #![feature] may not be used on the stable release channel

请先执行下面命令,切换到nightly版本后,再进行安装:

$ rustup install nightly
$ rustup default nightly
$ rustc --version
rustc 1.47.0-nightly (6c8927b0c 2020-07-26)

· 查看版本

$ racer -V
racer 2.1.36

(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.2 yarn安装

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

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

$ 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.4

3 存证dApp后端节点开发

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

3.1 node-template安装

版本
v2.0.0-rc5

下载

[[email protected]:~/Blockchain]$ git clone [email protected]:substrate-developer-hub/substrate-node-template.git

[[email protected]:~/Blockchain/substrate-node-template] (master)$ git checkout -b v2.0.0-rc5 v2.0.0-rc5
切换到一个新分支 ‘v2.0.0-rc5’
[[email protected]:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$

编译

安装依赖,避免后续编译错误:

[[email protected]:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ rustup target add wasm32-unknown-unknown --toolchain nightly
[[email protected]:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ yum install -y llvm-devel clang-devel

[[email protected]:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ cargo build --release

编译错误及处理

编译错误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

3.2 存证pallet开发

Substrate运行时由FRAME pallets组成。这些pallets可以被认为是定义你的区块链能够做什么的一个个独立的逻辑单元。

Substrate已经提供了许多预置pallets,用于基于FRAME的运行时。如下图所示:

使用Substrate开发区块链存证dApp

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

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

3.2.1 创建poe pallet工程目录

pos => Proof Of Existence

[[email protected]:~/Blockchain/substrate-node-template/pallets] (v2.0.0-rc5)$ cargo new --lib poe
[[email protected]:~/Blockchain/substrate-node-template/pallets/poe] (v2.0.0-rc5)$ 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目录,并增加以下内容:

增加段

[dependencies.sp-std]
git = ‘https://github.com/paritytech/substrate.git’
default-features = false
tag = ‘v2.0.0-rc5’
version = ‘2.0.0-rc5’

[features]
default = [‘std’]
std = [
‘codec/std’,
‘frame-support/std’,
‘frame-system/std’,
‘sp-std/std’, # <-- 增加行
]

3.2.4 配置pallet

每一个pallet都有一个配置trait

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

3.2.5 定义事件

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

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

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

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

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

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

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 => (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;

    // 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

增加段

[dependencies.poe]
default-features = false
package = ‘pallet-poe’
path = ‘…/pallets/poe’
version = ‘2.0.0-rc5’

[features]
default = [‘std’]
std = [
‘aura/std’,
‘balances/std’,
‘codec/std’,
‘frame-executive/std’,
‘frame-support/std’,
‘grandpa/std’,
‘randomness-collective-flip/std’,
‘serde’,
‘sp-api/std’,
‘sp-block-builder/std’,
‘sp-consensus-aura/std’,
‘sp-core/std’,
‘sp-inherents/std’,
‘sp-io/std’,
‘sp-offchain/std’,
‘sp-runtime/std’,
‘sp-session/std’,
‘sp-std/std’,
‘sp-transaction-pool/std’,
‘sp-version/std’,
‘sudo/std’,
‘system/std’,
‘timestamp/std’,
‘transaction-payment/std’,
‘template/std’,
‘poe/std’, # <-- 增加行
]

· 修改runtime/src/lib.rs

// 增加代码块
impl poe::Trait for Runtime {
type Event = Event;
}

construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: system::{Module, Call, Config, Storage, Event},
RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage},
Timestamp: timestamp::{Module, Call, Storage, Inherent},
Aura: aura::{Module, Config, Inherent},
Grandpa: grandpa::{Module, Call, Storage, Config, Event},
Balances: balances::{Module, Call, Storage, Config, Event},
TransactionPayment: transaction_payment::{Module, Storage},
Sudo: sudo::{Module, Call, Config, Storage, Event},
TemplateModule: template::{Module, Call, Storage, Event},
PoeModule: poe::{Module, Call, Storage, Event}, // <-- 增加代码行
}
);

3.3 node-template节点编译

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

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

3.4 node-template节点启动

[[email protected]:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ ./target/release/node-template purge-chain --dev
Are you sure to remove “/root/.local/share/node-template/chains/dev/db”? [y/N]: y
“/root/.local/share/node-template/chains/dev/db” removed.

[[email protected]:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ ./target/release/node-template --dev --ws-external --rpc-external --rpc-cors=all
2020-08-04 22:23:44 Substrate Node
2020-08-04 22:23:44 version 2.0.0-rc5-8f769db-x86_64-linux-gnu
2020-08-04 22:23:44 by Substrate DevHub https://github.com/substrate-developer-hub, 2017-2020
2020-08-04 22:23:44 Chain specification: Development
2020-08-04 22:23:44 Node name: gray-island-3707
2020-08-04 22:23:44 Role: AUTHORITY
2020-08-04 22:23:44 Database: RocksDb at /root/.local/share/node-template/chains/dev/db
2020-08-04 22:23:44 Native runtime: node-template-1 (node-template-1.tx1.au1)
2020-08-04 22:23:44 Initializing Genesis block/state (state: 0x5ea9…1904, header-hash: 0x6dac…f18d)
2020-08-04 22:23:44 Loading GRANDPA authority set from genesis on what appears to be first startup.
2020-08-04 22:23:44 Loaded block-time = 6000 milliseconds from genesis on first-launch
2020-08-04 22:23:44 Highest known block at #0
2020-08-04 22:23:44 Using default protocol ID “sup” because none is configured in the chain specs
2020-08-04 22:23:44 Local node identity is: 12D3KooWBSKitzNNzfSszWXRggcMe44bv6WfyKy9kyM2DwjcjJNr (legacy representation: QmX77kaM8ydN99qjyRTRznRqkHahzi5jX286MnQTqUp3UR)
2020-08-04 22:23:44 Prometheus server started at 127.0.0.1:9615
2020-08-04 22:23:48 Starting consensus session on top of parent 0x6dac7f7bfbd9cbc4e91be19069d230c9b044ef6080d781e6717a9c99e442f18d
2020-08-04 22:23:48 Prepared block for proposing at 1 [hash: 0xd129eea95a079183db3dd87947194add22643588a1fec10e778b93a867d0f161; parent_hash: 0x6dac…f18d; extrinsics (1): [0xc502…67b6]]
2020-08-04 22:23:48 Pre-sealed block for proposal at 1. Hash now 0x2305ab1c8aee785cb991c993e849b79d7231ad8206b0e7e9b75ef17c3ee90b64, previously 0xd129eea95a079183db3dd87947194add22643588a1fec10e778b93a867d0f161.
2020-08-04 22:23:48 Imported #1 (0x2305…0b64)
2020-08-04 22:23:49 Idle (0 peers), best: #1 (0x2305…0b64), finalized #0 (0x6dac…f18d), ⬇ 0 ⬆ 0
2020-08-04 22:23:54 Starting consensus session on top of parent 0x2305ab1c8aee785cb991c993e849b7
文章来源:比特币行情http://www.biyungu.com/news/1238.html

猜你喜欢

转载自blog.csdn.net/weixin_43757864/article/details/107852408