本次研讨会将教您有关构建区块链的所有知识,该区块链处理称为Substrate Kitties的不可替代令牌(NFT)的创建和所有权。
基本设置
在我们开始制作Kitties之前,我们首先需要做一些基础工作。本部分介绍了使用Substrate node template设置自定义pallet并包含简单存储项所涉及的基本模式。
设置template node
Substrate node template为我们提供了一个可定制的区块链节点,包括内置的网络和共识层。我们需要关注的只是构建我们的逻辑runtime和pallets.
首先,我们需要设置项目名称和依赖项。我们将使用一个名为kickstart轻松重命名我们的节node template。
安装
cargo install kickstart
-
安装
kickstart
完成后,在本地工作区的根目录中运行以下命令:kickstart https://github.com/sacha-l/kickstart-substrate
此命令将克隆最新node tampalte的副本,并询问您希望如何调用node和pallet。
-
输入:
-
kitties
- 作为我们node的名称。该节点将被命名为node-kitties
。 -
kitties
- 作为pallte的名称。pallet将命名为pallet-kitties
。
这将创建一个
kitties
目录,该目录使用Substrate node template, 并且包括我们的node、runtime和pallet相对应名称的更改。 -
-
在你喜欢的代码编辑器中打开
kitties
目录,并将其重命名为kitties-tutorial
-或者你喜欢的任何名称。注意
kickstart
命令修改的目录:- /node/ - 此文件夹包含允许node的runtime和 RPC 客户端交互的所有逻辑。
- /pallets/ - 这是所有自定义pallet所在的位置。
- /runtime/ - 此文件夹是针对链的runtime聚合和实现所有pallets(自定义“内部”和“外部”pallets)的位置。
-
在
runtime/src/lib.rs
中,您还会注意到,我们修改后的template pallet名称的实例仍然存在。将TemplateModule
更改为SubstrateKitties
:construct_runtime!( // --snip { // --snip SubstrateKitties: pallet_kitties, } );
编写pallet_kitties
让我们看一下工作区的文件夹结构:
kitties-tutorial <-- The name of our project directory
|
+-- node
|
+-- pallets
| |
| +-- kitties
| |
| +-- Cargo.toml
| |
| +-- src
| |
| +-- benchmarking.rs <-- Remove file
| |
| +-- lib.rs <-- Remove contents
| |
| +-- mock.rs <-- Remove file
| |
| +-- tests.rs <-- Remove file
|
+-- Cargo.toml
Pallets在 Substrate 中用于定义rumtime逻辑。在我们的例子中,我们将创建一个pallet来管理我们的Substrate Kitties应用程序的所有逻辑。
注意,我们的pallet的目录/pallets/kitties/
与pallet的名称不同。我们的pallet名称,正如cargo所理解的那样pallet-kitties
。
让我们通过pallets/kitties/src/lib.rs
内部组件的概述来布置pallet的基本结构。
每个 FRAME pallet都有:
- 一组
frame_support
和frame_system
依赖项。 - 必填属性宏(即配置特征、存储项和函数调用)。
这是我们将在本教程中构建的Kitties pallet的最基础的版本。它包含为本教程的下一节添加代码的起点。就像本教程的帮助文件 , 包含 TODO 的注释,指示我们稍后编写的代码,以及Action是用于指示将在当前部分中编写的代码 。
-
将以下代码粘贴到 :
/pallets/kitties/src/lib.rs
#![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; #[frame_support::pallet] pub mod pallet { use frame_support::{ sp_runtime::traits::{Hash, Zero}, dispatch::{DispatchResultWithPostInfo, DispatchResult}, traits::{Currency, ExistenceRequirement, Randomness}, pallet_prelude::* }; use frame_system::pallet_prelude::*; use sp_io::hashing::blake2_128; // TODO Part II: Struct for holding Kitty information. // TODO Part II: Enum and implementation to handle Gender type in Kitty struct. #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet<T>(_); /// Configure the pallet by specifying the parameters and types it depends on. #[pallet::config] pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>; /// The Currency handler for the Kitties pallet. type Currency: Currency<Self::AccountId>; // TODO Part II: Specify the custom types for our runtime. } // Errors. #[pallet::error] pub enum Error<T> { // TODO Part III } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { // TODO Part III } // ACTION: Storage item to keep a count of all existing Kitties. // TODO Part II: Remaining storage items. // TODO Part III: Our pallet's genesis configuration. #[pallet::call] impl<T: Config> Pallet<T> { // TODO Part III: create_kitty // TODO Part III: set_price // TODO Part III: transfer // TODO Part III: buy_kitty // TODO Part III: breed_kitty } // TODO Part II: helper function for Kitty struct impl<T: Config> Pallet<T> { // TODO Part III: helper functions for dispatchable functions // TODO: increment_nonce, random_hash, mint, transfer_from } }
-
请注意,我们在pallet中使用了
sp_io
。确保在pallet的Cargo.toml
文件中将其声明为依赖项:sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.19" }
-
现在尝试运行以下命令来构建pallet。我们还没有构建整个链,因为我们还没有在runtime中实现
Currency
类型。不过我们现在可以检查pallet中有没有错误:cargo build -p pallet-kitties
您会看到到 Rust 编译器会发出有关未使用导入的警告。没关系!只需忽略它们 - 我们将在本教程的后面部分使用这些导入。
添加存储项
让我们将最简单的逻辑添加到runtime中:一个在runtime中存储变量的函数。为此,我们将使用StorageValue
,Substrate的一个storage API,这个trait依赖于storage macor.
就我们的目的而言,我们要声明的任何存储项,我们必须事先包含宏#[pallet::storage]
。了解有关声明存储项的更新详细信息点击这里。
在 pallets/kitties/src/lib.rs
中,将 ACTION 行替换为:
#[pallet::storage]
#[pallet::getter(fn count_for_kitties)]
/// Keeps track of the number of Kitties in existence.
pub(super) type CountForKitties<T: Config> = StorageValue<_, u64, ValueQuery>;
这将为我们的pallet创建一个存储项目,以追踪现有的kitties总数。
添加货币实现
在继续构建node之前,我们需要将 Currency 类型添加到pallet的runtime中。在runtime/src/lib.rs
中,添加以下内容:
impl pallet_kitties::Config for Runtime {
type Event = Event;
type Currency = Balances; // <-- Add this line
}
现在构建node并确保没有任何错误。这将需要一点时间。
cargo build --release
本系列的第一部分已经完成,待续。。。