# 【Substrate入门】创建第一个私有区块链网络
author: 海阳之新
## 操作步骤
一、先看看效果——启动Alice 和 Bob两个节点的区块链网络
二、依葫芦画瓢——定制并启动我们第一个私有区块链网络
## 一、启动Alice和Bob两个节点的区块链网络
为了新人学习的便利性,在官方的学习教程里,特意做了些设计,比如预置了Alice、Bob、Dave...这些虚拟的人物,并为每个人物预先分配了账号(即公私钥),这里所说的启动Alice和Bob两个节点的区块链网络,是指用Alice和Bob的私钥分别启动两个区块链节点,并组成一个区块链网络,Alice和Bob都作为验证人参与出块。通过官方内置的数据,快速创建一个样板网络,来看看效果。
### 1.1 启动第一个节点Alice
1、启动新的测试网络时,我们应该先清理一下旧的链数据。
```
cd substrate-node-template //切换到我们clone时的目录所在位置
./target/release/node-template purge-chain --base-path /tmp/alice --chain local
Are you sure to remove "/tmp/alice/chains/local_testnet/db/full"? [y/N]:
键入y以确认我们要删除链数据。
```
> purge-chain参数的作用:清理旧的区块数据。
2、启动alice区块链节点:
```
./target/release/node-template \
--base-path /tmp/alice \
--chain local \
--alice \
--port 30333 \
--ws-port 9945 \
--rpc-port 9933 \
--node-key 0000000000000000000000000000000000000000000000000000000000000001 \
--telemetry-url "wss://telemetry.polkadot.io/submit/ 0" \
--validator
```
```
2022-09-05 10:23:29 Substrate Node
2022-09-05 10:23:29 ✌️ version 4.0.0-dev-fce479cd55d
2022-09-05 10:23:29 ❤️ by Substrate DevHub <https://github.com/substrate-developer-hub>, 2017-2022
2022-09-05 10:23:29 📋 Chain specification: Local Testnet
2022-09-05 10:23:29 🏷 Node name: Alice
2022-09-05 10:23:29 👤 Role: AUTHORITY
2022-09-05 10:23:29 💾 Database: RocksDb at /tmp/alice/chains/local_testnet/db/full
2022-09-05 10:23:29 ⛓ Native runtime: node-template-100 (node-template-1.tx1.au1)
2022-09-05 10:23:30 🔨 Initializing Genesis block/state (state: 0x2c47…f30c, header-hash: 0x42a9…9b35)
2022-09-05 10:23:30 👴 Loading GRANDPA authority set from genesis on what appears to be first startup.
2022-09-05 10:23:30 Using default protocol ID "sup" because none is configured in the chain specs
2022-09-05 10:23:30 🏷 Local node identity is: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp
2022-09-05 10:23:30 💻 Operating system: macos
2022-09-05 10:23:30 💻 CPU architecture: aarch64
2022-09-05 10:23:30 📦 Highest known block at #0
2022-09-05 10:23:30 〽️ Prometheus exporter started at 127.0.0.1:9615
2022-09-05 10:23:30 Running JSON-RPC HTTP server: addr=127.0.0.1:9933, allowed origins=Some(["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"])
2022-09-05 10:23:30 Running JSON-RPC WS server: addr=127.0.0.1:9945, allowed origins=Some(["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"])
2022-09-05 10:23:30 creating instance on iface 192.168.8.108
2022-09-05 10:23:35 💤 Idle (0 peers), best: #0 (0x42a9…9b35), finalized #0 (0x42a9…9b35), ⬇ 0 ⬆ 0
```
说明:
`🔨 Initializing Genesis block/state (state: 0x2c47…f30c, header-hash: 0x42a9…9b35)` 标识节点正在使用的初始块或创世块。当我们启动下一个节点时,请验证这些值是否相同。
`🏷 Local node identity is: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp` 指定唯一标识此节点的字符串。此字符串由--node-key用于使用alice帐户启动节点的 确定。当我们启动第二个节点时,我们可以使用此字符串来标识要连接的节点。
`💤 Idle (0 peers), best: #0 (0x42a9…9b35), finalized #0 (0x42a9…9b35), ⬇ 0 ⬆ 0 ` 表示网络中没有其他节点并且没有块正在生成。等待另一个节点加入网络,然后才能开始产生块。
### 1.2 向区块链网络添加第二个节点Bob
1、新开一个命令终端。
2、清除Bob节点的旧数据:
```
cd substrate-node-template
./target/release/node-template purge-chain --base-path /tmp/bob --chain local -y
```
通过添加-y到命令中,我们可以在不提示我们确认操作的情况下删除链数据。
4、启动bob区块链节点:
```
./target/release/node-template \
--base-path /tmp/bob \
--chain local \
--bob \
--port 30334 \
--ws-port 9946 \
--rpc-port 9934 \
--telemetry-url "wss://telemetry.polkadot.io/submit/ 0" \
--validator \
--bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp
```
请注意以上命令与启动Alice节点命令有所区别:
由于这两个节点在同一台物理计算机上运行,因此我们必须为--base-path、--port、--ws-port和--rpc-port选项指定不同的值。
此命令中包含--bootnodes选项并指定单个引导节点为alice的节点.
该--bootnodes选项指定了以下信息:
- ip4 表示节点的 IP 地址使用 IPv4 格式
- 127.0.0.1 指定运行节点的 IP 地址
- tcp 将 TCP 指定为用于对等通信的协议
- 30333 指定用于对等通信的端口号。在这种情况下,TCP 流量的端口号。
- `12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp`标识此网络要与之通信的运行节点。这里即alice的节点ID。
可以看到Bob节点启动并出块了:
```
2022-09-05 10:37:32 Substrate Node
2022-09-05 10:37:32 ✌️ version 4.0.0-dev-fce479cd55d
2022-09-05 10:37:32 ❤️ by Substrate DevHub <https://github.com/substrate-developer-hub>, 2017-2022
2022-09-05 10:37:32 📋 Chain specification: Local Testnet
2022-09-05 10:37:32 🏷 Node name: Bob
2022-09-05 10:37:32 👤 Role: AUTHORITY
2022-09-05 10:37:32 💾 Database: RocksDb at /tmp/bob/chains/local_testnet/db/full
2022-09-05 10:37:32 ⛓ Native runtime: node-template-100 (node-template-1.tx1.au1)
2022-09-05 10:37:32 🔨 Initializing Genesis block/state (state: 0x2c47…f30c, header-hash: 0x42a9…9b35)
2022-09-05 10:37:32 👴 Loading GRANDPA authority set from genesis on what appears to be first startup.
2022-09-05 10:37:32 Using default protocol ID "sup" because none is configured in the chain specs
2022-09-05 10:37:32 🏷 Local node identity is: 12D3KooWFBpboeH5XNTtPfwXqFEfEWGZJMYQcmBLjbGj2N4j4Efk
2022-09-05 10:37:32 💻 Operating system: macos
2022-09-05 10:37:32 💻 CPU architecture: aarch64
2022-09-05 10:37:32 📦 Highest known block at #0
2022-09-05 10:37:32 Running JSON-RPC HTTP server: addr=127.0.0.1:9934, allowed origins=Some(["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"])
2022-09-05 10:37:32 Running JSON-RPC WS server: addr=127.0.0.1:9946, allowed origins=Some(["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"])
2022-09-05 10:37:32 creating instance on iface 192.168.8.108
2022-09-05 10:37:32 discovered: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp /ip4/192.168.8.108/tcp/30333
2022-09-05 10:37:32 discovered: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp /ip4/127.0.0.1/tcp/30333
2022-09-05 10:37:32 discovered: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp /ip6/::1/tcp/30333
2022-09-05 10:37:36 ✨ Imported #1 (0x18b4…7661)
2022-09-05 10:37:37 💤 Idle (1 peers), best: #1 (0x18b4…7661), finalized #0 (0x42a9…9b35), ⬇ 1.5kiB/s ⬆ 1.5kiB/s
2022-09-05 10:37:42 🙌 Starting consensus session on top of parent 0x18b484c88f6e69139569cdab33f7a23cc479910049b54986084a2fa7e9467661
2022-09-05 10:37:42 🎁 Prepared block for proposing at 2 (0 ms) [hash: 0x3da4728b33f8dbc8276b3948a6ada698a69819e5214d789250a6e56186f47a01; parent_hash: 0x18b4…7661; extrinsics (1): [0x3bb4…7318]]
2022-09-05 10:37:42 🔖 Pre-sealed block for proposal at 2. Hash now 0xf293a56d396cc2ecd8cac3e5e2ce82506cf3de63f3706c0b4491a3933e0d932a, previously 0x3da4728b33f8dbc8276b3948a6ada698a69819e5214d789250a6e56186f47a01.
```
从以上行中,我们可以得知:
- Bob的节点ID为:`12D3KooWFBpboeH5XNTtPfwXqFEfEWGZJMYQcmBLjbGj2N4j4Efk`
- Bob发现了Alice节点:`discovered: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp /ip4/192.168.8.108/tcp/30333`
5、切换到Alice节点的命令终端,我们可以看到如下提示:
```
2022-09-05 10:37:30 💤 Idle (0 peers), best: #0 (0x42a9…9b35), finalized #0 (0x42a9…9b35), ⬇ 0 ⬆ 0
2022-09-05 10:37:32 discovered: 12D3KooWFBpboeH5XNTtPfwXqFEfEWGZJMYQcmBLjbGj2N4j4Efk /ip4/192.168.8.108/tcp/30334
2022-09-05 10:37:32 discovered: 12D3KooWFBpboeH5XNTtPfwXqFEfEWGZJMYQcmBLjbGj2N4j4Efk /ip4/127.0.0.1/tcp/30334
2022-09-05 10:37:32 discovered: 12D3KooWFBpboeH5XNTtPfwXqFEfEWGZJMYQcmBLjbGj2N4j4Efk /ip6/::1/tcp/30334
2022-09-05 10:37:35 💤 Idle (1 peers), best: #0 (0x42a9…9b35), finalized #0 (0x42a9…9b35), ⬇ 1.2kiB/s ⬆ 1.1kiB/s
2022-09-05 10:37:36 🙌 Starting consensus session on top of parent 0x42a951c48de64e136212d9efa7a7e9bafe372c82289a84ac0068bccce2039b35
2022-09-05 10:37:36 🎁 Prepared block for proposing at 1 (2 ms) [hash: 0x0b1d8260dd90fde80427b97280b53c276d98a627a9446b0bffb4f057bc3feec9; parent_hash: 0x42a9…9b35; extrinsics (1): [0xc405…41a5]]
2022-09-05 10:37:36 🔖 Pre-sealed block for proposal at 1. Hash now 0x18b484c88f6e69139569cdab33f7a23cc479910049b54986084a2fa7e9467661, previously 0x0b1d8260dd90fde80427b97280b53c276d98a627a9446b0bffb4f057bc3feec9.
2022-09-05 10:37:36 ✨ Imported #1 (0x18b4…7661)
2022-09-05 10:37:40 💤 Idle (1 peers), best: #1 (0x18b4…7661), finalized #0 (0x42a9…9b35), ⬇ 0.6kiB/s ⬆ 0.7kiB/s
......
2022-09-05 10:37:50 💤 Idle (1 peers), best: #3 (0x6fda…1700), finalized #1 (0x18b4…7661), ⬇ 0.7kiB/s ⬆ 0.7kiB/s
```
在以上行中,我们可以得知以下信息:
- 在网络上发现了第二个节点Bob身份: `12D3KooWFBpboeH5XNTtPfwXqFEfEWGZJMYQcmBLjbGj2N4j4Efk`。
- 该节点有一个对等点 ( 1 peers)。
- 节点产生了一些块(best: #3 (0x6fda…1700))。
- 区块正在完成(finalized #1 (0x18b4…7661))。
### 1.3 搭建完成
6、通过官方提供的[区块链浏览器](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9945#/explorer)可以直观地看到,我们的本地网络已经搭建好了,正常出块并有最终确定的区块数。

## 二、定制并启动我们第一个私有区块链网络
在本节中,主要需要修改两块内容:
- 将Alice和Bob的密钥替换成我们自己的
- 创建自定义的创世区块配置文件(chain spec文件)
### 2.1 规划网络
| 节点名称 | 数据保存路径 | p2p端口 | ws端口 | rpc端口 |
| --------| -------- | -------- | -------- |-------- |
| MyNode01 | /tmp/node01 | 30333 | 9945 | 9933 |
| MyNode02 | /tmp/node02 | 30334 | 9946 | 9934 |
实际上可以是任意节点个数,这里我们以两个为例,取代Alice和Bob。
### 2.2 生成密钥
先来生成自己的密钥,官方提供了两种方式:
- 通过subkey工具生成
- 通过Polkadot-JS Apps生成
分别来介绍一下。
1、通过subkey工具生成
我们需要先安装subkey,安装参考:https://docs.substrate.io/reference/command-line-tools/subkey/
编译成功后,将subkey文件所在目录加到path中,就可以使用subkey来生成密钥了。
> 一个节点需要有两种不同类型的密钥:sr25519和ed25519,sr25519 密钥用于 Aura 生产区块,ed25519密钥用于 GRANDPA 达成区块的最终确定性。
换句话说, 每个节点都需要添加两种类型的密钥:Aura 和 GRANDPA 密钥。 区块生成需要 Aura 密钥; 而区块达到最终性则需要 GRANDPA 密钥。
我们先来为MyNode01节点生成sr25519类型的密钥(本文是测试,就直接把密钥公开了):
```
$ subkey generate --scheme sr25519
Secret phrase: illness panda blame lobster next phone mushroom license mom ticket live catch
Network ID: substrate
Secret seed: 0xb592b98f6526d95e2b0e4ea2f70bcb374df6fb919c98770f267736458b6e2868
Public key (hex): 0x264466722e5202dff2a202d536494583533f1a21b670f5ea6557e006b025c416
Account ID: 0x264466722e5202dff2a202d536494583533f1a21b670f5ea6557e006b025c416
Public key (SS58): 5CvsziPAPArTj8xaswzynRxq9MuNron2KhuRifLEz7kXUdcn
SS58 Address: 5CvsziPAPArTj8xaswzynRxq9MuNron2KhuRifLEz7kXUdcn
```
再通过上面的助记词 `illness panda blame lobster next phone mushroom license mom ticket live catch` 来查看ed25519密钥:
```
$ subkey inspect --scheme ed25519 "illness panda blame lobster next phone mushroom license mom ticket live catch"
Secret phrase: illness panda blame lobster next phone mushroom license mom ticket live catch
Network ID: substrate
Secret seed: 0xb592b98f6526d95e2b0e4ea2f70bcb374df6fb919c98770f267736458b6e2868
Public key (hex): 0xdeb9a39088e99c957ee2e9ede5e489a87365ec125dcee2ea4fa99e9b45a57b47
Account ID: 0xdeb9a39088e99c957ee2e9ede5e489a87365ec125dcee2ea4fa99e9b45a57b47
Public key (SS58): 5H6jdiWFQNZDrnwHAyts8AnAe8BAkD7vQFXQx6nQ3sPapyJ5
SS58 Address: 5H6jdiWFQNZDrnwHAyts8AnAe8BAkD7vQFXQx6nQ3sPapyJ5
```
将上述信息安全地记录下来,一个节点的密钥就生成完毕了,重复上面的操作为MyNode02生成密钥:
```
$ subkey generate --scheme sr25519
Secret phrase: cheese rude round rail must hockey goose near hip cute inch hurdle
Network ID: substrate
Secret seed: 0x583cbb5c950f340a0d05f63cb5871f251b28091381c139e08ee8e96e349b41cb
Public key (hex): 0xb0969e8d664badadd7121381e858e6fa86c7ebf45655b66ddcf501e38952c657
Account ID: 0xb0969e8d664badadd7121381e858e6fa86c7ebf45655b66ddcf501e38952c657
Public key (SS58): 5G4F2ayxrPW87rMrKumur6MNrstNfcXH4x6pxvBrKZh78Z4R
SS58 Address: 5G4F2ayxrPW87rMrKumur6MNrstNfcXH4x6pxvBrKZh78Z4R
$ subkey inspect --scheme ed25519 "cheese rude round rail must hockey goose near hip cute inch hurdle"
Secret phrase: cheese rude round rail must hockey goose near hip cute inch hurdle
Network ID: substrate
Secret seed: 0x583cbb5c950f340a0d05f63cb5871f251b28091381c139e08ee8e96e349b41cb
Public key (hex): 0x7e1a0e8bf301635af3be1d4f150f20e1a390bff0b2b549f35d9a117e8712b6ce
Account ID: 0x7e1a0e8bf301635af3be1d4f150f20e1a390bff0b2b549f35d9a117e8712b6ce
Public key (SS58): 5Ev3dZMzRKoMT9gpX3nphF4HR68CoRAtZzM2Gv89Yws869ch
SS58 Address: 5Ev3dZMzRKoMT9gpX3nphF4HR68CoRAtZzM2Gv89Yws869ch
```
2、通过Polkadot-JS Apps生成密钥
为了安全,使用Polkadot-JS Apps生成密钥时,建议断网。
这里简单介绍一下,在“ Accounts”选项栏上,点击"Add account"。如下图所示操作:
> 注意妥善保管助记词,为了安全,不建议将此种方法生成的密钥用于生产环境。
### 2.3 创建自定义的创世区块配置文件
这个文件官方称之为Chain Spec文件,个人理解就是区块链的创世区块配置文件。这个文件不需要我们全新编写,只需要从现有项目中导出来做修改即可。
通过如下命令,将 chain spec 导出到一个名为 customSpec.json 的文件中:
```
$ cd substrate-node-template
$ ./target/release/node-template build-spec --disable-default-bootnode --chain local > customSpec.json
2022-09-05 14:16:50 Building chain spec
```
在VS Code中打开这个文件,找到如下部分的内容进行替换(参见注释内容):
```
{
"name": "Local Testnet",
"id": "local_testnet",
"chainType": "Local",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": null,
"codeSubstitutes": {},
"genesis": {
"runtime": {
"system": {
"code": "..."
},
"aura": {
"authorities": [
"5CvsziPAPArTj8xaswzynRxq9MuNron2KhuRifLEz7kXUdcn", // <=== 把这里替换成MyNode01的sr25519类型的SS58 Address
"5G4F2ayxrPW87rMrKumur6MNrstNfcXH4x6pxvBrKZh78Z4R" // <=== 把这里替换成MyNode02的sr25519类型的SS58 Address
]
},
"grandpa": {
"authorities": [
[
"5H6jdiWFQNZDrnwHAyts8AnAe8BAkD7vQFXQx6nQ3sPapyJ5", // <=== 把这里替换成MyNode01的ed25519类型的SS58 Address
1 <=== 作为验证节点的权重值,了解下
],
[
"5Ev3dZMzRKoMT9gpX3nphF4HR68CoRAtZzM2Gv89Yws869ch", // <=== 把这里替换成MyNode02的ed25519类型的SS58 Address
1 <=== 作为验证节点的权重值,了解下
]
]
},
"balances": {
"balances": [
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1152921504606846976
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
1152921504606846976
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
1152921504606846976
],
[
"5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy",
1152921504606846976
],
[
"5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw",
1152921504606846976
],
[
"5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
1152921504606846976
],
[
"5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY",
1152921504606846976
],
[
"5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc",
1152921504606846976
],
[
"5Ck5SLSHYac6WFt5UZRSsdJjwmpSZq85fd5TRNAdZQVzEAPT",
1152921504606846976
],
[
"5HKPmK9GYtE1PSLsS1qiYU9xQ9Si1NcEhdeCq9sw5bqu4ns8",
1152921504606846976
],
[
"5FCfAonRZgTFrTd9HREEyeJjDpT397KMzizE6T3DvebLFE7n",
1152921504606846976
],
[
"5CRmqmsiNFExV6VbdmPJViVxrWmkaXXvBrSX8oqBT8R9vmWk",
1152921504606846976
]
]
},
"transactionPayment": null,
"sudo": {
"key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
}
}
}
}
```
以上内容是已经替换好的效果。"aura" 字段显示,是用于创建区块的 Aura 权限;"grandpa" 字段显示,用于最终确定区块的 GRANDPA 权限。另外,我们还可以看到code字段里有很多16进制数据, 就是 runtime 的 Wasm 二进制文件内容,它是之前运行 cargo build --release 命令时构建的。
将修改好的 customSpec.json 文件保存,我们使用命令将其转换为 "原生"的chain spec文件:
```
./target/release/node-template build-spec --chain=customSpec.json --raw --disable-default-bootnode > customSpecRaw.json
```
最后,将 customSpecRaw.json 与网络中的所有其他验证节点共享。
> chain spec 应该由一人单独创建,并将生成的 customSpecRaw.json 文件共享给其他要加入网络的验证节点。因为 Rust -> Wasm 的优化构建并不 "可重现 ",所以每个人都会得到一个略有不同的 Wasm 二进制代码块,如果每个参与者都自己生成文件,将无法达成共识。
### 2.4 启动创世节点MyNode01
启动MyNode01节点 :
```
./target/release/node-template \
--base-path /tmp/node01 \
--chain ./customSpecRaw.json \
--port 30333 \
--ws-port 9945 \
--rpc-port 9933 \
--telemetry-url 'wss://telemetry.polkadot.io/submit/ 0' \
--validator \
--rpc-methods unsafe \
--name MyNode01
```
命令和参数与启动Alice节点是类似的,但有不同之处,如下:
- --alice 我们省略了该 flag,但很快我们就会通过 RPC 将自己的密钥加入密钥库中。
- --chain 该 flag 选项已更改,以使用我们自定义的 chain spec。
- --name 添加了该 flag,让我们可以在 telemetry 界面给该节点命名。
- --rpc-methods Unsafe 加了这个可选的 flag。 顾名思义,在生产环境中使用该 flag 并不安全,生产环境该怎么使用等待我们探索。
在MyNode01节点窗口中我们应该可以看到如下内容:
```
2022-09-05 14:44:49 Substrate Node
2022-09-05 14:44:49 ✌️ version 4.0.0-dev-fce479cd55d
2022-09-05 14:44:49 ❤️ by Substrate DevHub <https://github.com/substrate-developer-hub>, 2017-2022
2022-09-05 14:44:49 📋 Chain specification: Local Testnet
2022-09-05 14:44:49 🏷 Node name: MyNode01
2022-09-05 14:44:49 👤 Role: AUTHORITY
2022-09-05 14:44:49 💾 Database: RocksDb at /tmp/node01/chains/local_testnet/db/full
2022-09-05 14:44:49 ⛓ Native runtime: node-template-100 (node-template-1.tx1.au1)
2022-09-05 14:44:50 🔨 Initializing Genesis block/state (state: 0x8785…695a, header-hash: 0xea38…79d1)
2022-09-05 14:44:50 👴 Loading GRANDPA authority set from genesis on what appears to be first startup.
2022-09-05 14:44:50 Using default protocol ID "sup" because none is configured in the chain specs
2022-09-05 14:44:50 🏷 Local node identity is: 12D3KooWMY3PXN2MSsFLeVBE5gRyDSAHdW9hScYqN5t4YFDLcB6T
2022-09-05 14:44:50 💻 Operating system: macos
2022-09-05 14:44:50 💻 CPU architecture: aarch64
2022-09-05 14:44:50 📦 Highest known block at #0
2022-09-05 14:44:50 〽️ Prometheus exporter started at 127.0.0.1:9615
2022-09-05 14:44:50 Running JSON-RPC HTTP server: addr=127.0.0.1:9933, allowed origins=Some(["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"])
2022-09-05 14:44:50 Running JSON-RPC WS server: addr=127.0.0.1:9945, allowed origins=Some(["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"])
2022-09-05 14:44:50 creating instance on iface 192.168.8.108
2022-09-05 14:44:52 Accepting new connection, 1/100
2022-09-05 14:44:55 💤 Idle (0 peers), best: #0 (0xea38…79d1), finalized #0 (0xea38…79d1), ⬇ 0 ⬆ 0
```
### 2.5 将密钥添加到密钥库
MyNode01节点开始运行,我们会再次发现没有区块生成。 此时,我们应该将节点的密钥加进密钥库里。 注意:我们需要在网络中的每一个节点都完成这些步骤,记得操作时切换不同节点的端口。
添加方式有两种可选:1、通过命令curl的方式;2、通过Apps界面的方式。分别来介绍一下。
选项一:通过curl的方式
先通过命令curl的方式操作,这也是官方推荐的方式,因为在生产环境安全性是最重要的,所以必须采取一切可能的预防措施。 这意味着在不要在任何地方留下密钥的痕迹,比如在终端的历史记录中。 创建一个文件,用于定义 curl 请求的内容:
```
{
"jsonrpc":"2.0",
"id":1,
"method":"author_insertKey",
"params": [
"填写aura或gran,必填",
"填写生成密钥时的助记词,必填",
"对应的公钥,必填"
]
}
```
这里笔者在当前目录下,创建了一个专门的目录mykey用于存储这些文件:
如:`mykey/node01_aura.json`、`mykey/node01_gran.json`
内容分别为:
```
mykey/node01_aura.json文件内容:
{
"jsonrpc":"2.0",
"id":1,
"method":"author_insertKey",
"params": [
"aura",
"engine cube pioneer firm thought exhibit carbon dumb exotic pulse noodle ceiling",
"0x00301f5ae5f5e5f5067fb10728e963680704c96ee5472e30e370faf1e49d843e"
]
}
mykey/node01_gran.json文件内容:
{
"jsonrpc":"2.0",
"id":1,
"method":"author_insertKey",
"params": [
"gran",
"engine cube pioneer firm thought exhibit carbon dumb exotic pulse noodle ceiling",
"0x063f488785f1f63ecaf7dd74cce9b73a91904f3349ecfebb1e1dc0abf8f8b5dd"
]
}
```
分别执行如下命令,将MyNode01节点的密钥导入密钥库,这里的端口为MyNode01节点的rpc端口:
```
curl http://localhost:9933 -H "Content-Type:application/json;charset=utf-8" -d "@/Users/yangxinmin/workspace/rustproject/play-substrate-node-template/nodekey/node01_aura.json"
curl http://localhost:9933 -H "Content-Type:application/json;charset=utf-8" -d "@/Users/yangxinmin/workspace/rustproject/play-substrate-node-template/nodekey/node01_gran.json"
```
如果命令和参数输入正确,则节点将返回如下的 JSON 响应:
```
{ "jsonrpc": "2.0", "result": null, "id": 1 }
```
这样我们对MyNode01节点的密钥操作就完成了。
选项二:通过Apps界面的方式



通过以上操作就完成了MyNode01的密钥注入,在界面上切换到MyNode02的端口,重复以上操作,注意参数值的不同,最后重启节点即可。
### 2.6 启动第二个节点MyNode02
```
./target/release/node-template \
--base-path /tmp/node02 \
--chain ./customSpecRaw.json \
--port 30334 \
--ws-port 9946 \
--rpc-port 9934 \
--telemetry-url 'wss://telemetry.polkadot.io/submit/ 0' \
--validator \
--rpc-methods unsafe \
--name MyNode02 \
--bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWMY3PXN2MSsFLeVBE5gRyDSAHdW9hScYqN5t4YFDLcB6T
```
和之前一样,我们指定了另一个 base-path 和不同端口,给它另一个 name 参数,并指定此节点为 validator。
接下来重复MyNode01使用curl方式导入密钥的操作,对MyNode02节点的密钥进行导入,但是这里操作不当就会有坑,重点强调一下:除了文件和内容都换成MyNode02节点之外,在执行curl命令时,记得切换rpc端口。如下:
```
curl http://localhost:9934 -H "Content-Type:application/json;charset=utf-8" -d "@/Users/yangxinmin/workspace/rustproject/play-substrate-node-template/nodekey/node02_aura.json"
curl http://localhost:9934 -H "Content-Type:application/json;charset=utf-8" -d "@/Users/yangxinmin/workspace/rustproject/play-substrate-node-template/nodekey/node02_gran.json"
```
我们会注意到,即使在为第二个节点添加密钥之后,依然没有区块完成最终确定性 (finalized #0)。 Substrate节点在添加 GRANDPA 密钥后需要重启。 在节点命令终端按ctrl+c中止节点,使用与之前相同的命令重启他们,此时区块就可以达成最终确定性。
查看MyNode01节点,我们可以看到网络正常出块并确认区块了:
```
2022-09-05 15:14:45 Substrate Node
2022-09-05 15:14:45 ✌️ version 4.0.0-dev-fce479cd55d
2022-09-05 15:14:45 ❤️ by Substrate DevHub <https://github.com/substrate-developer-hub>, 2017-2022
2022-09-05 15:14:45 📋 Chain specification: Local Testnet
2022-09-05 15:14:45 🏷 Node name: MyNode01
2022-09-05 15:14:45 👤 Role: AUTHORITY
2022-09-05 15:14:45 💾 Database: RocksDb at /tmp/node01/chains/local_testnet/db/full
2022-09-05 15:14:45 ⛓ Native runtime: node-template-100 (node-template-1.tx1.au1)
2022-09-05 15:14:46 Using default protocol ID "sup" because none is configured in the chain specs
2022-09-05 15:14:46 🏷 Local node identity is: 12D3KooWMY3PXN2MSsFLeVBE5gRyDSAHdW9hScYqN5t4YFDLcB6T
2022-09-05 15:14:46 💻 Operating system: macos
2022-09-05 15:14:46 💻 CPU architecture: aarch64
2022-09-05 15:14:46 📦 Highest known block at #40
2022-09-05 15:14:46 〽️ Prometheus exporter started at 127.0.0.1:9615
2022-09-05 15:14:46 Running JSON-RPC HTTP server: addr=127.0.0.1:9933, allowed origins=Some(["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"])
2022-09-05 15:14:46 Running JSON-RPC WS server: addr=127.0.0.1:9945, allowed origins=Some(["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"])
2022-09-05 15:14:46 creating instance on iface 192.168.8.108
2022-09-05 15:14:47 Accepting new connection, 1/100
2022-09-05 15:14:51 💤 Idle (0 peers), best: #40 (0xfafd…a65b), finalized #0 (0xea38…79d1), ⬇ 0 ⬆ 0
2022-09-05 15:14:52 discovered: 12D3KooWLhjJez781vm14FV6yzrnLi1zoQctfqdRn7BFvghoWXWe /ip4/192.168.8.108/tcp/30334
2022-09-05 15:14:52 discovered: 12D3KooWLhjJez781vm14FV6yzrnLi1zoQctfqdRn7BFvghoWXWe /ip4/127.0.0.1/tcp/30334
2022-09-05 15:14:52 discovered: 12D3KooWLhjJez781vm14FV6yzrnLi1zoQctfqdRn7BFvghoWXWe /ip6/::1/tcp/30334
2022-09-05 15:14:54 ✨ Imported #41 (0x2166…86d1)
2022-09-05 15:14:56 💤 Idle (1 peers), best: #41 (0x2166…86d1), finalized #38 (0x9ebb…666b), ⬇ 1.3kiB/s ⬆ 1.1kiB/s
2022-09-05 15:15:00 🙌 Starting consensus session on top of parent 0x21660af826ebd7044d844761055f8b750ddd19176000fa6d4eacaa7c81c886d1
2022-09-05 15:15:00 🎁 Prepared block for proposing at 42 (0 ms) [hash: 0xb4e86f60f2593b296cea6054266a5b9040c088873761ddf99ebe659a05abe8c7; parent_hash: 0x2166…86d1; extrinsics (1): [0x5002…05ae]]
2022-09-05 15:15:00 🔖 Pre-sealed block for proposal at 42. Hash now 0xd4d456cb85f9f546d2e28fb06dc80cf1b570711def3cc2a42e252dc29d4a10ff, previously 0xb4e86f60f2593b296cea6054266a5b9040c088873761ddf99ebe659a05abe8c7.
```

好了,到此就大功告成了!
## 小结
通过本文的学习,我们熟悉并掌握了怎样创建一个自己的区块链网络,包括学会了生成自己的账号密钥、导入密钥到密钥库,并创建一个使用该密钥对的自定义创世区块配置文件(chain spec),离“使用substrate创建的区块链网络用于生产环境”又更近了一步了。
---
参考:https://docs.substrate.io/tutorials/get-started/simulate-network/