内容简介:通过本文,你会了解到,1\Substrate node-template的组成部分,及各部分的功能简介;2\参数如何配置本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
通过本文,你会了解到,1\Substrate node-template的组成部分,及各部分的功能简介;2\参数如何配置
通过本文,你会了解到,
- Substrate node-template的组成部分,及各部分的功能简介
- 参数如何配置
Substrate作为一个标准的区块链开发框架,不仅提供了必备的底层公共组件(如数据库、共识、P2P、交易池)和通用的runtime模块(如资产相关的balances,治理相关的democracy等),还提供了将各个功能组件连接起来的节点模板程序( node-template )和节点程序( node )。本文主要介绍node-template中各个代码块的功能。
文件目录
在使用类Unix操作系统的情况下,进入node-template文件目录,执行 tree -I target
命令,获取详细的文件信息如下:
这里我们忽略了target文件目录下的内容,来较少干扰性的输出。
workspace cargo.toml
node-template是一个标准的 Rust workspace 项目,当项目比较复杂时,使用workspace可以清晰地管理组件库(library)和可执行程序(binary)。在项目根目录的cargo.toml文件里有:
[workspace] members = [ 'node', 'pallets/template', 'runtime', ]
这个workspace的成员有node、pallets/template、runtime,其中node是可执行程序,在对应的src/main.rs文件内拥有一个可执行的main函数入口;pallets/template和runtime是组件库,在src/lib.rs定义了可被外部使用的函数和数据结构。
细心的同学会注意到cargo.toml里还有下面两行配置:
[profile.release] panic = 'unwind'
它和 catch_unwind
一起使用可以捕获某个线程内panic抛出的异常,常用的场景有:
- 在其它编程语言中嵌入Rust;
- 自定义线程处理的逻辑;
- 测试框架,因为测试用例可以panic,但是不能中断测试的运行。
具体请参考 Controlling panics with std::panic 。
workspace build.rs
自定义的构建脚本放置在项目的build.rs文件内,可以在编译构建项目之前,让Cargo去编译和执行该脚本,使用场景有:
- 编译、连接第三方的非Rust代码;
- 构建之前的代码生成功能。
node-template根目录下的build.rs的具体功能,参考下面的注释,
use vergen::{ConstantsFlags, generate_cargo_keys}; const ERROR_MSG: &str = "Failed to generate metadata files"; fn main() { // 使用vergen生成环境变量,供项目中的env!宏获取 // 这里设置了VERGEN_SHA_SHORT为Git最新的commit id的缩写 generate_cargo_keys(ConstantsFlags::SHA_SHORT).expect(ERROR_MSG); // 当.git/HEAD文件改变即切换Git分支时,重新执行这一脚本 build_script_utils::rerun_if_git_head_changed(); }
scripts/init.sh
初始化编译环境,包括升级Rust的版本,包括nightly和stable两个 发布渠道 :
rustup update nightly rustup update stable
并且添加构建WebAssembly的支持工具:
rustup target add wasm32-unknown-unknown --toolchain nightly
定期执行本脚本,可以解决一些常见的编译问题如某个依赖安装失败。
pallets
包含了自定义的runtime模块,默认只有一个template模块,以此模块为例:
cargo.toml 包含 :
-
package的基本信息如name,version,authors等。
-
package所依赖的第三方库,以
frame-support
为例,来源是Github上该代码仓库的某个commit id,并将default-features设置为false(即不使用默认的feature进行编译)。
[dependencies.frame-support] default-features = false git = 'https://github.com/paritytech/substrate.git' rev = '013c1ee167354a08283fb69915fda56a62fee943' version = '2.0.0-alpha.3'
- 通过 feature 进行条件编译,当使用Cargo进行构建时,下面的配置表示默认使用std feature,当编译依赖库如frame-support也默认使用std feature。这样的配置保证了runtime模块既可以编译为Native执行版本(使用std feature),也可以编译为Wasm执行版本(使用no_std feature,并由 WasmBuilder 进行编译,后面会详细介绍)。
[features] default = ['std'] std = [ 'codec/std', 'frame-support/std', 'safe-mix/std', 'system/std', ]
说明:Substrate为了保证应用的安全和稳定,对runtime有意地添加了一个约束,也就是在runtime代码里只能使用Rust的核心库及一些辅助库,而不能使用 标准库 。使用标准库会导致Wasm执行版本编译失败。
src/lib.rs是runtime模块的具体功能实现:
-
#![cfg_attr(not(feature = "std"), no_std)]
表示编译时如果feature不是std,那么必须是no_std。 -
mock和test模块只在运行测试时进行编译;
-
定义了模块的接口,继承自system模块的接口,并添加了一个关联类型Event,这个Event类型可以转换成system模块下的Event,也可以由当前的template模块定义的的Event转换而来;
pub trait Trait: system::Trait { type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>; }
- 使用decl_storage宏定义模块的存储单元;
- 使用decl_event宏定义模块可能触发的事件;
- 使用decl_error宏定义模块可以返回的错误种类;
- 最后通过decl_module宏定义了本模块的核心逻辑即可调用函数(Dispatchable Call),并初始化Error类型和Event的默认触发方式。
src/mock.rs是为测试用例服务的,
-
pub struct Test;
创建了一个测试用的runtime结构体; - 通过
impl_outer_origin
宏为runtime构造了一个Origin类型,用来标识交易的来源; - 通过
parameter_types
宏生成一些后面功能模块所需的满足Get
接口的数据类型; - 为runtime实现各个功能模块接口,这里使用了大量的
()
来mock不关心的数据类型;
impl Trait for Test { type Event = (); }
pub type TemplateModule = Module<Test>; new_test_ext
src/tests.rs包含了所有的测试用例,
- 引入mock数据和断言;
- 通过
#[test]
来标识测试函数; - 调用
new_test_ext
,并通过execute_with
执行closure内的代码; - 调用template模块的可调用函数,并返回执行结果
TemplateModule::do_something(Origin::signed(1)
。 -
assert_ok
断言结果是Ok,assert_eq
断言结果等于预期,assert_noop
断言结果为Error并且不修改链上存储状态。
runtime
cargo.toml除了上面提到的某个pallet对应的cargo.toml里的类似内容外,还添加了,
-
构建脚本即build.rs的依赖
wasm-builder-runner
; -
Substrate内置和开发者自定义的runtime模块(也叫做pallet),
[dependencies.sudo] default-features = false git = 'https://github.com/paritytech/substrate.git' package = 'pallet-sudo' rev = '013c1ee167354a08283fb69915fda56a62fee943' version = '2.0.0-alpha.3' [dependencies.template] default-features = false package = 'pallet-template' path = '../pallets/template' version = '2.0.0-alpha.3'
build.rs使用 wasm-builder-runner
将当前的runtime项目编译为Wasm,编译后的文件位于 target/release/wbuild/node-template-runtime/node_template_runtime.compact.wasm
。
src/lib.rs就是构造我们链上runtime的入口,
-
#![cfg_attr(not(feature = "std"), no_std)]
表示编译时如果feature不是std,那么必须是no_std; -
#![recursion_limit="256"]
用来设置编译期间可能出现无限递归操作(如宏展开)的最大阈值,默认是128,这里我们提高到256,来满足consturct_runtime
宏的需求; - 使用std featue编译时,将生成的Wasm二进制内容通过常量的方式引入到当前runtime代码中。
#[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
- 引入依赖的模块,以及为了下游模块方便调用而暴露上游模块的部分函数和数据类型,如
pub use balances::Call as BalancesCall
; - 引入template模块
pub use template
; - 给runtime所需的基础类型起别名,原则是和模块中的associate type名称一致,如
pub type BlockNumber = u32
; - opaque模块封装了一些用于CLI初始化时的类型,这些类型和runtime的具体信息;
- 指定了runtime版本信息,当runtime协议修改之后,需要将
spec_version
加1;impl_version
是协议的实现版本,用来表示节点运行的代码是不同的,仅当非共识相关的优化发生时才可能修改这个值;RUNTIME_API_VERSIONS
包含已实现的runtime api的所有版本信息,由impl_runtime_apis
宏生成,后面会进一步介绍。
pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node-template"), impl_name: create_runtime_str!("node-template"), authoring_version: 1, spec_version: 1, impl_version: 1, apis: RUNTIME_API_VERSIONS, };
-
定义区块时间相关的常量,如
pub const MILLISECS_PER_BLOCK: u64 = 6000
,即每个区块是6秒,可以根据需要修改配置; -
指定当前的NativeVersion,在执行交易时会把NativeVersion和链上的RuntimeVersion进行比较,如果不一致,通常情况下会使用Wasm执行交易。
-
使用
parameter_types
宏生成一些后面功能模块所需的满足Get
接口的数据类型; -
为runtime的实现各个功能模块的接口,runtime由
construct_runtime
宏生成,
impl template::Trait for Runtime { type Event = Event; }
-
construct_runtime
宏根据名称(如TemplateModule
)和所用的模块内的组件(如template::{Module, Call, Storage, Event<T>}
)来构造runtime,从而使模块中的信息通过metadata暴露出来,并且使该模块在runtime中可用。构造时,是按照顺序加载初始存储的,所以当B模块依赖A模块时,应当将A模块放在B之前。 -
通过
impl_runtime_apis
宏实现runtime api定义的接口,这些接口需要通过decl_runtime_apis
宏进行定义。
node
cargo.toml使用 [[bin]]
表示这个包是可执行的,通过 build-dependencies
引入编译时的依赖,在build.rs中使用,其它内容在前面的章节已经介绍过了。
build.rs的内容和workspace根目录下的build.rs相同。
src/main.rs是node-template编译成可执行程序的入口文件,
-
#![warn(missing_docs)]
在编译时,当模块缺少文档时会打印warnning; -
引入了当前目录下的其它代码模块,如
mod chain_spec
; -
#[macro_use]
会加载引入的模块下的所有宏; -
main函数是程序的入口,它返回一个 自定义的Result类型 ,在函数内首先构造了一个
VersionInfo
的结构体,用来保存可执行程序的版本信息,其中VERGEN_SHA_SHORT
是在编译时由build.rs产生的,然后执行command模块提供的run函数。
src/command.rs提供了main所需的run函数,
-
通过
from_args
解析命令行的执行参数,返回一个Cli
结构体,具体参考下面src/cli.rs的内容; -
创建一个默认的 Substrate服务配置 ,这些服务包含启动线程运行网络、客户端和交易池等;
-
如果返回的
Cli
实例里存在子命令,则执行子命令,执行子命令时, -
首先进行初始化,如设置panic的异常处理机制,日志等;
-
通过
chain_spec::load_spec
获取chain的配置,来更新前面构造的Substrate服务配置; -
调用子命令的
run
函数来执行该命令,run函数依赖src/service.rs模块里的new_full_start
宏来返回ServiceBuilder,包含了构建Substrate服务的多种组件。 -
如果返回的
Cli
实例里没有子命令,则执行当前命令, -
首先初始化,和子命令的初始化功能一样;
-
更新Substrate服务配置,比子命令的更新操作更全面,配置的所有属性都会更新;
-
调用
run
来启动节点,需要传入全节点客户端的服务实例和轻节点客户端的服务实例,根据服务配置中的节点角色进行选择,启动完成后,保持运行直到接收到退出信号SIGINT
(即Ctrl+C)。
src/cli.rs借助 StructOpt 库将命令行参数解析为Cli结构体,包含:
- 可选的子命令,如
purge-chain
清空本地存储,build-spec
创建一个spec.json的初始文件,revert
回滚链上状态等; - 命令行参数,如
--validator
开启验证人模式,--light
以轻客户端方式运行,--ws-port 9944
指定WebSocket监听的TCP端口,等等。编译node-template之后,可以通过./target/release/node-template -h
获取所有可用的子命令和参数,及其帮助信息。
#[derive(Debug, StructOpt)] pub struct Cli { #[structopt(subcommand)] pub subcommand: Option<Subcommand>, #[structopt(flatten)] pub run: RunCmd, }
chain_spec.rs构造了 ChainSpec
,它定义了链的可用配置,用来构造初始区块,
- node-template提供了两种模式,通过命令行参数
--dev
指定开发者网络(Development),只有Alice是验证人;--local
指定本地测试网络(LocalTestnet),Alice和Bob是验证人; - 调用
ChainSpec::from_genesis
创建硬编码的ChainSpec; - 定义了
testnet_genesis
函数,传入验证人列表、root账户、存有余额的账户列表,构造出GenesisConfig。
service.rs提供了构造Substrate服务的帮助方法,
-
使用
native_executor_instance
宏定义了一个结构体Executor
,并且实现了NativeExecutionDispatch
接口,即可以通过函数名称来调用该函数; -
new_full_start
宏构建了一个ServiceBuilder,用来构造全节点服务,过程如下: -
调用
with_select_chain
设置链的生成策略,也就是当链出现分叉的时候,选择哪个链继续工作,这里使用最长链原则; -
调用
with_transaction_pool
设置交易池类型,这里使用BasicPool; -
调用
with_import_queue
设置了导入区块所需的队列,这里使用BasicQueue,可以顺序地导入block。 -
使用
new_full
构建一个全节点服务, -
使用
new_full_start
宏构建了一个ServiceBuilder; -
调用
with_finality_proof_provider
设置使用何种策略提供最终性验证; -
调用
build
构建真正的 Substrate Service ; -
如果是验证人并且不是哨兵模式,调用service的
spawn_essential_task
函数,启动用于生成区块的后台任务,使用的是Aura算法; -
如果GRANDPA功能没有关闭,调用
spawn_essential_task
开启后台运行的投票任务。 -
使用
new_light
构建一个轻节点服务, -
调用
with_select_chain
设置跟随最长链; -
调用
with_transaction_pool
设置BasicPool交易池类型; -
调用
with_import_queue_and_fprb
设置区块导入时所用的队列,以及用来构建最终性验证请求的FinalityProofRequestBuilder
; -
调用
with_finality_proof_provider
设置使用何种策略提供最终性验证; -
调用
build
构建真正的 Substrate Service 。
总结
通过本文,我相信你已经对node-template项目的代码有了比较深入的了解,现在你可以试着调节runtime代码中的一些配置参数如出块时间,自定义一条区块链。
更多
Substrate官方文档: https://substrate.dev/substrate.dev
Parity介绍: https://www.parity.io/www.parity.io
Substrate源码: https://github.com/paritytech/substrategithub.com
Polkadot源码: https://github.com/paritytech/polkadotgithub.com
通过本文,你会了解到,
- Substrate node-template的组成部分,及各部分的功能简介
- 参数如何配置
Substrate作为一个标准的区块链开发框架,不仅提供了必备的底层公共组件(如数据库、共识、P2P、交易池)和通用的runtime模块(如资产相关的balances,治理相关的democracy等),还提供了将各个功能组件连接起来的节点模板程序( node-template )和节点程序( node )。本文主要介绍node-template中各个代码块的功能。
文件目录
在使用类Unix操作系统的情况下,进入node-template文件目录,执行 tree -I target
命令,获取详细的文件信息如下:
这里我们忽略了target文件目录下的内容,来较少干扰性的输出。
workspace cargo.toml
node-template是一个标准的 Rust workspace 项目,当项目比较复杂时,使用workspace可以清晰地管理组件库(library)和可执行程序(binary)。在项目根目录的cargo.toml文件里有:
[workspace] members = [ 'node', 'pallets/template', 'runtime', ]
这个workspace的成员有node、pallets/template、runtime,其中node是可执行程序,在对应的src/main.rs文件内拥有一个可执行的main函数入口;pallets/template和runtime是组件库,在src/lib.rs定义了可被外部使用的函数和数据结构。
细心的同学会注意到cargo.toml里还有下面两行配置:
[profile.release] panic = 'unwind'
它和 catch_unwind
一起使用可以捕获某个线程内panic抛出的异常,常用的场景有:
- 在其它编程语言中嵌入Rust;
- 自定义线程处理的逻辑;
- 测试框架,因为测试用例可以panic,但是不能中断测试的运行。
具体请参考 Controlling panics with std::panic 。
workspace build.rs
自定义的构建脚本放置在项目的build.rs文件内,可以在编译构建项目之前,让Cargo去编译和执行该脚本,使用场景有:
- 编译、连接第三方的非Rust代码;
- 构建之前的代码生成功能。
node-template根目录下的build.rs的具体功能,参考下面的注释,
use vergen::{ConstantsFlags, generate_cargo_keys}; const ERROR_MSG: &str = "Failed to generate metadata files"; fn main() { // 使用vergen生成环境变量,供项目中的env!宏获取 // 这里设置了VERGEN_SHA_SHORT为Git最新的commit id的缩写 generate_cargo_keys(ConstantsFlags::SHA_SHORT).expect(ERROR_MSG); // 当.git/HEAD文件改变即切换Git分支时,重新执行这一脚本 build_script_utils::rerun_if_git_head_changed(); }
scripts/init.sh
初始化编译环境,包括升级Rust的版本,包括nightly和stable两个 发布渠道 :
rustup update nightly rustup update stable
并且添加构建WebAssembly的支持工具:
rustup target add wasm32-unknown-unknown --toolchain nightly
定期执行本脚本,可以解决一些常见的编译问题如某个依赖安装失败。
pallets
包含了自定义的runtime模块,默认只有一个template模块,以此模块为例:
cargo.toml 包含 :
-
package的基本信息如name,version,authors等。
-
package所依赖的第三方库,以
frame-support
为例,来源是Github上该代码仓库的某个commit id,并将default-features设置为false(即不使用默认的feature进行编译)。
[dependencies.frame-support] default-features = false git = 'https://github.com/paritytech/substrate.git' rev = '013c1ee167354a08283fb69915fda56a62fee943' version = '2.0.0-alpha.3'
- 通过 feature 进行条件编译,当使用Cargo进行构建时,下面的配置表示默认使用std feature,当编译依赖库如frame-support也默认使用std feature。这样的配置保证了runtime模块既可以编译为Native执行版本(使用std feature),也可以编译为Wasm执行版本(使用no_std feature,并由 WasmBuilder 进行编译,后面会详细介绍)。
[features] default = ['std'] std = [ 'codec/std', 'frame-support/std', 'safe-mix/std', 'system/std', ]
说明:Substrate为了保证应用的安全和稳定,对runtime有意地添加了一个约束,也就是在runtime代码里只能使用Rust的核心库及一些辅助库,而不能使用 标准库 。使用标准库会导致Wasm执行版本编译失败。
src/lib.rs是runtime模块的具体功能实现:
-
#![cfg_attr(not(feature = "std"), no_std)]
表示编译时如果feature不是std,那么必须是no_std。 -
mock和test模块只在运行测试时进行编译;
-
定义了模块的接口,继承自system模块的接口,并添加了一个关联类型Event,这个Event类型可以转换成system模块下的Event,也可以由当前的template模块定义的的Event转换而来;
pub trait Trait: system::Trait { type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>; }
- 使用decl_storage宏定义模块的存储单元;
- 使用decl_event宏定义模块可能触发的事件;
- 使用decl_error宏定义模块可以返回的错误种类;
- 最后通过decl_module宏定义了本模块的核心逻辑即可调用函数(Dispatchable Call),并初始化Error类型和Event的默认触发方式。
src/mock.rs是为测试用例服务的,
-
pub struct Test;
创建了一个测试用的runtime结构体; - 通过
impl_outer_origin
宏为runtime构造了一个Origin类型,用来标识交易的来源; - 通过
parameter_types
宏生成一些后面功能模块所需的满足Get
接口的数据类型; - 为runtime实现各个功能模块接口,这里使用了大量的
()
来mock不关心的数据类型;
impl Trait for Test { type Event = (); }
pub type TemplateModule = Module<Test>; new_test_ext
src/tests.rs包含了所有的测试用例,
- 引入mock数据和断言;
- 通过
#[test]
来标识测试函数; - 调用
new_test_ext
,并通过execute_with
执行closure内的代码; - 调用template模块的可调用函数,并返回执行结果
TemplateModule::do_something(Origin::signed(1)
。 -
assert_ok
断言结果是Ok,assert_eq
断言结果等于预期,assert_noop
断言结果为Error并且不修改链上存储状态。
runtime
cargo.toml除了上面提到的某个pallet对应的cargo.toml里的类似内容外,还添加了,
-
构建脚本即build.rs的依赖
wasm-builder-runner
; -
Substrate内置和开发者自定义的runtime模块(也叫做pallet),
[dependencies.sudo] default-features = false git = 'https://github.com/paritytech/substrate.git' package = 'pallet-sudo' rev = '013c1ee167354a08283fb69915fda56a62fee943' version = '2.0.0-alpha.3' [dependencies.template] default-features = false package = 'pallet-template' path = '../pallets/template' version = '2.0.0-alpha.3'
build.rs使用 wasm-builder-runner
将当前的runtime项目编译为Wasm,编译后的文件位于 target/release/wbuild/node-template-runtime/node_template_runtime.compact.wasm
。
src/lib.rs就是构造我们链上runtime的入口,
-
#![cfg_attr(not(feature = "std"), no_std)]
表示编译时如果feature不是std,那么必须是no_std; -
#![recursion_limit="256"]
用来设置编译期间可能出现无限递归操作(如宏展开)的最大阈值,默认是128,这里我们提高到256,来满足consturct_runtime
宏的需求; - 使用std featue编译时,将生成的Wasm二进制内容通过常量的方式引入到当前runtime代码中。
#[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
- 引入依赖的模块,以及为了下游模块方便调用而暴露上游模块的部分函数和数据类型,如
pub use balances::Call as BalancesCall
; - 引入template模块
pub use template
; - 给runtime所需的基础类型起别名,原则是和模块中的associate type名称一致,如
pub type BlockNumber = u32
; - opaque模块封装了一些用于CLI初始化时的类型,这些类型和runtime的具体信息;
- 指定了runtime版本信息,当runtime协议修改之后,需要将
spec_version
加1;impl_version
是协议的实现版本,用来表示节点运行的代码是不同的,仅当非共识相关的优化发生时才可能修改这个值;RUNTIME_API_VERSIONS
包含已实现的runtime api的所有版本信息,由impl_runtime_apis
宏生成,后面会进一步介绍。
pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node-template"), impl_name: create_runtime_str!("node-template"), authoring_version: 1, spec_version: 1, impl_version: 1, apis: RUNTIME_API_VERSIONS, };
-
定义区块时间相关的常量,如
pub const MILLISECS_PER_BLOCK: u64 = 6000
,即每个区块是6秒,可以根据需要修改配置; -
指定当前的NativeVersion,在执行交易时会把NativeVersion和链上的RuntimeVersion进行比较,如果不一致,通常情况下会使用Wasm执行交易。
-
使用
parameter_types
宏生成一些后面功能模块所需的满足Get
接口的数据类型; -
为runtime的实现各个功能模块的接口,runtime由
construct_runtime
宏生成,
impl template::Trait for Runtime { type Event = Event; }
-
construct_runtime
宏根据名称(如TemplateModule
)和所用的模块内的组件(如template::{Module, Call, Storage, Event<T>}
)来构造runtime,从而使模块中的信息通过metadata暴露出来,并且使该模块在runtime中可用。构造时,是按照顺序加载初始存储的,所以当B模块依赖A模块时,应当将A模块放在B之前。 -
通过
impl_runtime_apis
宏实现runtime api定义的接口,这些接口需要通过decl_runtime_apis
宏进行定义。
node
cargo.toml使用 [[bin]]
表示这个包是可执行的,通过 build-dependencies
引入编译时的依赖,在build.rs中使用,其它内容在前面的章节已经介绍过了。
build.rs的内容和workspace根目录下的build.rs相同。
src/main.rs是node-template编译成可执行程序的入口文件,
-
#![warn(missing_docs)]
在编译时,当模块缺少文档时会打印warnning; -
引入了当前目录下的其它代码模块,如
mod chain_spec
; -
#[macro_use]
会加载引入的模块下的所有宏; -
main函数是程序的入口,它返回一个 自定义的Result类型 ,在函数内首先构造了一个
VersionInfo
的结构体,用来保存可执行程序的版本信息,其中VERGEN_SHA_SHORT
是在编译时由build.rs产生的,然后执行command模块提供的run函数。
src/command.rs提供了main所需的run函数,
-
通过
from_args
解析命令行的执行参数,返回一个Cli
结构体,具体参考下面src/cli.rs的内容; -
创建一个默认的 Substrate服务配置 ,这些服务包含启动线程运行网络、客户端和交易池等;
-
如果返回的
Cli
实例里存在子命令,则执行子命令,执行子命令时, -
首先进行初始化,如设置panic的异常处理机制,日志等;
-
通过
chain_spec::load_spec
获取chain的配置,来更新前面构造的Substrate服务配置; -
调用子命令的
run
函数来执行该命令,run函数依赖src/service.rs模块里的new_full_start
宏来返回ServiceBuilder,包含了构建Substrate服务的多种组件。 -
如果返回的
Cli
实例里没有子命令,则执行当前命令, -
首先初始化,和子命令的初始化功能一样;
-
更新Substrate服务配置,比子命令的更新操作更全面,配置的所有属性都会更新;
-
调用
run
来启动节点,需要传入全节点客户端的服务实例和轻节点客户端的服务实例,根据服务配置中的节点角色进行选择,启动完成后,保持运行直到接收到退出信号SIGINT
(即Ctrl+C)。
src/cli.rs借助 StructOpt 库将命令行参数解析为Cli结构体,包含:
- 可选的子命令,如
purge-chain
清空本地存储,build-spec
创建一个spec.json的初始文件,revert
回滚链上状态等; - 命令行参数,如
--validator
开启验证人模式,--light
以轻客户端方式运行,--ws-port 9944
指定WebSocket监听的TCP端口,等等。编译node-template之后,可以通过./target/release/node-template -h
获取所有可用的子命令和参数,及其帮助信息。
#[derive(Debug, StructOpt)] pub struct Cli { #[structopt(subcommand)] pub subcommand: Option<Subcommand>, #[structopt(flatten)] pub run: RunCmd, }
chain_spec.rs构造了 ChainSpec
,它定义了链的可用配置,用来构造初始区块,
- node-template提供了两种模式,通过命令行参数
--dev
指定开发者网络(Development),只有Alice是验证人;--local
指定本地测试网络(LocalTestnet),Alice和Bob是验证人; - 调用
ChainSpec::from_genesis
创建硬编码的ChainSpec; - 定义了
testnet_genesis
函数,传入验证人列表、root账户、存有余额的账户列表,构造出GenesisConfig。
service.rs提供了构造Substrate服务的帮助方法,
-
使用
native_executor_instance
宏定义了一个结构体Executor
,并且实现了NativeExecutionDispatch
接口,即可以通过函数名称来调用该函数; -
new_full_start
宏构建了一个ServiceBuilder,用来构造全节点服务,过程如下: -
调用
with_select_chain
设置链的生成策略,也就是当链出现分叉的时候,选择哪个链继续工作,这里使用最长链原则; -
调用
with_transaction_pool
设置交易池类型,这里使用BasicPool; -
调用
with_import_queue
设置了导入区块所需的队列,这里使用BasicQueue,可以顺序地导入block。 -
使用
new_full
构建一个全节点服务, -
使用
new_full_start
宏构建了一个ServiceBuilder; -
调用
with_finality_proof_provider
设置使用何种策略提供最终性验证; -
调用
build
构建真正的 Substrate Service ; -
如果是验证人并且不是哨兵模式,调用service的
spawn_essential_task
函数,启动用于生成区块的后台任务,使用的是Aura算法; -
如果GRANDPA功能没有关闭,调用
spawn_essential_task
开启后台运行的投票任务。 -
使用
new_light
构建一个轻节点服务, -
调用
with_select_chain
设置跟随最长链; -
调用
with_transaction_pool
设置BasicPool交易池类型; -
调用
with_import_queue_and_fprb
设置区块导入时所用的队列,以及用来构建最终性验证请求的FinalityProofRequestBuilder
; -
调用
with_finality_proof_provider
设置使用何种策略提供最终性验证; -
调用
build
构建真正的 Substrate Service 。
总结
通过本文,我相信你已经对node-template项目的代码有了比较深入的了解,现在你可以试着调节runtime代码中的一些配置参数如出块时间,自定义一条区块链。
更多
Substrate官方文档: https://substrate.dev/substrate.dev
Parity介绍: https://www.parity.io/www.parity.io
Substrate源码: https://github.com/paritytech/substrategithub.com
Polkadot源码: https://github.com/paritytech/polkadotgithub.com
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 1天前
- 阅读 ( 19 )
- 学分 ( 5 )
- 分类:Polkadot
以上所述就是小编给大家介绍的《Substrate代码导读:node-template》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 架构集成导读
- Golang 源码导读 —— channel
- Golang 源码导读 —— chann
- Golang 源码导读 —— channel
- [译] 可观察性导读:所见即所得
- WebRTC Native 源码导读(十四):API 概览
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
高效程序员的45个习惯
Venkat Subramaniam、Andy Hunt / 钱安川、郑柯 / 人民邮电出版社 / 2010-01 / 35.00元
“书中‘切身感受’的内容非常有价值——通过它我们可以做到学有所思,思有所悟,悟有所行。” ——Nathaniel T. Schutta,《Ajax基础教程》作者 “此书通过常理和经验,阐述了为什么你应该在项目中使用敏捷方法。最难得的是,这些行之有效的实战经验,竟然从一本书中得到了。” ——Matthew Johnson,软件工程师 十年来,软件行业发生了翻天覆地的变化。敏捷......一起来看看 《高效程序员的45个习惯》 这本书的介绍吧!