# 【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)可以直观地看到,我们的本地网络已经搭建好了,正常出块并有最终确定的区块数。 ![](https://i.imgur.com/LBRaruH.png) ## 二、定制并启动我们第一个私有区块链网络 在本节中,主要需要修改两块内容: - 将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界面的方式 ![](https://i.imgur.com/WIMOB1H.png) ![](https://i.imgur.com/4WuoJtC.png) ![](https://i.imgur.com/y1nmDr2.png) 通过以上操作就完成了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. ``` ![](https://i.imgur.com/TMoQkam.png) 好了,到此就大功告成了! ## 小结 通过本文的学习,我们熟悉并掌握了怎样创建一个自己的区块链网络,包括学会了生成自己的账号密钥、导入密钥到密钥库,并创建一个使用该密钥对的自定义创世区块配置文件(chain spec),离“使用substrate创建的区块链网络用于生产环境”又更近了一步了。 --- 参考:https://docs.substrate.io/tutorials/get-started/simulate-network/