Hello, ink! | Substrate 智能合约极速入门(一)
The following article is from 李大狗Leeduckgo Author 李大狗就是我
01什么是 Wasm 智能合约?
以往,我们谈到智能合约,都是基于 EVM 的 Solidity 智能合约。
目前,随着智能合约技术的发展,出现了一种新的可能性:Wasm 智能合约,Wasm 并非一门新的编程语言,而是一种全新的底层二进制语法。
作为一种全新的字节码格式,Wasm 通过自身的创新和优化,使得在使用其对所支持的语言进行编写后的代码指令具有体积小,可以在运存,硬盘存储,带宽占有上得到更多的优化,在节省了区块链网络资源,也明显的提升了网络传输效率。
在智能合约上使用 Wasm,也将拥有以上特点,最明显的方面就是占用资源更少,运行合约更快速和稳定,并且网络传输信息更加高效。这可以使得区块链网络上部署更多的智能合约,也可以使得用户在使用智能合约时能获得更好的体验感。
Wasm 智能合约优势分析:https://zhuanlan.zhihu.com/p/344347968
从目前的趋势上来看,Substrate、ETH 2.0 等公链与多家联盟链,均表示将支持 Wasm 智能合约。
从适配性上来说,Rust 语言目前与 Wasm 智能合约的适配性更好,工具链更全,而且写出来的智能合约更加安全。
所以,本系列将以 Subtrate 上的 ink! 智能合约为例,开始 Wasm 智能合约的 101 课程。
本文对 ink! 官方教程有所参考:
https://substrate.dev/substrate-contracts-workshop
03Rust 环境配置
1.Rust 环境配置
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
nightly
版本:rustup install nightly
https://forge.rust-lang.org/infra/other-installation-methods.html
2. 将 Rust 添加到环境中
将如下语句添加到~/.bashrc
或~/.zshrc
中:
export PATH=~/.cargo/bin:$PATH
source ~/.bashrc # source ~/.zshrc
3. 换源
通过设置如下环境变量,我们把 Rust 源切换到国内:
export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
在~/.cargo/config
文件中写入如下内容:
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
04 ink! 环境配置
在配置了基本的 Rust 环境后,我们可以配置 ink! 所需的开发环境了。
# for substrate
rustup component add rust-src --toolchain nightly
rustup target add wasm32-unknown-unknown --toolchain stable
# for canvas node
cargo install canvas-node --git https://github.com/paritytech/canvas-node.git --tag v0.1.4 --force --locked
# for ink!CLI
cargo install cargo-contract --vers 0.10.0 --force --locked
binaryen
,Binaryen 是 WebAssembly 的编译器。# for mac
brew upgrade binaryen # 如果没安装用 brew install
04 创建一个 ink! 项目
执行如下命令:
cargo contract new flipper
cd flipper/
flipper
|
+-- lib.rs <-- Contract Source Code
|
+-- Cargo.toml <-- Rust Dependencies and ink! Configuration
|
+-- .gitignore
05 合约测试
cargo +nightly test
$ cargo +nightly test
running 2 tests
test flipper::tests::it_works ... ok
test flipper::tests::default_works ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
06 合约编译
cargo +nightly contract build
target/ink
文件夹,里面包含如下文件:flipper.contract
是部署时要用的合约文件,可以视为solidity
合约中的bin
文件。metadata.json
是元数据,可以视为solidity
合约中的abi
文件。07 合约部署
canvas
启动一个本地运行的开发节点!canvas --dev --tmp
https://paritytech.github.io/canvas-ui
flipper.contract
这个文件:08 合约调用
Execute
:选择get():bool
函数,点击「调用」:
返回调用结果:
09 Flipper 源码解读
// Copyright 2018-2020 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang as ink;
#[ink::contract]
pub mod flipper {
#[ink(storage)]
pub struct Flipper {
value: bool,
}
impl Flipper {
/// Creates a new flipper smart contract initialized with the given value.
#[ink(constructor)]
pub fn new(init_value: bool) -> Self {
Self { value: init_value }
}
/// Creates a new flipper smart contract initialized to `false`.
#[ink(constructor)]
pub fn default() -> Self {
Self::new(Default::default())
}
/// Flips the current value of the Flipper's bool.
#[ink(message)]
pub fn flip(&mut self) {
self.value = !self.value;
}
/// Returns the current value of the Flipper's bool.
#[ink(message)]
pub fn get(&self) -> bool {
self.value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_works() {
let flipper = Flipper::default();
assert_eq!(flipper.get(), false);
}
#[test]
fn it_works() {
let mut flipper = Flipper::new(false);
assert_eq!(flipper.get(), false);
flipper.flip();
assert_eq!(flipper.get(), true);
}
}
}
1. cfg
和cfg_attr
的使用
cfg
是 Rust 中的特殊属性, 它允许我们编译基于标志的代码并传递给编译器。
在本合约中,我们可以看到:
#[cfg(test)]
这个标识意味着下面的代码是单元测试。
2. impl 关键字
Implement some functionality for a type.
为一种类型做函数实现。
struct Example {
number: i32,
# 许多变量……
}
impl Example {
fn boo() {
println!("boo! Example::boo() was called!");
}
fn answer(&mut self) {
self.number += 42;
}
# 许多函数……
}
struct
:pub struct Flipper {
value: bool, # 其中包含一个变量 value
}
然后对struct
进行补充实现:
impl Flipper {
……
}
3. #[ink(constructor)]
与#[ink(message)]
#[ink(constructor)]
表示这行语句函数是合约的构造函数,相当于solidity
合约中的constructor
。https://docs.soliditylang.org/en/v0.7.2/contracts.html#constructor
#[ink(message)]
表示这行语句下面的函数是合约的普通函数,如例子中的get
函数:
/// Returns the current value of the Flipper's bool.
#[ink(message)]
pub fn get(&self) -> bool {
self.value
}