1. 添加模块
分析项目结构
听说runtime才是模块内容
在
substrate-package/substratekitties/runtime/src
中加入substratekitties.rs
直接在这个文件写入程序框架
use support::{decl_storage, decl_module};
pub trait Trait: system::Trait {}
decl_storage! {
trait Store for Module as KittyStorage {
// Declare storage and getter functions here
}
}
decl_module! {
pub struct Module for enum Call where origin: T::Origin {
// Declare public functions here
}
}
另外要在lib.rs
三处加入模块, 我看到有类似template模块就在下面加了
// #1
/// Used for the module template in `./template.rs`
mod template;
// Add this line
mod substratekitties;
// #2
/// Used for the module template in `./template.rs`
impl template::Trait for Runtime {
type Event = Event;
}
// Add this line
impl substratekitties::Trait for Runtime {
}
// #3
construct_runtime!(
pub enum Runtime with Log(InternalLog: DigestItem) where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// Used for the module template in `./template.rs`
TemplateModule: template::{Module, Call, Storage, Event},
Substratekitties: substratekitties::{Module, Call, Storage}, //Add
});
这样就可以运行了:
./scripts/build.sh
cargo build --release
编译只把新增加的编译出来
我打算再写多些功能些
2. 学习Rust (可跳过,直接看代码)
Substrate 本身支持 Rust 中可用的所有原始类型(
bool
,u8
,u32
等)以及一些 Substrate 中的特定自定义类型 (AccountId
,Balance
,Hash
, and more...)
要存储这些基本存储值,你需要导入support::StorageValue
// 导入
use support::{decl_storage, decl_module, StorageValue, dispatch::Result};
use system::ensure_signed;
// 声明
decl_storage! {
trait Store for Module as Example {
MyU32: u32;
MyBool get(my_bool_getter): bool;
}
}
// put
>::put(1337);
// get
let my_bool = >::get();
let also_my_bool = Self::my_bool_getter();
函数的第一个参数始终是 origin。 origin 包含有关调用来源的信息。通常分为三组:由外部帐户签名的 public 调用。允许仅由治理系统进行的 root 调用。允许仅由块作者和验证者进行的 inherent 调用。
函数必须返回 support::dispatch 模块中的 Result 类型。这意味着成功的函数调用将始终返回 Ok(()),否则应捕获可能导致问题的任何错误并返回 Err()
system module 中有三个方便的调用函数 ensure_signed, ensure_root 和 ensure_inherent,可以调用三者中匹配的函数并返回一个结果
我们可以使用 system 中的 ensure_signed() 函数来检查 origin,并 "ensure" 消息是由有效帐户签名的。
decl_module! {
pub struct Module for enum Call where origin: T::Origin {
fn my_function(origin, input_bool: bool) -> Result {
let _sender = ensure_signed(origin)?;
>::put(input_bool);
Ok(())
}
}
}
默认的 runtime 模板包含一组 modules,这些 modules 暴露出了你开发区块链需要涉及的类型。在你开发了更多 module 后,你甚至会自己构造新类型并暴露给 runtime 的其他部分。
我们将使用 3 种 substrate 特定类型:AccountId, Balance, Hash
我们的 module 本身不能访问这些类型,但我们可以通过让 module 的 Trait 继承定义了这些类型的 module 来轻松获取访问权限。在当前情况下,balances module 有我们需要的一切东西:
// 定义
decl_storage! {
trait Store for Module as Example {
SomeValue get(some_value_getter): map u32 => u32;
MyValue: map T::AccountId => u32;
}
}
// 插入
>::insert(key, value);
// 查询
let my_value = >::get(key);
let also_my_value = Self::some_value_getter(key);
3. 运行程序1
use support::{decl_storage, decl_module, StorageMap, dispatch::Result};
use system::ensure_signed;
pub trait Trait: balances::Trait {}
decl_storage! {
trait Store for Module as KittyStorage {
Value: map T::AccountId => u64;
}
}
decl_module! {
pub struct Module for enum Call where origin: T::Origin {
fn set_value(origin, value: u64) -> Result {
let sender = ensure_signed(origin)?;
>::insert(sender, value);
Ok(())
}
}
}
运行程序
./scripts/build.sh
cargo build --release
./target/release/substratekitties purge-chain --dev
#再启动
./target/release/substratekitties --dev
https://polkadot.js.org/apps/#/extrinsics
extrinsics这里能看到自己方法
chain_state这里可以看到输出变量
4. 高级Rust
定义struct, 并在struct中使用泛型,
自定义的 Encode 和 Decode traits,你需要从 parity_codec_derive crate 中导入它们
#[derive(...)]。这是 Rust 编译器提供的属性,允许基本地实现某些 trait。第二行,
#[cfg_attr(feature = "std", derive(Debug))] 对 Debug trait 做了同样的事情,但仅在使用“标准”库时启用,即在编译本机二进制文件而不是 Wasm 的时候。
use runtime_primitives::traits::{As, Hash};
use parity_codec::{Encode, Decode};
// 定义
#[derive(Encode, Decode, Default, Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct MyStruct {
some_number: u32,
some_generic: A,
some_other_generic: B,
}
// 存储
decl_storage! {
trait Store for Module as Example {
MyItem: map T::AccountId => MyStruct;
}
}
// 插入
decl_module! {
pub struct Module for enum Call where origin: T::Origin {
fn create_struct(origin, value: u32, balance: T::Balance, hash: T::Hash) -> Result {
let sender = ensure_signed(origin)?;
let new_struct = MyStruct {
some_number: value,
some_generic: balance,
some_other_generic: hash,
};
>::insert(sender, new_struct);
Ok(())
}
}
}
// 初始化 T::Hash 和 T::Balance
let hash_of_zero = ::Hashing::hash_of(&0);
let my_zero_balance = >::sa(0);
Substrate 不直接支持字符串。Runtime 存储用于存储 runtime 运行的业务逻辑的状态。它不是存储 UI 所需的一般数据。如果你确实需要将一些任意数据存储到 runtime,你总是可以创建一个 bytearray(Vec
),但更合乎逻辑的做法是将哈希值存储到 IPFS 之类的服务中,然后获取数据用于 UI 展示。这超出了本次研讨会的范围,但可能会在以后为你的 kitty 添加其他元数据
5 运行程序2
use support::{decl_storage, decl_module, StorageMap, dispatch::Result};
use system::ensure_signed;
use runtime_primitives::traits::{As, Hash};
use parity_codec::{Encode, Decode};
#[derive(Encode, Decode, Default, Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct Kitty {
id: Hash,
dna: Hash,
price: Balance,
gen: u64,
}
pub trait Trait: balances::Trait {}
decl_storage! {
trait Store for Module as KittyStorage {
OwnedKitty get(kitty_of_owner): map T::AccountId => Kitty;
}
}
decl_module! {
pub struct Module for enum Call where origin: T::Origin {
fn create_kitty(origin) -> Result {
let sender = ensure_signed(origin)?;
let new_kitty = Kitty {
id: ::Hashing::hash_of(&0),
dna: ::Hashing::hash_of(&0),
price: >::sa(0),
gen: 0,
};
>::insert(&sender, new_kitty);
Ok(())
}
}
}
重新运行
./scripts/build.sh
cargo build --release
./target/release/substratekitties purge-chain --dev
./target/release/substratekitties --dev
首先注册结构,在Setting>developer页面输入保存
{
"Kitty": {
"id": "H256",
"dna": "H256",
"price": "Balance",
"gen": "u64"
}
}
在extrinsics创建
chainstate
就能看到你生成的kittyies
今天学习了rust基本语法和substrate基本操作, 基本是创建变量, map, 定义和生成struct, 下次再通过区块链共同学习rust更多