如何用 Substrate 创建一条私有链
生成自己的密钥
现在,我们已经了解了基础知识和命令行选项,可以着手生成属于自己的密钥,无需再使用众所周知的 Alice 和 Bob 密钥了。每个想要参与区块链的人都应该生成自己的密钥。这一部分会说明用于生成密钥的几种方式,其中有三个选项,每个参与者只需要选择一个这样的选项。无论选择哪个选项,请确保记录本节中的所有输出,以备日后使用。
选项 1: Subkey
Subkey 是生成密钥的工具,一般会与 Substrate 一起使用。
首先编译并安装该实用程序,这可能需要15分钟左右的时间。
cargo install --force subkey --git https://github.com/paritytech/substrate --tag v2.0.0-rc3
我们将需要为每种类型至少生成 2 个密钥。每个节点都需要有自己的密钥。
生成一个助记词,并查看和 sr25519 关联的密钥和地址,Aura 将使用该密钥进行区块的生产。
$ subkey --sr25519 generateSecret phrase `infant salmon buzz patrol maple subject turtle cute legend song vital leisure` is account: Network ID/version: substrate Secret seed: 0xa2b0200f9666b743402289ca4f7e79c9a4a52ce129365578521b0b75396bd242 Public key (hex): 0x0a11c9bcc81f8bd314e80bc51cbfacf30eaeb57e863196a79cccdc8bf4750d21 Account ID: 0x0a11c9bcc81f8bd314e80bc51cbfacf30eaeb57e863196a79cccdc8bf4750d21 SS58 Address: 5CHucvTwrPg8L2tjneVoemApqXcUaEdUDsCEPyE7aDwrtR8D
现在查看与相同助记符关联的 ed25519 密钥和地址。GRANDPA 将使用此密钥进行区块的终结。
$ subkey --ed25519 inspect "infant salmon buzz patrol maple subject turtle cute legend song vital leisure"Secret phrase `infant salmon buzz patrol maple subject turtle cute legend song vital leisure` is account: Network ID/version: substrate Secret seed: 0xa2b0200f9666b743402289ca4f7e79c9a4a52ce129365578521b0b75396bd242 Public key (hex): 0x1a0e2bf1e0195a1f5396c5fd209a620a48fe90f6f336d89c89405a0183a857a3 Account ID: 0x1a0e2bf1e0195a1f5396c5fd209a620a48fe90f6f336d89c89405a0183a857a3 SS58 Address: 5CesK3uTmn4NGfD3oyGBd1jrp4EfRyYdtqL3ERe9SXv8jUHb
选项 2: Polkadot-JS Apps
我们用来查看正在生成的块的相同 UI 也可以用于生成密钥。如果您不想安装 subkey,那么此选项很方便。它可以用于生产密钥,但是在生成此类密钥时不应将系统连接到 Internet 中。
“
无论选择哪种方法,都不能将生成生产密钥的系统连接到 Internet。这里特别提到它是因为,使用诸如 Polkadot JS Apps 之类的 Web 应用程序时通常需要具有 Internet 连接。
在 "Accounts" 标签上,单击 "Add account"。您无需提供名称,尽管您可能希望在验证之外保存该帐户以用于提交交易。
生成一个 sr25519密钥,Aura将使用该密钥进行区块生产。请留意并记录助记词以及通过单击左上角的 identicon 复制 SS58 地址。
生成了一个 ed25519 密钥后, GRANDPA 将使用该密钥进行区块确认。再次提醒记录好助记词和 SS58 地址。
选项 3: 使用预生成的密钥
如果您想先进行本教程的学习,则可以使用以下预生成的密钥对之一。但是要意识到,这些密钥绝对不应该在实际生产区块中使用,而仅出于学习目的而提供。
Pair 1
Key(密钥)Value(值)Secret phrase(助记词)clip organ olive upper oak void inject side suit toilet stick narrowSecret seed0x4bd2b2c1dad3dbe3fa37dc6ad5a4e32ddd8ad84b938179ac905b0622880e86e7SR25519Public key(公钥)0x9effc1668ca381c242885516ec9fa2b19c67b6684c02a8a3237b6862e5c8cd7eSS58 Address5FfBQ3kwXrbdyoqLPvcXRp7ikWydXawpNs2Ceu3WwFdhZ8W4ED25519Public key(公钥)0xb48004c6e1625282313b07d1c9950935e86894a2e4f21fb1ffee9854d180c781SS58 Address5G9NWJ5P9uk7am24yCKeLZJqXWW6hjuMyRJDmw4ofqxG8Js2
Pair 2
Key(密钥)Value(值)Secret phrase(助记词)paper next author index wedding frost voice mention fetch waste march tiltSecret seed0x4846fedafeed0cf307da3e2b5dfa61415009b239119242006fc8c0972dde64b0SR25519Public key(公钥)0x74cca68a32156615a5923c67024db70da5e7ed36e70c8cd5bcf3556df152bb6dSS58 Address5EhrCtDaQRYjVbLi7BafbGpFqcMhjZJdu8eW8gy6VRXh6HDpED25519Public key(公钥)0x0fe9065f6450c5501df3efa6b13958949cb4b81a2147d68c14ad25366be1ccb4SS58 Address5CRZoFgJs4zLzCCAGoCUUs2MRmuD5BKAh17pWtb62LMoCi9h
创建自定义链规范
既然每个参与者都有自己的密钥对,那么您就可以开始创建自定义链规范了。我们将使用此自定义链规范替代之前使用的内置 local 规范。
在此示例中,我们将创建一个网络,里面包含两个节点,但该过程可以直接推广到更多节点。
创建链规范
上一节内容中,我们使用了 --chain local ,它是预定义的“链规范”,其中将 Alice 和 Bob 指定为验证人以及其他一些有用的默认值。
与其完全从头开始编写链式规范,不如对我们以前使用的规范进行一些修改。首先,我们需要将链规范导出到名为 customSpec.json 的文件中。请记住,可以通过运行 node-template --help 获得有关所有这些命令的更多详细信息。
# Export the local chainspec to json$ ./target/release/node-template build-spec --disable-default-bootnode --chain local > customSpec.json2020-05-28 13:29:05 Building chain spec
我们刚刚创建的文件包含几个字段,您可以通过探索它们学习很多东西。到目前为止,最大的字段是单个二进制blob,它是运行时的Wasm二进制文件。这是您先前运行cargo build命令时所构建内容的一部分。
我们感兴趣的文件部分是用于创建块的 Aura 授权,由下面的**"aura"** 字段指示;用于确定块的 GRANDPA 授权,由 **"grandpa"**字段指示。该部分如下:
{ //-- snip -- "genesis": { "runtime": { "system": { "changesTrieConfig": null //-- snip -- }, "aura": { "authorities": [ "5FfBQ3kwXrbdyoqLPvcXRp7ikWydXawpNs2Ceu3WwFdhZ8W4", "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" ] }, "grandpa": { "authorities": [ ["5G9NWJ5P9uk7am24yCKeLZJqXWW6hjuMyRJDmw4ofqxG8Js2", 1], ["5GoNkf6WdbxCFnPdAnYYQyCjAKPJgLNxXwPjwTh6DGg6gN3E", 1] ] } //-- snip -- } }}“
这些指令是为节点模板编写的。他们将与其他基于 Substrate 节点一起工作,而无需进行任何修改。在包括 session pallet 的 Substrate 节点中。您应该将 Aura 和 Grandpa 配置保留为空,而应将此信息插入会话配置中。从所需节点导出的链规范中已说明了所有这些。
由于 Grandpa 协议支持加权投票,因此 Grandpa 数据的格式更加复杂。在这种情况下,我们为每个验证人赋予了1权重。
我们要做的就是将列出的授权地址(当前为 Alice 和 Bob)更改为在上一步中生成的我们自己的地址。sr25519 地址位于 aura 部分,而 ed25519 地址位于 Grandpa 部分。您可以根据需要添加任意数量的验证人。有关其他上下文,请阅读有关 Substrate中的密钥 [1]的信息
“
验证人不应共享相同的密钥,即使出于学习目的也是如此。如果两个验证人使用相同的密钥,则它们将在运行过程中产生冲突的块。
准备好链规范后,将其转换为“初始(raw)”链规格。初始链规范包含所有相同的信息,但它包含已编码的存储密钥,该节点将使用该存储密钥来引用其本地存储中的数据。分发初始规范可确保每个节点将数据存储在相应的存储密钥上。
$ ./target/release/node-template build-spec --chain=customSpec.json --raw --disable-default-bootnode > customSpecRaw.json2020-05-28 13:31:37 Building chain specCopy
最后,与网络中的所有其他验证人共享 customSpecRaw.json 文件。
“
每个人都应该创建链规范,并与其他验证人共享生成的 customSpecRaw.json 文件。
因为Rust-> Wasm 优化的构建不是“可复制的”,所以每个人都会得到一个略有不同的Wasm blob,如果每个参与者自己生成文件而不共享,将破坏共识。
创建您的专用网络
创建自定义链规范并共享给所有参与者后,您就可以启动自己的自定义链了。在本节中,不再需要使用单个物理计算机或单个二进制文件。
第一个参与者启动一个 Bootnode
您已经完成了所有必要的准备工作,现在可以启动链了。此过程与您早先与(Alice)和(Bob)建立链接的过程非常相似。从无记录基本路径开始很重要,因此,如果您打算使用以前使用的相同路径,请从该目录中删除所有内容。
第一个参与者可以使用以下命令启动其节点:
./target/release/node-template \ --base-path /tmp/node01 \ --chain=./customSpecRaw.json \ --port 30333 \ --ws-port 9944 \ --rpc-port 9933 \ --telemetry-url 'ws://telemetry.polkadot.io:1024 0' \ --validator \ --rpc-methods=Unsafe \ --name MyNode01
以下是一些和创建 Alice 有差异的内容:
我已经省略了 --alice 标记。取而代之的是,我们很快将通过RPC将自己的自定义密钥插入密钥库。
--chain 标记已更改为使用我们的自定义链规范。
我添加了可选的 --name 标志。可以使用它在 telemetry UI 中为节点提供易于理解的名称。
添加了可选的 --rpc-methods=Unsafe 标记, 顾名思义,在生产环境中使用此标志并不安全,但可以使本教程重点关注当前的主题。在生产中,您应该使用 JSON-RPC proxy[2] 。
您应该能看到控制台输出如下:
2020-06-10 13:25:16 Substrate Node2020-06-10 13:25:16 ✌️ version 2.0.0-rc3-f5acce1-x86_64-linux-gnu2020-06-10 13:25:16 ❤️ by Substrate DevHub
, 2017-20202020-06-10 13:25:16 ? Chain specification: Local Testnet2020-06-10 13:25:16 ? Node name: MyNode012020-06-10 13:25:16 ? Role: AUTHORITY2020-06-10 13:25:16 ? Database: RocksDb at /tmp/node01/chains/local_testnet/db2020-06-10 13:25:16 ⛓ Native runtime: node-template-1 (node-template-1.tx1.au1)2020-06-10 13:25:16 ? Initializing Genesis block/state (state: 0xecc5…18c6, header-hash: 0x1af8…2b26)2020-06-10 13:25:16 ? Loading GRANDPA authority set from genesis on what appears to be first startup.2020-06-10 13:25:16 ⏱ Loaded block-time = 6000 milliseconds from genesis on first-launch2020-06-10 13:25:16 ? Highest known block at #02020-06-10 13:25:16 Using default protocol ID "sup" because none is configured in the chain specs2020-06-10 13:25:16 ? Local node identity is: 12D3KooWKDc78RJSWnMAo4dkVdfyFqvrvEZZSVddP16fsHhSy6sw (legacy representation: QmPGVPfrg3gsUgoFgxpNNwjBputPKpukcWizQwedeBbKUr)2020-06-10 13:25:16 〽️ Prometheus server started at 127.0.0.1:96152020-06-10 13:25:21 ? Idle (0 peers), best: #0 (0x1af8…2b26), finalized #0 (0x1af8…2b26), ⬇ 0 ⬆ 02020-06-10 13:25:26 ? Idle (0 peers), best: #0 (0x1af8…2b26), finalized #0 (0x1af8…2b26), ⬇ 0 ⬆ 02020-06-10 13:25:31 ? Idle (0 peers), best: #0 (0x1af8…2b26), finalized #0 (0x1af8…2b26), ⬇ 0 ⬆ 0将密钥添加到密钥库
节点运行后,您可能会发现没有任何块产生。此时,您需要将密钥添加到密钥库中。请注意,您需要为网络中的每个节点完成这些步骤。
选项 1: 使用 Polkadot-JS Apps UI
您可以使用 Apps UI 将密钥插入密钥库。导航到 “工具箱” 选项卡和 “ RPC调用” 子选项卡。选择“author”和“ insertKey”。可以这样填写字段:
keytype: aurasuri:(eg. clip organ olive upper oak void inject side suit toilet stick narrow)publicKey:(eg. 0x9effc1668ca381c242885516ec9fa2b19c67b6684c02a8a3237b6862e5c8cd7e)“如果您是通过 Apps UI 生成密钥的,则您将不知道原始的公钥。在这种情况下,您可以改用您的SS58地址 (5FfBQ3kwXrbdyoqLPvcXRp7ikWydXawpNs2Ceu3WwFdhZ8W4) 。
你已经成功插入 aura 密钥,可以通过重复这个步骤插入 grandpa 密钥 ( ed25519 密钥)
keytype: gransuri:(eg. clip organ olive upper oak void inject side suit toilet stick narrow)publicKey:(eg. 0xb48004c6e1625282313b07d1c9950935e86894a2e4f21fb1ffee9854d180c781)“如果您是通过 Apps UI 生成密钥的,则您将不知道原始的公钥。在这种情况下,您可以改用您的SS58地址 (5G9NWJ5P9uk7am24yCKeLZJqXWW6hjuMyRJDmw4ofqxG8Js2) 。
“如果要对网络中的 第二 节点执行以下步骤,则必须在插入密钥之前将 UI 连接到第二个节点。
选项 2: 使用 curl
您还可以通过在命令行中使用 `curl`[3] 将密钥插入密钥库。在创建环境中,这种方法可能更可取。您可能正在使用基于云的虚拟专用服务器的位置。
因为安全创建环境中最重要的问题,所以采取一切可能的预防措施很重要。在这种情况下,这意味着要注意不要遗留任何私钥痕迹,例如终端的历史记录。创建一个文件,用于定义“ curl”请求的主体:
{ "jsonrpc":"2.0", "id":1, "method":"author_insertKey", "params": [ "", "", "" ]}# Submit a new key via RPC, connect to where your `rpc-port` is listening$ curl http://localhost:9933 -H "Content-Type:application/json;charset=utf-8" -d "@/path/to/file"如果正确输入命令和参数,则节点将返回 JSON 响应,如下:
{ "jsonrpc": "2.0", "result": null, "id": 1 }完成后,请确保删除包含密钥的文件。
后续参与者的加入
随后的验证人现在可以加入网络。可以参照 Bob 之前的操作指定--bootnodes参数来完成。
./target/release/node-template \ --base-path /tmp/node02 \ --chain ./customSpecRaw.json \ --port 30334 \ --ws-port 9945 \ --rpc-port 9934 \ --telemetry-url 'ws://telemetry.polkadot.io:1024 0' \ --validator \ --rpc-methods=Unsafe \ --name MyNode02 \ --bootnodes /ip4//tcp//p2p/类似之前的操作,我们指定另一个base-path,给它另一个name,并且还将这个节点指定为validator。一旦第二个节点启动,您应该看到它们的创建:
2020-06-10 13:27:45 Substrate Node2020-06-10 13:27:45 ✌️ version 2.0.0-rc3-f5acce1-x86_64-linux-gnu2020-06-10 13:27:45 ❤️ by Substrate DevHub, 2017-20202020-06-10 13:27:45 ? Chain specification: Local Testnet2020-06-10 13:27:45 ? Node name: MyNode022020-06-10 13:27:45 ? Role: AUTHORITY2020-06-10 13:27:45 ? Database: RocksDb at /tmp/node02/chains/local_testnet/db2020-06-10 13:27:45 ⛓ Native runtime: node-template-1 (node-template-1.tx1.au1)2020-06-10 13:27:45 ? Initializing Genesis block/state (state: 0xecc5…18c6, header-hash: 0x1af8…2b26)2020-06-10 13:27:45 ? Loading GRANDPA authority set from genesis on what appears to be first startup.2020-06-10 13:27:45 ⏱ Loaded block-time = 6000 milliseconds from genesis on first-launch2020-06-10 13:27:45 ? Highest known block at #02020-06-10 13:27:45 Using default protocol ID "sup" because none is configured in the chain specs2020-06-10 13:27:45 ? Local node identity is: 12D3KooWPe2TanAA62uDo8wnBfBhKBewneUyc2j2j8KFkurrLxq7 (legacy representation: QmULySEqL3r4q7d5MDvHWX77YWQnRGQMyuNbiJrpoBeDQu)2020-06-10 13:27:46 ? Discovered new external address for our node: /ip4/192.168.0.120/tcp/30334/p2p/12D3KooWPe2TanAA62uDo8wnBfBhKBewneUyc2j2j8KFkurrLxq72020-06-10 13:27:50 ? Idle (0 peers), best: #0 (0x1af8…2b26), finalized #0 (0x1af8…2b26), ⬇ 0.6kiB/s ⬆ 0.7kiB/s2020-06-10 13:27:55 ? Idle (1 peers), best: #0 (0x1af8…2b26), finalized #0 (0x1af8…2b26), ⬇ 0.2kiB/s ⬆ 0.2kiB/s2020-06-10 13:28:00 ✨ Imported #1 (0x605b…a82b)2020-06-10 13:28:00 ? Idle (1 peers), best: #1 (0x605b…a82b), finalized #0 (0x1af8…2b26), ⬇ 0.2kiB/s ⬆ 0.2kiB/s最后几行显示您的节点已与另一个节点 (1 peers)建立了点对点连接,并且产生了一个块 (best: #1 (0x605b…a82b)).
现在,您可以参照第一个节点上的操作,按照以下过程(在上一节中的操作)将密钥添加到其密钥库中。
“如果要通过 UI 插入密钥,则必须在插入第二个节点的密钥之前将 UI 连接到第二个节点的 WebSocket 端点。
“提醒:所有验证人必须使用相同的链规范才能进行点对点连接。您应该看到相同的初始区块和状态根哈希。
您会注意到,即使在添加了第二个节点的密钥之后,也没有发生块最终确定(finalized #0 (0x1af8…2b26))。插入 Grandpa 密钥后,Substrate 节点需要重新启动。这时可以停止节点,然后使用以前使用的相同命令重新启动它们。现在应该就可以完成块了。
2020-06-10 13:30:08 Substrate Node2020-06-10 13:30:08 ✌️ version 2.0.0-rc3-f5acce1-x86_64-linux-gnu2020-06-10 13:30:08 ❤️ by Substrate DevHub, 2017-20202020-06-10 13:30:08 ? Chain specification: Local Testnet2020-06-10 13:30:08 ? Node name: MyNode022020-06-10 13:30:08 ? Role: AUTHORITY2020-06-10 13:30:08 ? Database: RocksDb at /tmp/node02/chains/local_testnet/db2020-06-10 13:30:08 ⛓ Native runtime: node-template-1 (node-template-1.tx1.au1)2020-06-10 13:30:08 ? Highest known block at #152020-06-10 13:30:08 Using default protocol ID "sup" because none is configured in the chain specs2020-06-10 13:30:08 ? Local node identity is: 12D3KooWPe2TanAA62uDo8wnBfBhKBewneUyc2j2j8KFkurrLxq7 (legacy representation: QmULySEqL3r4q7d5MDvHWX77YWQnRGQMyuNbiJrpoBeDQu)2020-06-10 13:30:08 ? Discovered new external address for our node: /ip4/127.0.0.1/tcp/30334/p2p/12D3KooWPe2TanAA62uDo8wnBfBhKBewneUyc2j2j8KFkurrLxq72020-06-10 13:30:12 ✨ Imported #16 (0x1da4…8673)2020-06-10 13:30:13 ? Idle (1 peers), best: #16 (0x1da4…8673), finalized #13 (0x69da…d2cc), ⬇ 1.9kiB/s ⬆ 2.0kiB/s成功启动
恭喜你!您已经启动了自己的区块链!
在本教程中,您学习了如何生成自己的密钥对,创建使用这些密钥对的自定义链规范,并根据您的自定义链规范启动专用网络。
微信扫描关注公众号,及时掌握新动向
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场