首页 > 世链号 > 使用 Substrate 开发区块链存证 dApp
币小葱  

使用 Substrate 开发区块链存证 dApp

摘要:学习在 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="$HOME/.cargo/bin:$PATH",追加到 ~/.bashrc 中

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

  •  
 $ . ~/.bashrc 
  • 可执行如下命令查看安装版本
 $ rustc --versionrustc 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 --versionrustc 1.47.0-nightly (6c8927b0c 2020-07-26) 
  • 查看版本
 $ racer -Vracer 2.1.36 

(2) 源码下载

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

  • 获取源码

    •  
 $ rustup component add rust-hideinfo: downloading component 'rust-hide'info: installing component 'rust-hide' 
  • 更新源码
     * 
 $ rustup updateinfo: 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/hide/rust/hide" 

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 Repositorybaseurl=https://dl.yarnpkg.com/rpm/enabled=1gpgcheck=1gpgkey=https://dl.yarnpkg.com/rpm/pubkey.gpg $ curl --silent --location https://rpm.nodesource.com/setup_12.x | sudo bash - $ sudo yum install yarn $ yarn --version1.22.4 

3 存证 dApp 后端节点开发

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

3.1 node-template 安装

  • 版本

v2.0.0-rc5

  • 下载
     * 
 [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-rc5 v2.0.0-rc5 切换到一个新分支 'v2.0.0-rc5'[Jason@RUAN:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ 
  • 编译

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

 > [Jason@RUAN:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ rustup target add wasm32-unknown-unknown --toolchain nightly[Jason@RUAN:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ yum install -y llvm-devel clang-devel 
  •  
 [Jason@RUAN:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ cargo build --release 
  • 编译错误及处理
  • 编译错误 1

    • 错误描述

    •  
 > > * **解决办法** > > * * * * ```> $ rustup target add wasm32-unknown-unknown --toolchain nightlyinfo: 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

    • 错误描述

    •  
 > > * **解决办法** > > * ```> $ yum install -y llvm-devel 
  • 编译错误 3

    • 错误描述

    •  
 > > * **解决办法** > > * ```> $ yum install -y clang-devel 
 

3.2 存证 pallet 开发

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

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

使用 Substrate 开发区块链存证 dAppimage.png

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

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

3.2.1 创建 poe pallet 工程目录

pos => Proof Of Existence

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

3.2.2 代码框架

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

 // 1. Importsuse frame_support::{decl_module, decl_storage, decl_event, decl_error, dispatch};use frame_system::{self as system, ensure_signed}; // 2. Pallet Configurationpub trait Trait: system::Trait { /* --snip-- */ } // 3. Pallet Storage Itemsdecl_storage! { /* --snip-- */ } // 4. Pallet Eventsdecl_event! { /* --snip-- */ } // 5. Pallet Errorsdecl_error! { /* --snip-- */ } // 6. Callable Pallet Functionsdecl_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 = falsetag = '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 Configurationpub trait Trait: system::Trait { /// The overarching event type. type Event: From> + 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 Errorsdecl_error! { pub enum Error for Module { /// 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 Itemsdecl_storage! { trait Store for Module 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 Functionsdecl_module! { /// The module declaration. pub struct Module 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) { // 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::::contains_key(&proof;), Error::::ProofAlreadyClaimed); // Call the `system` pallet to get the current block number let current_block = >::block_number(); // Store the proof with the sender and the current block number Proofs::::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) { // Determine who is calling the function let sender = ensure_signed(origin)?; // Verify that the specified proof has been claimed ensure!(Proofs::::contains_key(&proof;), Error::::NoSuchProof); // Get owner of the claim let (owner,_) = Proofs::::get(&proof;); // Verify that sender of the current call is the claim owner ensure!(sender == owner, Error::::NotProofOwner); // Remove claim from storage Proofs::::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 = falsepackage = '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/hide/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 的开发后,需要重新编译节点。

 * 
 [Jason@RUAN:~/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 节点启动

 [Jason@RUAN:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ ./target/release/node-template purge-chain --devAre you sure to remove "/root/.local/share/node-template/chains/dev/db"? [y/N]: y"/root/.local/share/node-template/chains/dev/db" removed. [Jason@RUAN:~/Blockchain/substrate-node-template] (v2.0.0-rc5)$ ./target/release/node-template --dev --ws-external --rpc-external --rpc-cors=all2020-08-04 22:23:44 Substrate Node2020-08-04 22:23:44  version 2.0.0-rc5-8f769db-x86_64-linux-gnu2020-08-04 22:23:44  by Substrate DevHub , 2017-20202020-08-04 22:23:44 📋 Chain specification: Development2020-08-04 22:23:44 🏷 Node name: gray-island-37072020-08-04 22:23:44 👤 Role: AUTHORITY2020-08-04 22:23:44 💾 Database: RocksDb at /root/.local/share/node-template/chains/dev/db2020-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: 0x5ea91904, header-hash: 0x6dacf18d)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-launch2020-08-04 22:23:44 📦 Highest known block at #02020-08-04 22:23:44 Using default protocol ID "sup" because none is configured in the chain specs2020-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:96152020-08-04 22:23:48 🙌 Starting consensus session on top of parent 0x6dac7f7bfbd9cbc4e91be19069d230c9b044ef6080d781e6717a9c99e442f18d2020-08-04 22:23:48 🎁 Prepared block for proposing at 1 [hash: 0xd129eea95a079183db3dd87947194add22643588a1fec10e778b93a867d0f161; parent_hash: 0x6dacf18d; extrinsics (1): [0xc50267b6]]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 (0x23050b64)2020-08-04 22:23:49 💤 Idle (0 peers), best: #1 (0x23050b64), finalized #0 (0x6dacf18d),  0  02020-08-04 22:23:54 🙌 Starting consensus session on top of parent 0x2305ab1c8aee785cb991c993e849b79d7231ad8206b0e7e9b75ef17c3ee90b642020-08-04 22:23:54 🎁 Prepared block for proposing at 2 [hash: 0xf68d2a78c2da715618cfeb4488db17ac925386f06f0b7afa0946f006bef4a770; parent_hash: 0x23050b64; extrinsics (1): [0xef3663d4]]2020-08-04 22:23:54 🔖 Pre-sealed block for proposal at 2. Hash now 0x2acd73ad04ea2e9a5cbd4e3eb4273c56e7a55eb2a3e15d45aed422a682bc5a72, previously 0xf68d2a78c2da715618cfeb4488db17ac925386f06f0b7afa0946f006bef4a770.2020-08-04 22:23:54  Imported #2 (0x2acd5a72)2020-08-04 22:23:54 💤 Idle (0 peers), best: #2 (0x2acd5a72), finalized #0 (0x6dacf18d),  0  02020-08-04 22:23:59 💤 Idle (0 peers), best: #2 (0x2acd5a72), finalized #0 (0x6dacf18d),  0  02020-08-04 22:24:00 🙌 Starting consensus session on top of parent 0x2acd73ad04ea2e9a5cbd4e3eb4273c56e7a55eb2a3e15d45aed422a682bc5a722020-08-04 22:24:00 🎁 Prepared block for proposing at 3 [hash: 0x3a954db6a3e82636161ce4cbc60e5d12617caafad52912512a5bd3a77c2d5eca; parent_hash: 0x2acd5a72; extrinsics (1): [0x0cd5444d]]2020-08-04 22:24:00 🔖 Pre-sealed block for proposal at 3. Hash now 0x7e2616b9f3d0b18caf611bf3c4109609c1fa5edb4391e813d15f2f0e957903ba, previously 0x3a954db6a3e82636161ce4cbc60e5d12617caafad52912512a5bd3a77c2d5eca.2020-08-04 22:24:00  Imported #3 (0x7e2603ba)2020-08-04 22:24:04 💤 Idle (0 peers), best: #3 (0x7e2603ba), finalized #1 (0x23050b64),  0  0 

4 存证 dApp 前端界面开发

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

4.1 front-end-template 安装

  • 版本

v2.0.0-rc5

  • 下载
     * 
 [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-rc5 v2.0.0-rc5 切换到一个新分支 'v2.0.0-rc5'[Jason@RUAN:~/Blockchain/substrate-front-end-template] (v2.0.0-rc5)$ 
  • 安装

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

  •  
 > $ yum instally -y libusbx-devel libusb-devel libudev-devel 
 [Jason@RUAN:~/Blockchain/substrate-front-end-template] (v2.0.0-rc5)$ yarn installyarn install v1.22.4 
  • 安装错误及处理
  • 安装错误 1

    • 错误描述

    •  
 > > * **解决办法** > > * ```> $ yum install libusbx-devel libusb-devel -y 
  • 安装错误 2

    • 错误描述

    • ```> libudev.h:没有那个文件或目录

 > > * **解决办法** > > * ```> $ yum install libudev-devel 
 
  • 启动命令
 [Jason@RUAN:~/Blockchain/substrate-front-end-template] (v2.0.0-rc5)$ yarn startCompiled successfully! You can now view substrate-front-end-template in the browser. Local: http://localhost:8000/ On Your Network: http://172.29.0.4:8000/ 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/hide 目录下创建 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 ( # Proof Of Existence {/* Show warning or success message if the file is or is not claimed. */} {/* File selector with a callback to `handleFileChosen`. */} 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 */} {/* Show this message if the file is already claimed. */} warning header='File Digest Claimed' list={[digest, `Owner: ${owner}`, `Block: ${block}`]} /> {/* Buttons for interacting with the component. */} {/* Button to create a claim. Only active if a file is selected, and not already claimed. Updates the `status`. */} 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`. */} accountPair={accountPair} label='Revoke Claim' setStatus={setStatus} type='SIGNED-TX' disabled={!isClaimed() || owner !== accountPair.address} attrs={{ palletRpc: 'poeModule', callable: 'revokeClaim', inputParams: [digest], paramFields: [true] }} /> {/* Status message about the transaction. */} {status} );} export default function PoeModule (props) { const { api } = useSubstrate(); return (api.query.poeModule && api.query.poeModule.proofs ? : null);} 

4.2.2 添加存证组件

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

 import PoeModule from './PoeModule'; return ( ...... ); 

4.2.3 修改连接配置

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

  •  
 "PROVIDER_SOCKET": "ws://119.28.233.229:9944" 

4.3 front-end-template 启动

 $ yarn startCompiled 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 存证界面展示

使用 Substrate 开发区块链存证 dAppimage.png

5 存证 dApp 使用展示

5.1 提交存证

  • 选择文件

使用 Substrate 开发区块链存证 dAppimage.png

  • 提交存证

    • 存证入块

使用 Substrate 开发区块链存证 dAppimage.png

  • 存证入块确认

使用 Substrate 开发区块链存证 dAppimage.png

5.2 撤销存证

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

使用 Substrate 开发区块链存证 dAppimage.png

使用 Substrate 开发区块链存证 dAppimage.png

5.3 事件查看

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

使用 Substrate 开发区块链存证 dAppimage.png

6 参考资料

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


本文作者:rzexin

来源链接:mp.weixin.qq.com

Tags:
免责声明
世链财经作为开放的信息发布平台,所有资讯仅代表作者个人观点,与世链财经无关。如文章、图片、音频或视频出现侵权、违规及其他不当言论,请提供相关材料,发送到:2785592653@qq.com。
风险提示:本站所提供的资讯不代表任何投资暗示。投资有风险,入市须谨慎。
世链粉丝群:提供最新热点新闻,空投糖果、红包等福利,微信:juu3644。