Sub Dev 分享 | Substrate Based区块链上线实战经验
一块链习是首家区块链技术学习社区,提供最系统的区块链技术课程学习,定期出品有深度的技术观察 + 评论。
.01
多节点配置
线上环境的多节点配置上是大家非常关心的内容,一般开发的时候,根据教程我们会使用--dev来运行一条开发环境的区块链,或者根据--chain local来运行一条本地测试网络。
配置 Chain spec
这两条都是内置的测试网络,实际是在chain spec中配置的。需要注意的是,这两条都是仅能用于测试的网络,到生产环境的话,需要增加自己的网络。这就需要修改对应的chain spec的代码了。
chain spec是Substrate的区块链初始配置,类似于其他区块链的genesis。具体的实现一般放在chain_spec.rs中,具体的位置根据节点的目录结构而有所不同,在node template下是在node/src/chain_spec.rs中。
最核心的原因,是因为dev 和 local testnet 使用的出块密钥都是well-known keys,也就是一些约定俗成的密钥,像是Alice,Bob。而在生产环境,我们需要添加网络将其修改为我们自己私底下生成的节点的密钥,并且只将公钥放到上面。
观察chain_spec.rs文件,我们可以找到 dev 网络的出块节点配置,一般来说是像下面这个样子
testnet_genesis(
// initial_authorities
vec![
authority_keys_from_seed("Alice"),
],
// root_key
get_account_id_from_seed::<sr25519::Public>("Alice"),
// endowed_accounts
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
true,
)
其中,testnet_genesis的第一个参数就是初始的出块节点,第二个参数是root key,第三个参数是初始的活跃账户,第四个参数这里不需要管。
其中有两个方法值得注意,第一个是其中authority_keys_from_seed,第二个是get_account_id_from_seed,这两个函数都是从seed生成对应所需要的公钥的函数。其中authority_keys_from_seed稍微复杂一点,由多组密钥组成。
修改过后的配置文件,应该是类似于下面这个样子。
beta_genesis(
// initial_authorities
vec![(
// 5FBod7BC86ahPWt6U1sxqEozt1MF8m48soYEJkvWTLkAQBje
hex!["8a1ed431fa78b83f195e228c47777cc4661916fd8b1571ac4e9801ae56560952"].unchecked_into(),
// 5FR2ttJ17Nn7KivDHMJsU7fadgSmCHgHvnZ3F9hqv3K6eygH
hex!["9435b260b98343ac8f42cdb0047e54d6c7229b3d6d24667bdf48a9a4e2f83aae"].unchecked_into(),
), (
// 5FEyMs3XyiUq87iC7RVB1gUX1vBhpL2n6UdaCsm6RyuJsVBn
hex!["8c8957e6c33e1faef273f27283c0d2567f776d1fa487f8c310e7d88305f03b27"].unchecked_into(),
// 5DDw6Z8ZgFWYU2rpVqkSLjP8GPRR7K8vSqXWKohMF7EyKLoa
hex!["3346028b09c81cadd77853fc363f23a9dcdfe20132229a3298073f7c5c3fa45b"].unchecked_into(),
)],
// root_key
hex!["20cd1afa4f95b59b7f61a97360e8bc74a26a6fc13712e6f2eef3a1e020bbcd68"].into(), // 5CoiKRg4hQopwaHxvjdk7C2Gq1pbdvJhsLiYrVHAcsHNkV8m
// endowed_accounts
vec![
// 5Ca8HLb1EkJgsDMSpifh33rprTCn9m1DpRkjXNTe7czU1UA5
hex!["167056fa07ae7bc1d36da5520dd8c4c06cb5a5db557986f0c6ae2af0030d4c44"].into(),
// 5G9xD8Bn32KALTZWafYP97e9TwnnzqacwUgSNdLB8mvbvRt1
hex!["b4f179ee3e5e2498eb6d59d1d899bcb0157a917f9cfd8523101e4f78c8a52050"].into()
],
true,
)
请注意,这里将authority_keys_from_seed变成了由两组密钥组成的元组,原因是因为本身authority_keys_from_seed就是根据session keys的格式组装出来的,有可能由2个key组成,也有可能由其他数量的key组成,具体需要参照runtime中session keys的定义。
在这里,session keys是由aura和grandpa的密钥组成,分别由sr25519 和 ed25519进行加密。因此在上面的一个元组中,第一个元素为AuraId的公钥,第二个元素为grandpa的公钥。
一般我们可以通过subkey或者polkadot.js或者更安全的工具来生成Substrate相关的密钥,根据需求需要提前生成sr25519或者ed25519的密钥。
在上述代码中,还使用了hex!这个宏将相应代码转化为十六进制的格式,因此我们需要注意去掉所生成密钥的公钥开头的0x。hex!引用自hex_literal,所以还需要在文件最上方加上
use hex_literal::hex;
并且添加相关的Cargo依赖。这样,我们就为一个网络添加了指定的出块节点的密钥。
接下来要做的一件事情,就是将节点密钥与运行中的节点进行绑定了,这样链上才知道当前正在运行的节点,是什么样的身份。而在做到这一点之前,我们需要了解一个概念- Session Keys
Seesion Keys
简单来说,Session Keys 用于验证节点签名共识相关的消息。Session Keys 由多个Key组成,具体定义在runtime中可以看到。不同类型的Key可能由不同的算法实现,比如sr25519 或 ed25519。为了安全,Session Keys记得进行定时更换。
一般生成Session Keys有两种方式:
从节点RPC接口去插入对应的keys
从节点RPC接口生成Session Keys
前者的做法,适用于需要指定相关的密钥的区块,一般是搭建初始的出块节点或提前生成好相关的密钥的区块。后者的情况,则更适用与转入POS后搭建新的节点的区块,因为还需要额外发起一个链上交易,来绑定节点的密钥。
前者的实现主要是调用RPC接口中的author_rotateKeys,其可参考的cURL代码如下:
curl -H 'Content-Type: application/json' --data '{ "jsonrpc":"2.0", "method":"author_rotateKeys", "id":1 }' http://localhost:9933
调用完成后,系统会返回一串json,其中包含的就是Session Keys。
前者的实现主要是调用RPC接口中的author_insertKey的方法,,后者的可参考cURL实现如下:
curl http://localhost:9933 -H "Content-Type:application/json;charset=utf-8" -d \
'{
"jsonrpc":"2.0",
"id":1, "method":"author_insertKey", "params": [
"<aura/gran>",
"<mnemonic phrase>",
"<public key>"
] }'
在做完该项绑定后,应该正常区块下会开始出块。
Node key
大家运行node-template --help的话,一般会出现一条关于node-key的信息。libp2p是由ipfs主导开发的一个p2p的网络模块,这里的Node key,主要是p2plib在直接使用。
一般需要注意的是,种子节点需要指定固定的Node key,以防止identity发生变化,导致外部无法连接。
.02
出块节点和数据同步节点
这里给出一个参照的运行命令。需要注意的主要有,--pool-limit是交易池的大小,可以适当调整大。--ws-max-connections是提升websocket的可连接数。
出块节点的参考命令
node-template —-chain local --node-key b6800b71239ae4a7b1792f3f19239eb65229b6277d2453a2890639cc91e499f2 --name substrate-n1
--base-path /home/node/data/
--pruning=archive --validator
--telemetry-url ws://172.31.200.11:8000/submit
--pool-limit 10000
—execution=NativeElseWasm
数据同步节点的参照命令
node-template —-chain local
--name substrate-data-node-0
--port 30333
--node-key 7cec6023c2e8e7d70354d413b4361634dbb87eecec0b05bd114a6bdc669c23bd --base-path /home/node/data/
--rpc-external
--ws-external
--rpc-cors=all
--pruning=archive
--bootnodes /ip4/172.31.200.5/tcp/30333/p2p/QmYgcaAUKXAnX9CpyEDWaVbjWmhZVwzTCA7D22fNb6aV8t -- telemetry-url ws://172.31.200.11:8000/submit
--ws-max-connections 2048 --pool-limit 10000
使用pm2来运行节点
pm2 --name n1 start -x ‘./bin/node-template' -- --chain local --node-key b6800b71239ae4a7b1792f3f19239eb65229b6277d2453a2890639cc91e499f2 --name substrate-n1
--base-path /home/node/data/
--pruning=archive --validator
--telemetry-url ws://172.31.200.11:8000/submit --pool-limit 10000
—execution=NativeElseWasm
使用systemd来运行节点
可参照官网的教程
https://wiki.polkadot.network/docs/en/maintain-guides-how-to-systemd
负载优化
RPC比WebSocket更方便实际的缓存与优化,在海量用户访问中尤其明显。但在Polkadot Js中,对RPC的支持不够完善,因此我们想了一个相对hack的方式,自己组装Storage Key来调用相关的接口。具体代码请参照
https://github.com/RioDefi/rpc_example/blob/master/index.js
.03
链上升级
在这里,为大家提供一个可参考的链上升级流程。
1、请先确保在staging环境测试升级
2、修改runtime module业务代码
3、修改runtime/lib.rs中的version,这个主要是一个参考的作用,
4、执行cargo build --release
5、输出的wasm默认在:
./target/release/wbuild/node-template-runtime/node- template_runtime.compact.wasm
6、打开polkadot ui中通过sudo,打开system模块的setCode方法,上传刚才编译的wasm文件,提交交易 交易打包执行成功后,应该可以观察到version已经更新到相应的版本。
需要注意的是,链上升级时注意不要修改到Chain Spec,否则可能会影响genesis hash,造成节点间无法互联。
但我觉得区块链运维,最终还是要对整体区块链有足够了解,才能在关键时候作出正确的选择。
这么干货的分享,这是门什么样的课?
体系完善的技术开发课——在2个月的时间内,致力于通过每周1/2次的线上课程+高强度的课后代码作业任务,帮助课程中的每一位成员,实现具备入门Substrate开发的能力。
有含金量的烧脑课——虽然这是入门课,四位老师在课程中也尽量用最直白的语言讲解,并多穿插案例,但是如果想要完全理解,需要花工夫多思考和补充学习。
循序渐进的视频课 ——我们将课程按知识模块安排成12节内容,使用视频的形式,方便同学们利用碎片化时间学习;每周更新1/2节,保证之前听的能够有时间消化,循序渐进地学习。
欢迎扫码了解更多和课程报名!
扫码关注公众号,回复“1”加入开发者社群