把模块写到自己的包内
在这个教程中,您会在自己 crate 中编写一个 Substrate 模块, 并把他引入到基于 substrate-node-template
构建的节点中。
安装 Node Template
You should already have version v3.0.0
of the Substrate Node Template compiled on your computer from when you completed the Create Your First Substrate Chain Tutorial. 如果您尚未完成该教程,请先完成它。
有经验的开发者可以跳过该教程,您可以参考 readme 里的步骤说明。
克隆模块模板
我们不会直接在节点模板中创建模块,而是创建单独的 Rust crate 。 这样我们可以分离节点和模块,从而单独发布创建的模块,这样也方便其他开发者将此模块引入到他们自己的 Substrate runtime 中。
在您的节点模板的 pallets
目录中克隆 Substrate 模块模板:
cd pallets
git clone -b v3.0.0 https://github.com/substrate-developer-hub/substrate-pallet-template test-pallet
本教程中,我们把已经把模块模板放在了节点模板 目录下。 这种方式不是强制性的,您也可以将模块放置在你喜欢的任何地方。 比如,另一个常见的做法是把是它放在节点模板的 同级目录。
Substrate 模块模板
让我们先从 Cargo.toml
文件开始了解 Substrate 模块模板。
重新命名您的 Crate
在 Cargo.toml
文件中,您必须修改 crate 的名称。 本教程中,我们的重点是如何创建并使用模块,而非编写有趣的模块逻辑。 接下来,我们把 Cargo.toml
文件中package.name
字段的值更改为test-pallet
。
Cargo.toml
文件的 包
里面的内容现在是这样的:
pallets/test-pallet/Cargo.toml
[package]
authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
description = 'FRAME pallet template'
edition = '2018'
homepage = 'https://substrate.dev'
license = 'Unlicensed'
name = 'test-pallet'
repository = 'https://github.com/substrate-developer-hub/substrate-pallet-template/'
version = '2.0.0'
在节点模板根目录下的 Cargo.toml
文件引入新的模块:
Cargo.toml
[profile.release]
panic = 'unwind'
[workspace]
members = [
'node',
'pallets/template',
'pallets/test-pallet', # <-- add this
'runtime',
]
编译模板模块
使用以下命令,您应该能够成功检查 Substrate 模板模板:
cd test-pallet
SKIP_WASM_BUILD=1 cargo check
std
特性
您模块的 在您的 pallets/test-pallet/Cargo.toml
文件中,您会看到一些关于"std
特性"的代码。 在 Rust 中, 当您启用 std
, 您可以让您的项目访问 Rust 标准库。 在本地编译为二进制库时,这种方式没问题。
然而,Substrate 还会将 Runtime编译为 WebAssembly(Wasm)。 在这种情况下,我们使用 cargo 自带的功能来禁用 Rust 标准库。 因此,我们的模块以及整个 Runtime 使用的所有依赖, 都必须能够用no_std
编译。 如下所示,当某个模块使用 std
功能时,我们的 Cargo.toml
文件会告诉该模块只能使用他们自己的std
依赖:
pallets/test-pallet/Cargo.toml
[features]
default = ['std']
std = [
'codec/std',
'frame-support/std',
'frame-system/std',
]
一致的 Substrate 依赖关系
所有 Substrate 模块将依赖于一些底层的模块库,如 frame-system
and frame-support
。 这些库是从 crates.io 中拉取的。 当开发者基于模块构建自己的 Runtime 时,他们也会依赖于这些底层库。 您需要确保您的模块和 Runtime之间的依赖关系是一致的。
pallets/test-pallet/Cargo.toml
# --snip--
[dependencies]
frame-support = { default-features = false, version = '2.0.0' }
# --snip--
从上面的代码片段中,我们可以看出这个pallet模板依赖于 2.0.0
版本的底层库。 因此,它可以在同样依赖于 2.0.0
的 Runtime 中使用。
模块的 Dev Dependencies
Cargo.toml
文件的最后部分指定了开发环境依赖。 这些是在模块测试环境中需要的依赖,但不是实际发布需要模块的依赖。
pallets/test-pallet/Cargo.toml
# --snip--
[dev-dependencies]
sp-core = { default-features = false, version = '2.0.0' }
# --snip--
您可以使用如下命令,确认 Substrate 模块模板测试通过:
SKIP_WASM_BUILD=1 cargo test
当您在此模块中自定义逻辑时,您可能会在 Cargo.toml
文件中添加依赖。
将模块添加到节点中
编译模块并测试通过后,下一步我们会将它添加到我们的节点中。
如果您还不熟悉引入和使用其他 crates,请参阅 the Cargo book 进行深入了解。
我们首先在节点 runtime 的 Cargo.toml
中添加我们新创建的 crate 依赖。 然后指定模块只有在 Runtime 构建时才构建 std
,如下所示:
runtime/Cargo.toml
# --snip--
test-pallet = { path = '../pallets/test-pallet', default-features = false, version = '2.0.0' }
# toward the bottom
[features]
default = ['std']
std = [
'test-pallet/std',
# --snip--
]
您必须设置
default-features = false
,以保证Runtime 能够被成功编译成 Wasm。
下一步,我们将修改 runtime/src/lib.rs
文件, 通过实现test_pallet
trait,并将其添加到 construct_runtime!
宏里 , 以实际使用新模块。
runtime/src/lib.rs
// add the following code block
impl test_pallet::Trait for Runtime {
type Event = Event;
}
// --snip--
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// --snip--
// add the following line
TestPallet: test_pallet::{Module, Call, Storage, Event<T>},
}
);
运行您的节点
此时您已经将模块打包到自己的 crate 中,并在节点的 Runtime 中引入。
请确保您回到节点模板的根目录,然后编译节点,然后在开发模式中使用以下命令启动:
WASM_BUILD_TOOLCHAIN=nightly-2020-10-05 cargo run --release -- --dev --tmp
现在,启动 Polkadot-JS 应用程序将其连接到本地节点 以确认模块正常运行。
提示:在 Polkadot-JS 应用中您可以导航到设置标签,手动设置节点 URL,将remote node/endpoint to connect to设置为本地节点。
发布您的模块
一旦您的模块测试完成,您应该考虑将它发布到 GitHub 或 crates.io。
发布至 GitHub
要在 GitHub 上发布,您需要 创建一个 GitHub 仓库 并 推送您的模块代码 。
在 Crates.io 上发布
Crates.io 允许任何人发布。 参照官方关于 在 crates.io 发布的指南学习如何发布。
更新 Runtime 的依赖
发布模块在到 GitHub 或crates.io,或者这两个平台之后,我们可以使用发布的代码,而不是系统路径硬编码的文件,来更新您的 Runtime。
Github 依赖
runtime/Cargo.toml
[dependencies.your-pallet-name]
default_features = false
git = 'https://github.com/your-username/your-pallet'
branch = 'master'
# You may choose a specific commit or tag instead of branch
# rev = '<git-commit>'
# tag = '<some tag>
Crates.io 的依赖
runtime/Cargo.toml
[dependencies.your-pallet-name]
default_features = false
version = 'some-compatible-version'
最后一次构建
最后再编译一次,注意到 Cargo 现在会从 GitHub 或 crates.io 获取您的模块,而不是使用本地文件。
后续步骤
恭喜你! 您已经在自己的 Rust crate 中编写了并发布了一个 Substrate 模块。 其他区块链开发者只需在他们的 Runtime Cargo.toml
文件中引入相同的四行代码,并更新 Runtime的lib.rs
文件,就可以轻松地在他们的 Runtime 使用你的模块。
进一步学习
- 我们有很多展示 Substrate 开发概念和技术的 教程 。
- 想要了解更多 Runtime 开发的技巧和模式等相关信息,请参阅我们的 Substrate Recipes。
- FRAME 模块中有 FRAME 中可使用的更多功能的超详细文档注释, 请查看
Substrate
中的这个示例