Substrate 基础教程(Tutorials) -- 模拟网络 添加可信节点
三、模拟网络
本教程基本介绍了如何使用一个私有验证器(validators)的授权集合来启动私有区块链网络。
Substrate节点模板使用授权共识模型(authority consensus model),该模型将块生产限制为授权帐户的旋转列表(rotating list)。授权帐户(authorities,授权机构)负责以循环方式创建块。
在本教程中,您将通过使用两个预定义的帐户作为允许节点生成块的权限,了解权限共识模型在实践中是如何工作的。在这个模拟网络中,两个节点使用不同的帐户和密钥启动,但运行在一台计算机上。
3.1 教程的目标
通过完成本教程,您将实现以下目标:
- 使用预定义帐户启动区块链节点。
- 了解用于启动节点的关键命令行选项。
- 确定一个节点是否正在运行并生成块。
- 将第二个节点连接到运行中的网络。
- 验证对等计算机生成和完成块。
3.2 启动第一个区块链节点
在生成密钥以启动自己的专用Substrate网络之前,可以使用称为local的预定义网络规范学习基本原理,该规范在预定义用户帐户下运行。
本教程通过在一台本地计算机上使用命名为alice和bob的预定义帐户运行两个Substrate节点来模拟专用网络。
启动区块链:
- 在计算机上打开一个终端。
- 切换到编译Substrate节点模板的根目录。
- 运行命令如下:清除旧链数据。
./target/release/node-template purge-chain --base-path /tmp/alice --chain local
命令提示用户确认操作:
Are you sure to remove "/tmp/alice/chains/local_testnet/db"? [y/N]:
-
输入
y以确认要删除链数据。
当你开始一个新的网络时,你应该总是删除旧的链数据。 -
以“
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 --unsafe-ws-external

检查命令行选项
在继续之前,看看如何使用以下选项启动节点。
| Option | Option |
|---|---|
| –base-path | 指定用于存储与此链相关的所有数据的目录 |
| –chain local | 指定要使用的链规范。有效的预定义链规范包括local, development, 和staging. |
| –alice | 将alice帐户的预定义密钥添加到节点的密钥存储库中。使用此设置,alice帐户将用于块生产和finalization。 |
| –port 30333 | 指定侦听点对点(p2p)流量的端口。由于本教程使用在同一物理计算机上运行的两个节点来模拟网络,因此必须为至少一个帐户显式指定不同的端口。 |
| –ws-port 9945 | 指定监听进入 WebSocket 流量的端口。默认端口号为9944。本教程使用自定义WebSocket 端口号(9945) |
| –rpc-port 9933 | 指定侦听传入RPC通信的端口。默认端口号为9933。 |
| –node-key | 指定用于libp2p网络的Ed25519秘密密钥。您应该只在开发和测试时使用此选项 |
| –telemetry-url | Specifies where to send telemetry data. For this tutorial, you can send telemetry data to a server hosted by Parity that is available for anyone to use.指定发送遥测数据的位置。在本教程中,您可以将遥测数据发送到Parity托管的任何人都可以使用的服务器。 |
| –validator | Specifies that this node participates in block production and finalization for the network. 指定该节点参与网络的块生产和结束。 |
有关节点模板(node template)可用的命令行选项的更多信息,请参见运行以下命令的用法帮助:
./target/release/node-template --help
查看显示的节点消息
如果节点启动成功,终端显示网络操作信息。例如,你应该看到类似这样的输出:

特别地,您应该注意输出中的以下消息:
Initializing Genesis block/state (state: 0x4e1c…a8dc, header-hash: 0xbd60…2cb6):标识节点正在使用的初始块或起源块。当启动下一个节点时,验证这些值是否相同。
Local node identity is: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp :指定唯一标识此节点的字符串。该字符串由--node-key决定,该key用于使用alice帐户启动节点。在启动第二个节点时,可以使用此字符串标识要连接的节点。
Idle (0 peers), best: #0 (0xbd60…2cb6), finalized #0 (0xbd60…2cb6), ⬇ 0 ⬆ 0:表示网络中没有其他节点,也没有正在生成的块。在区块开始生成之前,必须有另一个节点加入网络。
3.3 添加第二个节点到区块链网络
现在您使用alice帐户密钥启动的节点正在运行,您可以使用bob帐户将另一个节点添加到网络中。因为您正在加入一个已经在运行的网络,所以您可以使用正在运行的节点来标识新节点要加入的网络。这些命令与您以前使用的命令类似,但有一些重要的区别。
向运行中的区块链添加节点:
- 在计算机上打开一个新的终端。
- 切换到编译Substrate节点模板的根目录。
- 使用实例清除旧链数据。
./target/release/node-template purge-chain --base-path /tmp/bob --chain local -y
- 以“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
注意此命令与上一个命令的区别如下:
因为两个节点运行在同一台物理计算机上,所以必须为--base-path、--port、--ws-port和--rpc-port选项指定不同的值。
该命令包括--bootnodes选项,并指定一个引导节点,即由alice启动的节点。
The --bootnodes option specifies the following information:
ip4表示节点IP地址为IPv4格式127.0.0.1为运行节点的IP地址。在本例中,是localhost的地址。- tcp指定使用tcp协议进行点对点通信。
30333为点对点通信时使用的端口号。 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp标识此网络中要与之通信的运行节点。在本例中,节点的标识符开始使用alice帐户。
3.4 验证块的生产和最终确定
启动第二个节点后,这些节点应该作为对等点相互连接,并开始生成块。
- 验证块正在完成:
验证您在启动第一个节点的终端中看到类似于下面的行:
2023-03-01 11:08:13 Low open file descriptor limit configured for the process. Current value: 4096, recommended value: 10000.
2023-03-01 11:08:13 Substrate Node
2023-03-01 11:08:13 ✌️ version 4.0.0-dev-d79d8cef20b
2023-03-01 11:08:13 ❤️ by Substrate DevHub <https://github.com/substrate-developer-hub>, 2017-2023
2023-03-01 11:08:13 📋 Chain specification: Local Testnet
2023-03-01 11:08:13 🏷 Node name: Bob
2023-03-01 11:08:13 👤 Role: AUTHORITY
2023-03-01 11:08:13 💾 Database: RocksDb at /tmp/bob/chains/local_testnet/db/full
2023-03-01 11:08:13 ⛓ Native runtime: node-template-100 (node-template-1.tx1.au1)
2023-03-01 11:08:15 🔨 Initializing Genesis block/state (state: 0x4e1c…a8dc, header-hash: 0xbd60…2cb6)
2023-03-01 11:08:16 👴 Loading GRANDPA authority set from genesis on what appears to be first startup.
2023-03-01 11:08:16 Using default protocol ID "sup" because none is configured in the chain specs
2023-03-01 11:08:16 🏷 Local node identity is: 12D3KooWGGyA4aXnaEkazB8hFYUt2Pf9mDN5nYEGeoDwV73UYG4v
2023-03-01 11:08:16 💻 Operating system: linux
2023-03-01 11:08:16 💻 CPU architecture: x86_64
2023-03-01 11:08:16 💻 Target environment: gnu
2023-03-01 11:08:16 💻 CPU: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
2023-03-01 11:08:16 💻 CPU cores: 2
2023-03-01 11:08:16 💻 Memory: 1960MB
2023-03-01 11:08:16 💻 Kernel: 5.4.230-1.el7.elrepo.x86_64
2023-03-01 11:08:16 💻 Linux distribution: CentOS Linux 7 (Core)
2023-03-01 11:08:16 💻 Virtual machine: yes
2023-03-01 11:08:16 📦 Highest known block at #0
2023-03-01 11:08:16 Running JSON-RPC HTTP server: addr=127.0.0.1:9934, allowed origins=["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"]
2023-03-01 11:08:16 Running JSON-RPC WS server: addr=127.0.0.1:9946, allowed origins=["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"]
2023-03-01 11:08:16 discovered: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp /ip4/192.168.122.1/tcp/30333
2023-03-01 11:08:16 discovered: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp /ip4/172.18.0.1/tcp/30333
2023-03-01 11:08:16 discovered: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp /ip4/172.17.0.1/tcp/30333
2023-03-01 11:08:16 discovered: 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp /ip4/192.168.159.128/tcp/30333
2023-03-01 11:08:18 🙌 Starting consensus session on top of parent 0xbd60aaeb57aeb6a7620f7450d5d7bc07410fe512591d0032979ff5ba65bf2cb6
2023-03-01 11:08:18 🎁 Prepared block for proposing at 1 (0 ms) [hash: 0xdb613010821ae195a3fa19fa051c6c9349322115930ad9a37d19f01d145d68a0; parent_hash: 0xbd60…2cb6; extrinsics (1): [0xcaad…5f79]]
2023-03-01 11:08:18 🔖 Pre-sealed block for proposal at 1. Hash now 0x8ace1e4b0545173d26b8dddef90c0b8e264ab38ca61c65a287707187f8139a48, previously 0xdb613010821ae195a3fa19fa051c6c9349322115930ad9a37d19f01d145d68a0.
2023-03-01 11:08:18 ✨ Imported #1 (0x8ace…9a48)
2023-03-01 11:08:21 💤 Idle (1 peers), best: #1 (0x8ace…9a48), finalized #0 (0xbd60…2cb6), ⬇ 1.8kiB/s ⬆ 1.9kiB/s
2023-03-01 11:15:39 💤 Idle (1 peers), best: #17 (0x6d18…12fe), finalized #15 (0x20ac…5747), ⬇ 0.7kiB/s ⬆ 0.6kiB/s
2023-03-01 11:15:42 🙌 Starting consensus session on top of parent 0x6d1894bbb824f7534cc9c1834722e2ffb38f1e72dcd71e0d21fdf992f84212fe
2023-03-01 11:15:42 🎁 Prepared block for proposing at 18 (0 ms) [hash: 0x409738e75bf5e80aee3a126d274dce1339b2924372e0c323e2f5312b391ff8b0; parent_hash: 0x6d18…12fe; extrinsics (1): [0xf57c…57da]]
2023-03-01 11:15:42 🔖 Pre-sealed block for proposal at 18. Hash now 0xa53d462295b3c84fb86f471a9ee666f7cc398218f65950462b6edcc6aa63a0df, previously 0x409738e75bf5e80aee3a126d274dce1339b2924372e0c323e2f5312b391ff8b0.
2023-03-01 11:15:42 ✨ Imported #18 (0xa53d…a0df)
2023-03-01 11:15:44 💤 Idle (1 peers), best: #18 (0xa53d…a0df), finalized #16 (0x0604…04af), ⬇ 0.6kiB/s ⬆ 0.8kiB/s
2023-03-01 11:15:48 ✨ Imported #19 (0x2370…cfe1)
2023-03-01 11:15:49 💤 Idle (1 peers), best: #19 (0x2370…cfe1), finalized #17 (0x6d18…12fe), ⬇ 0.7kiB/s ⬆ 0.6kiB/s
2023-03-01 11:15:54 🙌 Starting consensus session on top of parent 0x2370ab5a6ad0e4fd4a36cd18aa4d58dbb70a9be80e887d84cbaa8116945fcfe1
2023-03-01 11:15:54 🎁 Prepared block for proposing at 20 (6 ms) [hash: 0xf1f4369f6f8dc41adc0d6e7b5d96489d0dcedce232e5c075477a84db6157ab1c; parent_hash: 0x2370…cfe1; extrinsics (1): [0xa4fe…dbc3]]
2023-03-01 11:15:54 🔖 Pre-sealed block for proposal at 20. Hash now 0x7e307e4e16102a98a125c7c00fb697ceaa5b8db8f1b32b55419686c0c96cad5e, previously 0xf1f4369f6f8dc41adc0d6e7b5d96489d0dcedce232e5c075477a84db6157ab1c.
2023-03-01 11:15:54 ✨ Imported #20 (0x7e30…ad5e)
2023-03-01 11:15:54 💤 Idle (1 peers), best: #20 (0x7e30…ad5e), finalized #17 (0x6d18…12fe), ⬇ 0.6kiB/s ⬆ 0.7kiB/s
2023-03-01 11:15:59 💤 Idle (1 peers), best: #20 (0x7e30…ad5e), finalized #18 (0xa53d…a0df), ⬇ 0.6kiB/s ⬆ 0.6kiB/s
2023-03-01 11:16:00 ✨ Imported #21 (0x0470…7707)
在这些行中,您可以看到关于您的区块链的以下信息:
- 在网络上发现第二个节点标识
12D3KooWGGyA4aXnaEkazB8hFYUt2Pf9mDN5nYEGeoDwV73UYG4v - The node has a one peer (1 peers).
- The nodes have produced some blocks
best: #20 (0x7e30…ad5e) - 这些区块正在最后确定
finalized #17 (0x6d18…12fe)
- 验证在启动第二个节点的终端中是否看到类似的输出。
- 在终端shell中按Control-c关闭其中一个节点。
- 关闭节点后,您将看到剩下的节点现在没有对等点,并且已经停止生成块。例如:
2023-03-01 11:16:03 💤 Idle (1 peers), best: #21 (0x0470…7707), finalized #19 (0x2370…cfe1), ⬇ 0.7kiB/s ⬆ 0.8kiB/s
2023-03-01 11:16:06 ✨ Imported #22 (0x6481…152b)
2023-03-01 11:16:08 💤 Idle (0 peers), best: #22 (0x6481…152b), finalized #19 (0x2370…cfe1), ⬇ 0.6kiB/s ⬆ 0.5kiB/s
- 在终端shell中按Control-c关闭第二个节点。
如果您想从模拟网络中删除链状态,可以在/tmp/bob和/tmp/alice目录中使用带有--base-path命令行选项的purge-chain子命令。
四、添加可信节点
本教程演示如何使用一个私有验证器(validator)的 授权集启动一个小型的独立区块链网络。
正如你在区块链基础知识中所学到的,所有区块链都要求网络中的节点在任何特定的时间点对数据的状态达成一致,这种关于状态的一致被称为共识(consensus)。
Substrate节点模板使用授权证明共识模型(proof of authority consensus model),也称为authority round或Aura共识。Aura共识协议将区块生产限制为授权帐户的轮询列表。授权帐户(authorities)以轮询方式创建块,通常被认为是网络中可信的参与者。
这个共识模型为有限数量的参与者提供了一个简单的方法来启动一个单独的区块链。在本教程中,您将了解如何生成授权节点参与网络所需的密钥,如何配置网络并与其他授权帐户共享有关网络的信息,以及如何使用一组经过批准的验证器启动网络。
4.1 教程的目标
通过完成本教程,您将实现以下目标:
- 生成密钥对以作为网络授权使用。
- 创建一个自定义链规范文件。
- 启动私有双节点区块链网络。
4.2 生成您的帐户和密钥
在模拟网络中,使用预定义的帐户和密钥启动对等节点。对于本教程,您将为网络中的验证器节点生成自己的密钥。重要的是要记住,区块链网络中的每个参与者都负责生成唯一密钥。
key 生成选项
有几种生成密钥的方法。例如,您可以使用node-template子命令、独立subkey命令行程序、Polkadot-JS应用程序或第三方密钥生成实用程序生成密钥对。
虽然您可以使用预定义的键对来完成本教程,但您绝不会在生产环境中使用这些键。本教程没有使用预定义的密钥或更安全的subkey 程序,而是说明如何使用Substrate节点模板和key子命令生成密钥。
使用节点模板生成本地密钥
作为最佳实践,在为生产区块链(production blockchain)生成密钥时,应该使用从未连接到internet的air-gapped计算机。至少,在生成任何密钥用于不受您控制的公共或私有区块链之前,您应该断开与互联网的连接。
但是,对于本教程,您可以使用node-template命令行选项在本地生成随机键,同时保持与互联网的连接。
- 运行如下命令生成一个随机的秘密短语和密钥:
./target/release/node-template key generate --scheme Sr25519 --password-interactive

你现在有了Sr25519 key,可以在一个节点上使用aura生成区块。在此例中,帐号的Sr25519公钥为5Esh9DFdgPTsoBYNWCBRy5z91WkPhE35yskGivwvapqi2DNd。
- 对刚刚生成的帐户使用
secret phrase,以使用Ed25519签名方案派生密钥。
./target/release/node-template key inspect --password-interactive --scheme Ed25519 "reduce nerve siren ramp pride wrist main cry dolphin immune car group"
- 输入用于生成密钥的密码。
[root@localhost substrate-node-template]# ./target/release/node-template key inspect --password-interactive --scheme Ed25519 "reduce nerve siren ramp pride wrist main cry dolphin immune car group"
Key password:
Secret phrase: reduce nerve siren ramp pride wrist main cry dolphin immune car groupNetwork ID: substrateSecret seed: 0x157b8dec08acbcf1abbdff5256586cce1f6d096959d0b6e9c90e3a244c1e1831Public key (hex): 0x03424a4a780766de57469c422d771c44e24e0fc97ccdc28e9f92a4e107b443f2Account ID: 0x03424a4a780766de57469c422d771c44e24e0fc97ccdc28e9f92a4e107b443f2Public key (SS58): 5C8yh9u4rwY3VrQW6pDdScnP6WTx7xJfQSn53K44Uw1ShCuwSS58 Address: 5C8yh9u4rwY3VrQW6pDdScnP6WTx7xJfQSn53K44Uw1ShCuw
现在您有了Ed25519 key,用于对一个节点使用grandpa 来确认块。在此例中,帐号的Ed25519公钥为5C8yh9u4rwY3VrQW6pDdScnP6WTx7xJfQSn53K44Uw1ShCuw。
生成第二组密钥
对于本教程,专用网络仅由两个节点组成,因此需要两组密钥。你有几个选项来继续本教程:
- 您可以对预定义帐户之一使用密钥。
- 您可以在本地计算机上使用不同的标识重复上一节中的步骤,以生成第二个密钥对。
- 您可以派生子密钥对来模拟本地计算机上的第二个标识。
- 您可以招募其他参与者来生成加入您的私有网络所需的密钥。
[root@localhost substrate-node-template]# ./target/release/node-template key generate --scheme Sr25519 --password-interactive
Key password:
Secret phrase: zoo trip idle enable gaze betray shy caught harvest crash warm puddingNetwork ID: substrateSecret seed: 0x0364437d6f1ff68042d0f05e98c63a2e56a691b9ed5474af8048b63696d78a8fPublic key (hex): 0x065ccea9171c88a1a1098c4d21576b9b54b334c23ccc06c427164fa37bc7324cAccount ID: 0x065ccea9171c88a1a1098c4d21576b9b54b334c23ccc06c427164fa37bc7324cPublic key (SS58): 5CD3iN6qxUUvay2hVMBSgTJAXHXZgFPJU4i3jkNVBMAqUB24SS58 Address: 5CD3iN6qxUUvay2hVMBSgTJAXHXZgFPJU4i3jkNVBMAqUB24
[root@localhost substrate-node-template]# ./target/release/node-template key inspect --password-interactive --scheme Ed25519 'zoo trip idle enable gaze betray shy caught harvest crash warm pudding'
Key password:
Secret phrase: zoo trip idle enable gaze betray shy caught harvest crash warm puddingNetwork ID: substrateSecret seed: 0x0364437d6f1ff68042d0f05e98c63a2e56a691b9ed5474af8048b63696d78a8fPublic key (hex): 0x19b6e3e347b28fd5bee94cb57945d9cb116f81741e35f5e0731c3cec019b741bAccount ID: 0x19b6e3e347b28fd5bee94cb57945d9cb116f81741e35f5e0731c3cec019b741bPublic key (SS58): 5CeRPE4xNPqrUCRChmSGG2KZ4jDcVsBdj64BRfeVeC3xa4FSSS58 Address: 5CeRPE4xNPqrUCRChmSGG2KZ4jDcVsBdj64BRfeVeC3xa4FS
4.3 创建自定义链规范
生成用于区块链的密钥后,就可以使用这些密钥对创建自定义链规范,然后与称为验证者(validators)的可信网络参与者共享自定义链规范。
要使其他人能够参与您的区块链网络,请确保他们生成自己的密钥。收集网络参与者的密钥后,可以创建自定义链规范来替换local 链规范。
为简单起见,在本教程中创建的自定义链规范是local 链规范的修改版本,该规范演示了如何创建双节点网络。如果您拥有所需的密钥,可以按照相同的步骤向网络添加更多节点。
修改local 链规格
您可以修改预定义的local 链规范,而不是编写一个全新的链规范。
基于本地规范创建一个新的链规范:
- 将本地链规范导出到名为
customSpec.json的文件中。通过执行以下命令查看:
./target/release/node-template build-spec --disable-default-bootnode --chain local > customSpec.json
如果您打开customSpec.json文件在文本编辑器中,您将看到它包含几个字段。其中一个字段是使用cargo build --release命令构建的运行时的WebAssembly (Wasm)二进制文件。因为WebAssembly (Wasm)二进制文件是一个大的blob,您可以预览第一行和最后几行,以查看需要更改的字段。
- 预览
customSpec.json中的前几个字段。执行以下命令:
head customSpec.json

3. 预览customSpec.json中的最后几行:
tail -n 80 customSpec.json
该命令显示Wasm二进制字段后面的最后部分,包括运行时中使用的几个pallet盘(如sudo和balanced托盘)的详细信息。
- 修改
name字段,将此链规范标识为自定义链规范。
"name": "My Custom Testnet",
-
修改
aura字段,为每个网络参与者添加Sr25519 SS58地址密钥,指定有权限创建块的节点。 -
修改
grandpa字段,通过为每个网络参与者添加Ed25519 SS58地址密钥,指定具有完成块的权限的节点。
"aura": {"authorities": ["5Esh9DFdgPTsoBYNWCBRy5z91WkPhE35yskGivwvapqi2DNd","5CD3iN6qxUUvay2hVMBSgTJAXHXZgFPJU4i3jkNVBMAqUB24"]},"grandpa": {"authorities": [["5C8yh9u4rwY3VrQW6pDdScnP6WTx7xJfQSn53K44Uw1ShCuw",1],["5CeRPE4xNPqrUCRChmSGG2KZ4jDcVsBdj64BRfeVeC3xa4FS",1]]},
请注意,grandpa 部分中的authorities 字段有两个数据值。第一个值是地址key。第二个值用于支持加权投票(weighted votes)。在本例中,每个验证器的权重为1票。
- 添加验证器
正如您刚才看到的,您可以通过修改aura和grandpa部分来添加和更改链规范中的授权地址(authority addresses)。您可以使用此技术添加任意数量的验证器。
To add validators:
- Modify the
aurasection to include Sr25519 addresses. - Modify the
grandpasection to include Ed25519 addresses and a voting weight.
有关使用密钥对和签名的更多信息,请参见 Public-Key cryptography
4.4 将链规范转换为原始格式
在准备了带有验证器信息的链规范之后,必须在使用它之前将其转换为原始规范格式(raw specification format)。原始链规范包含与未转换的规范相同的信息。但是,原始链规范还包含编码的存储键,节点使用这些存储键引用本地存储中的数据。分发原始链规范可确保每个节点使用适当的存储密钥存储数据。
将一个链规范转换为使用原始格式:
用文件名customSpecRaw.json转换customSpec_my.json链规范到原始格式。
./target/release/node-template build-spec --chain=customSpec_my.json --raw --disable-default-bootnode > customSpecRaw.json
4.5 准备启动私有网络
将自定义链规范分发给所有网络参与者之后,就可以启动自己的私有区块链了。步骤与启动第一个区块链节点中所遵循的步骤类似。但是,如果您遵循本教程中的步骤,您将能够将多台计算机添加到网络中。
要继续,请验证以下内容:
- 您已经生成或收集了至少两个权限帐户的帐户密钥。
- 您已经更新了自定义链规范,以包括区块生产(aura)和区块终结(grandpa)的键。
- 您已经将自定义链规范转换为原始格式,并将原始链规范分发到参与私有网络的节点。
4.6 启动第一个节点
作为私有区块链网络中的第一个参与者,您负责启动称为引导节点(bootnode)的第一个节点。
运行如下命令,使用自定义链规范启动第一个节点:
./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 \--password-interactive \--unsafe-ws-external
请注意以下命令行选项,您正在使用来启动节点:
- The
--validatorcommand-line option indicates that this node is an authority for the chain. - The
--rpc-methods Unsafecommand-line option allows you to continue the tutorial using an unsafe communication mode because your blockchain is not being used in a production setting. - The
--namecommand-line option enables you to give your node a human-readable name in the telemetry UI.
该命令还使用您自己的密钥而不是预定义的帐户启动节点。因为您没有使用具有已知密钥的预定义帐户,所以需要在单独的步骤中将密钥添加到密钥存储库中。
4.7 查看节点操作信息
2023-03-01 14:34:58 Low open file descriptor limit configured for the process. Current value: 4096, recommended value: 10000.
2023-03-01 14:34:58 Substrate Node
2023-03-01 14:34:58 ✌️ version 4.0.0-dev-d79d8cef20b
2023-03-01 14:34:58 ❤️ by Substrate DevHub <https://github.com/substrate-developer-hub>, 2017-2023
2023-03-01 14:34:58 📋 Chain specification: My Custom Testnet
2023-03-01 14:34:58 🏷 Node name: MyNode01
2023-03-01 14:34:58 👤 Role: AUTHORITY
2023-03-01 14:34:58 💾 Database: RocksDb at /tmp/node01/chains/local_testnet/db/full
2023-03-01 14:34:58 ⛓ Native runtime: node-template-100 (node-template-1.tx1.au1)
2023-03-01 14:34:58 🔨 Initializing Genesis block/state (state: 0x972a…636b, header-hash: 0x7633…9642)
2023-03-01 14:34:58 👴 Loading GRANDPA authority set from genesis on what appears to be first startup.
2023-03-01 14:34:59 Using default protocol ID "sup" because none is configured in the chain specs
2023-03-01 14:34:59 🏷 Local node identity is: 12D3KooWAecWYYGnLz6iUsQgU1kmTcVPyNunbwQaV14oeaW31jmB
2023-03-01 14:34:59 💻 Operating system: linux
2023-03-01 14:34:59 💻 CPU architecture: x86_64
2023-03-01 14:34:59 💻 Target environment: gnu
2023-03-01 14:34:59 💻 CPU: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
2023-03-01 14:34:59 💻 CPU cores: 2
2023-03-01 14:34:59 💻 Memory: 1960MB
2023-03-01 14:34:59 💻 Kernel: 5.4.230-1.el7.elrepo.x86_64
2023-03-01 14:34:59 💻 Linux distribution: CentOS Linux 7 (Core)
2023-03-01 14:34:59 💻 Virtual machine: yes
2023-03-01 14:34:59 📦 Highest known block at #0
2023-03-01 14:34:59 〽️ Prometheus exporter started at 127.0.0.1:9615
2023-03-01 14:34:59 Running JSON-RPC HTTP server: addr=127.0.0.1:9933, allowed origins=["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"]
2023-03-01 14:34:59 Running JSON-RPC WS server: addr=0.0.0.0:9945, allowed origins=["http://localhost:*", "http://127.0.0.1:*", "https://localhost:*", "https://127.0.0.1:*", "https://polkadot.js.org"]
2023-03-01 14:35:04 💤 Idle (0 peers), best: #0 (0x7633…9642), finalized #0 (0x7633…9642), ⬇ 0 ⬆ 0
2023-03-01 14:35:09 💤 Idle (0 peers), best: #0 (0x7633…9642), finalized #0 (0x7633…9642), ⬇ 0 ⬆ 0
请注意以下信息:
- 输出表明正在使用的链规范是使用
--chain命令行选项创建和指定的自定义链规范。 - 输出表明该节点是一个authority 节点,因为您使用
--validator命令行选项启动了该节点。 - 输出显示了用块哈希
(state: 0x972a…636b, header-hash: 0x7633…9642)初始化的创世块 - 输出为您的节点指定Local节点标识。在本例中,节点标识为
12D3KooWAecWYYGnLz6iUsQgU1kmTcVPyNunbwQaV14oeaW31jmB。 - 说明节点使用的IP地址为本地主机
127.0.0.1。
这些值是针对这个特定的教程示例的。输出中的值将特定于您的节点,您必须将节点的值提供给其他网络参与者以连接到引导节点。
现在您已经成功地使用自己的 key 启动了验证器节点,并记录了节点标识,可以继续下一步。但是,在将密钥添加到密钥存储库之前,请通过按Control-c停止节点。
4.8 向密钥存储库添加密钥
在启动第一个节点之后,还没有生成任何块。下一步是为网络中的每个节点向密钥存储库添加两种类型的密钥。
对于每个节点:
- 添加
aura授权密钥以启用块生产。 - 添加
grandpa授权密钥以启用块结束。
有几种方法可以将密钥插入密钥存储库。对于本教程,您可以使用key子命令插入本地生成的密钥。
- 执行如下命令插入
key子命令生成的aurasecret key:
./target/release/node-template key insert --base-path /tmp/node01 \--chain customSpecRaw.json \--scheme Sr25519 \--suri <your-secret-seed> \--password-interactive \--key-type aura
将<your-secret-seed>替换为您在“使用节点模板生成本地密钥”中生成的第一个密钥对的秘密短语或秘密种子。
在本教程中,秘密短语是reduce nerve siren ramp pride wrist main cry dolphin immune car group,因此--suri命令行选项指定将密钥插入密钥库的字符串。
--suri "reduce nerve siren ramp pride wrist main cry dolphin immune car group"
- 还可以从指定的文件位置插入密钥。查看可用的命令行选项信息,执行以下命令:
./target/release/node-template key insert --help
- 输入用于生成密钥的密码。
- 执行如下命令插入key子命令生成的
grandpa密钥:
./target/release/node-template key insert \--base-path /tmp/node01 \--chain customSpecRaw.json \--scheme Ed25519 \--suri <your-secret-key> \--password-interactive \--key-type gran
<your-secret-seed>同上
- 通过运行以下命令,验证您的密钥位于
node01的密钥存储库中:
ls /tmp/node01/chains/local_testnet/keystore

4.9 允许其他与会者加入
现在可以使用--bootnodes和-validator命令行选项允许其他验证器加入网络。
向私有网络添加第二个验证器:
- 以如下命令启动第二个区块链节点:
./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/12D3KooWAecWYYGnLz6iUsQgU1kmTcVPyNunbwQaV14oeaW31jmB\--password-interactive
该命令使用base-path、name和validator命令行选项将该节点标识为私有网络的第二个验证器。--chain命令行选项指定要使用的链规范文件。该文件对于网络中的所有验证器必须相同。确保为--bootnodes命令行选项设置了正确的信息。特别是,确保您已经从网络中的第一个节点指定了本地节点标识符。如果你没有设置正确的引导节点标识符,你会看到这样的错误:
The bootnode you want to connect to at ... provided a different peer ID than the one you expect: ...
- 执行如下命令添加
key子命令生成的aurasecret key:
./target/release/node-template key insert --base-path /tmp/node02 \--chain customSpecRaw.json \--scheme Sr25519 \--suri <second-participant-secret-seed> \--password-interactive \--key-type gran
将<second-participant-secret-seed>替换为您在生成第二个密钥对中生成的短语或种子。aura密钥类型是启用块生产所必需的。
- 运行如下命令,将key子命令生成的爷爷密钥添加到本地密钥存储库中:
./target/release/node-template key insert --base-path /tmp/node02 \--chain customSpecRaw.json \--scheme Ed25519 --suri <second-participant-secret-seed> \--password-interactive \--key-type gran
<second-participant-secret-seed> 同上
块结束(Block finalization )需要至少三分之二的验证器将它们的密钥添加到各自的密钥存储库中。因为这个网络在链规范中配置了两个验证器,所以区块终结只能在第二个节点添加了它的密钥之后开始。
- 通过运行以下命令,验证您的密钥位于
node02的密钥存储库中:
ls /tmp/node02/chains/local_testnet/keystore

Substrate 节点在插入grandpa key 后需要重新启动,因此必须在看到块最终完成之前关闭并重新启动节点。
在两个节点将它们的密钥添加到各自的密钥存储库(位于/tmp/node01和/tmp/node02下)并重新启动之后,您应该会看到相同的起源块和状态根散列。
您还应该看到每个节点都有一个对等体(1 peers),并且它们已经生成了一个块提议(best: #2 (0xe111…c084))。几秒钟后,您应该看到两个节点上都完成了新的块。
Accounts, addresses, and keys
Chain specification
subkey
Cryptography
相关文章:
Substrate 基础教程(Tutorials) -- 模拟网络 添加可信节点
三、模拟网络 本教程基本介绍了如何使用一个私有验证器(validators)的授权集合来启动私有区块链网络。 Substrate节点模板使用授权共识模型(authority consensus model),该模型将块生产限制为授权帐户的旋转列表(rotating list)。授权帐户(…...
SAP 设置无物料号的费用采购
现在还是以外购电来说一下ERP中费用采购单的使用步骤: (1).Tcode:OMSF定义物料组D1,如下图。 (2).到配置路径IMG Path:物料管理->采购->帐户分配(或直接SE16:V_T163K)定义一科目分配类别,默认的K就是费用采购科目分配类型,如果可能可以复制一个,如下图,注意下…...
k8s ConfigMap 中 subPath 字段和 items 字段
Kubernetes中什么是subPath 有时,在单个 Pod 中共享卷以供多方使用是很有用的。volumeMounts.subPath 属性可用于指定所引用的卷内的子路径,而不是其根路径。 这句话理解了,基本就懂subPath怎么用了,比如我们要替换nginx.cnf, 挂…...
UML建模
主要记录UML中的相关知识,包括类、对象、接口、方法、用例、活动、状态、组件和部署图,详细介绍类之间关系与类图的绘制 文章目录一、UML介绍二、类图类之间的关系依赖关系继承关系实现关系关联关系组合关系聚合关系正文内容: 一、UML介绍 …...
JavaScript常见面试题(更新中)
介绍js的基本数据类型 js一共有五种数据类型 分别是undefined null boolean number string 还有ES6中新增的symbol和ES10的bigInt symbol代表创建后独一无二的不可变的数据类型,他的出现我认为是为了解决可能出现的全局变量冲突的问题 BigInt是一种数字类型的数据 …...
TCP/IP协议
✏️作者:银河罐头 📋系列专栏:JavaEE 🌲“种一棵树最好的时间是十年前,其次是现在” 目录TCP/IP协议应用层协议自定义应用层协议DNS传输层协议端口号UDP协议UDP协议端格式TCP协议TCP协议段格式TCP工作机制确认应答(安…...
Python使用异步线程池实现异步TCP服务器交互
背景: 实现客户端与服务端交互,由于效率原因,要发送与接收异步,提高效率。 需要多线程,本文用线程池管理。 common代码: import pickle import struct import timedef send_msg(conn, data):time.sleep(…...
matplotlib常用操作
文章目录1 matplotlib绘图1.1 绘图步骤2 matplotlib基本元素2.1 matplotlib 画布2.2 设置坐标轴长度和范围2.3 设置图形的线型和颜色2.4 设置图形刻度范围、刻度标签和坐标轴标签等2.4.1 设置刻度范围2.4.2 设置坐标轴刻度2.5 文本标签图例3 matplotlib的ax对象绘图4 绘制子图5…...
二分算法题
文章目录一、在排序数组中查找数字二、0~n-1中缺失的数字三、旋转数组的最小数字四、二维数组中的查找一、在排序数组中查找数字 题目传送门 法一:暴力解 直接遍历然后计数 法二:二分法求边界 看到关键字排序数组、有序数组,一定要想到二分…...
Vue+ElementUI+SpringBoot项目配合分页插件快速实现分页(简单暴力)
首先需要在项目中引入Element-UI的组件库,使用以下命令,不会引入的请自行百度。 npm i element-ui -S Element官网地址:https://element.eleme.cn/#/zh-CN/component/changelog 去Element-UI官网组件库找到合适的分页插件,并把他引…...
【回眸】牛客网刷刷刷!嵌入式软件中也会遇到的嵌入式硬件,通讯,通讯协议专题(一)
前言 最近继续刷题,看看嵌入式软件还需要了解一些嵌入式硬件中的通讯协议和常用接口协议 比如说SPI CAN I2C 通讯协议专题 1.波特率 波特率 每秒传送的字符数 * 字符位数。串口的工作模式为1个起始位,7个数据位,1个校验位,1个…...
使用Vue展示数据(动态查询)
学习内容来源:视频P4 本篇文章进度接着之前的文章进行续写 精简前后端分离项目搭建 Vue基础容器使用 目录选择组件修改表格组件修改分页组件增加后端接口前端请求数据接口页面初始化请求数据点击页码请求数据选择组件 在官方文档中选择现成的组件,放在页…...
构建数据库测试数据——mysql
建表脚本 -- 建表 CREATE TABLE test_table (id INT(11) NOT NULL AUTO_INCREMENT,varchar_col VARCHAR(50),char_col CHAR(10),text_col TEXT,tinyint_col TINYINT(4),smallint_col SMALLINT(6),mediumint_col MEDIUMINT(9),int_col INT(11),bigint_col BIGINT(20),float_col…...
你想要的Android性能优化系列:启动优化 !
App启动优化为什么要做App的启动优化?网页端存在的一个定律叫8秒定律:即指用户访问一个网站时,如果等待打开的时间超过8秒,超过70%的用户将会放弃等待。同样的,移动端也有一个8秒定律:如果一个App的启动时间…...
python3的基础入门3:基本数据类型
基本数据类型 python 中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。 在 Python 中,变量就是变量,它没有类型,我们所说的"类型"是变量所指的内存中对象的类型。 等号(&…...
消息队列原理与实战-学习笔记
消息队列:保存消息的一个容器,本质是个队列,但是需要支持高吞吐、高并发、高可用。 1 前世今生 1.1 业界消息队列对比 Kafka:分布式的、分区的、多副本的日志提交服务,在高吞吐场景下发挥较为出色RocketMQ:低延迟、强一致、高性…...
Linux权限相关知识(大量图文展示,及详细操作)
Linux权限相关概念 Linux下有两种用户:一种是超级用户(root)、一种是普通用户。 超级用户:可以在linux系统下做任何事情,不受限制 普通用户:在linux下做有限的事情。 超级用户的命令提示符是“#”…...
Ep_操作系统面试题-什么是协程
协程 是一种 比线程更加轻量级的存 在,一个线程可以拥有多个协程。是一个特殊的 函数 ,这个函数可以在某个地方挂起,并且可以重新在挂起处外继续运行。协程 不是被操作系统内核所管理 , 而完全是由程序所控制(也就是在…...
在C#中使用互斥量解决多线程访问共享资源的冲突问题
在阿里云上对互斥量的概述:互斥量的获取是完全互斥的,即同一时刻,互斥量只能被一个任务获取。而信号量按照起始的计数值的配置,可以存在多个任务获取同一信号量的情况,直到计数值减为0,则后续任务无法再获取…...
JavaEE进阶第六课:SpringBoot配置文件
上篇文章介绍了SpringBoot的创建和使用,这篇文章我们将会介绍SpringBoot配置文件 目录1.配置文件的作用2.配置文件的格式2.1 .properties语法2.1.1.properties的缺点2.2 .yml语法2.2.1优点分析2.2.2配置与读取对象2.2.3配置与读取集合2.2.4补充说明3.设置不同环境的…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...
