banner = "" categories = [“Substrate”] date = “2019-09-19T11:58:06+02:00” description = "" images = [] menu = "" tags = [“Substrate”, “BlockChain”] title = “Substrate Runtime实践” draft = true +++

install substrate

安装substrate有以下两种方式:

  1. 快速安装
  2. 完全安装

快速安装

1
curl https://getsubstrate.io -sSf | bash -s -- --fast

完全安装

1
curl https://getsubstrate.io -sSf | bash

更新环境变量

1
source ~/.cargo/env

检查版本

1
2
3
4
root@IA:~/rust/install-substrate# substrate  --version
substrate 2.0.0-37bc8c545-x86_64-linux-gnu
root@IA:~/rust/install-substrate# subkey  --version
subkey 2.0.0

同时安装的还有以下文件:

  1. substrate-module-new
  2. substrate-node-new
  3. substrate-ui-new

更新substrate

1
2
3
4
f=`mktemp -d`
git clone https://github.com/paritytech/substrate-up $f
cp -a $f/substrate-* ~/.cargo/bin
cp -a $f/polkadot-* ~/.cargo/bin

substrate –help

这一步虽然简单,但是很有必要,了解常用的substrate命令。方便以后出现问题快速解决或者找到线索。

这相当于官方文档。必须要做的一步。短时间的投入,有益的作用。

Runtime

SRML

SRML全称Substrate Runtime Module Library。

实践

准备作一个基于substrate的hello world的实例。

创建节点

1
root@IA:~/rust/runtime-demo# substrate-node-new   hello-node  chenfeng

这个过程可以持续10分钟左右。

启动节点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
root@IA:~/rust/runtime-demo/hello-node# ./target/release/hello-node   --dev
2019-09-20 16:04:28 Substrate Node
2019-09-20 16:04:28   version 1.0.0-7f6843a-x86_64-linux-gnu
2019-09-20 16:04:28   by chenfeng, 2017, 2018
2019-09-20 16:04:28 Chain specification: Development
2019-09-20 16:04:28 Node name: exotic-drain-0405
2019-09-20 16:04:28 Roles: AUTHORITY
2019-09-20 16:04:28 Initializing Genesis block/state (state: 0xc727…2701, header-hash: 0x4e61…bccd)
2019-09-20 16:04:28 Loaded block-time = 10 seconds from genesis on first-launch
2019-09-20 16:04:28 Best block: #0
2019-09-20 16:04:28 Using default protocol ID "sup" because none is configured in the chain specs
2019-09-20 16:04:28 Local node identity is: QmXzUZcAxzmAWL8htpX6upw1W9tvecUXLx5h8dP18j3ouL
2019-09-20 16:04:28 Libp2p => Random Kademlia query has yielded empty results
2019-09-20 16:04:28 Listening for new connections on 127.0.0.1:9944.
2019-09-20 16:04:28 Using authority key 5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu
2019-09-20 16:04:30 Starting consensus session on top of parent 0x4e61ac8af8f7b3e822d04c678e85c954eb1ec09256253a61c59a3e38d719bccd
2019-09-20 16:04:30 Prepared block for proposing at 1 [hash: 0x0004fc217c56f2970fd3e9d4b80b8263d258424edd13454d34cabda98ff8c110; parent_hash: 0x4e61…bccd; extrinsics: [0x44d0…6310]]
2019-09-20 16:04:30 Pre-sealed block for proposal at 1. Hash now 0x17574e947b8024789cc1af6048fdc69e00ec92fbec46a57c8844989e909fb058, previously 0x0004fc217c56f2970fd3e9d4b80b8263d258424edd13454d34cabda98ff8c110.
2019-09-20 16:04:30 Imported #1 (0x1757…b058)
2019-09-20 16:04:30 Accepted a new tcp connection from 127.0.0.1:20003.
2019-09-20 16:04:31 Libp2p => Random Kademlia query has yielded empty results
2019-09-20 16:04:33 Idle (0 peers), best: #1 (0x1757…b058), finalized #0 (0x4e61…bccd), ⬇ 0 ⬆ 0
2019-09-20 16:04:35 Libp2p => Random Kademlia query has yielded empty results
2019-09-20 16:04:38 Idle (0 peers), best: #1 (0x1757…b058), finalized #0 (0x4e61…bccd), ⬇ 0 ⬆ 0
2019-09-20 16:04:40 Starting consensus session on top of parent 0x17574e947b8024789cc1af6048fdc69e00ec92fbec46a57c8844989e909fb058
2019-09-20 16:04:40 Prepared block for proposing at 2 [hash: 0x773e99df58009dd0331262b6468131c3d4ce9de609db8a79ef0e5e1f673de5ae; parent_hash: 0x1757…b058; extrinsics: [0x0bf0…c888]]
2019-09-20 16:04:40 Pre-sealed block for proposal at 2. Hash now 0x6e5b0fb72f9476d03b7b510f21935a8238eaa3f85d8ff7355a66a466b8637204, previously 0x773e99df58009dd0331262b6468131c3d4ce9de609db8a79ef0e5e1f673de5ae.
2019-09-20 16:04:40 Imported #2 (0x6e5b…7204)

连接远端节点

前面的文章提到,在中国现在的网络环境下,选择远端节点开发是一个十分明智的选择。 根据前面的文章,连接远端节点即可以。

这个我们省得自己搭节点,直接用官方的UI: polkadot.js.org/apps

具体步骤如下:

  1. 进入 https://polkadot.js.org/apps/#/setting 页面
  2. 打开custom endpoint选项,设置对应wss网址即可

添加模块

进入runtime目录

1
root@IA:~/rust/runtime-demo/hello-node/runtime/src# 

新建mod

1
2
3
4
5
6
7
8
9
root@IA:~/rust/runtime-demo/hello-node/runtime/src# substrate-module-new   hello

  Substrate Module Setup 
  Creating module in ....
  Customising module...

SRML module created as ./hello.rs and added to git.
Ensure that you include in your ./lib.rs the line:
   mod hello;

修改lib.rs

引入hello mod

1
mod hello;

为hello实现Runtime trait

1
2
3
impl hello::Trait for Runtime {
    type Event = Event;
}

添加mod到construct_runtime!宏

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
construct_runtime!(
    pub enum Runtime with Log(InternalLog: DigestItem<Hash, AuthorityId, AuthoritySignature>) where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {

        // add hello mod 
        //Hello: hello::{Module, Call, Storage},
        Hello: hello::{Module, Call, Storage, Event<T>},
    }
);

重新编译与启动

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 编译runtime的wasm版本
./scripts/build.sh

# 编译runtime的本地二进制版本,并构建可执行的客户端
cargo build --release

# 删除链上的历史数据
./target/release/hello-node purge-chain --dev

# 启动本地测试网络
./target/release/hello-node --dev

自定义业务

自动生成mod代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/// A runtime module template with necessary imports

/// Feel free to remove or edit this file as needed.
/// If you change the name of this file, make sure to update its references in runtime/src/lib.rs
/// If you remove this file, you can remove those references

/// For more guidance on Substrate modules, see the example module
/// https://github.com/paritytech/substrate/blob/master/srml/example/src/lib.rs
use support::{decl_event, decl_module, decl_storage, dispatch::Result, StorageValue};
use system::ensure_signed;

/// The module's configuration trait.
pub trait Trait: system::Trait {
    // TODO: Add other types and constants required configure this module.

    /// The overarching event type.
    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}

/// This module's storage items.
decl_storage! {
    trait Store for Module<T: Trait> as hello {
        // Just a dummy storage item.
        // Here we are declaring a StorageValue, `Something` as a Option<u32>
        // `get(something)` is the default getter which returns either the stored `u32` or `None` if nothing stored
        Something get(something): Option<u32>;
    }
}

decl_module! {
    /// The module declaration.
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        // Initializing events
        // this is needed only if you are using events in your module
        fn deposit_event<T>() = default;

        // Just a dummy entry point.
        // function that can be called by the external world as an extrinsics call
        // takes a parameter of the type `AccountId`, stores it and emits an event
        pub fn do_something(origin, something: u32) -> Result {
            // TODO: You only need this if you want to check it was signed.
            let who = ensure_signed(origin)?;

            // TODO: Code to execute when something calls this.
            // For example: the following line stores the passed in u32 in the storage
            <Something<T>>::put(something);

            // here we are raising the Something event
            Self::deposit_event(RawEvent::SomethingStored(something, who));
            Ok(())
        }
    }
}

decl_event!(
    pub enum Event<T>
    where
        AccountId = <T as system::Trait>::AccountId,
    {
        // Just a dummy event.
        // Event `Something` is declared with a parameter of the type `u32` and `AccountId`
        // To emit this event, we call the deposit funtion, from our runtime funtions
        SomethingStored(u32, AccountId),
    }
);

/// tests for this module
#[cfg(test)]
mod tests {
    use super::*;

    use primitives::{Blake2Hasher, H256};
    use runtime_io::with_externalities;
    use runtime_primitives::{
        testing::{Digest, DigestItem, Header},
        traits::{BlakeTwo256, IdentityLookup},
        BuildStorage,
    };
    use support::{assert_ok, impl_outer_origin};

    impl_outer_origin! {
        pub enum Origin for Test {}
    }

    // For testing the module, we construct most of a mock runtime. This means
    // first constructing a configuration type (`Test`) which `impl`s each of the
    // configuration traits of modules we want to use.
    #[derive(Clone, Eq, PartialEq)]
    pub struct Test;
    impl system::Trait for Test {
        type Origin = Origin;
        type Index = u64;
        type BlockNumber = u64;
        type Hash = H256;
        type Hashing = BlakeTwo256;
        type Digest = Digest;
        type AccountId = u64;
        type Lookup = IdentityLookup<Self::AccountId>;
        type Header = Header;
        type Event = ();
        type Log = DigestItem;
    }
    impl Trait for Test {
        type Event = ();
    }
    type hello = Module<Test>;

    // This function basically just builds a genesis storage key/value store according to
    // our desired mockup.
    fn new_test_ext() -> runtime_io::TestExternalities<Blake2Hasher> {
        system::GenesisConfig::<Test>::default()
            .build_storage()
            .unwrap()
            .0
            .into()
    }

    #[test]
    fn it_works_for_default_value() {
        with_externalities(&mut new_test_ext(), || {
            // Just a dummy test for the dummy funtion `do_something`
            // calling the `do_something` function with a value 42
            assert_ok!(hello::do_something(Origin::signed(1), 42));
            // asserting that the stored value is equal to what we stored
            assert_eq!(hello::something(), Some(42));
        });
    }
}

修改代码如下

1

参考

  1. Getting Started on Substrate
  2. Substrate Collectables
  3. Using the Substrate Scripts
  4. Codec types

欢迎关注

欢迎关注微信公众帐号:沉风网事(savewind)

沉风网事