其他
Mapping 数据结构 | 用 Rust 写智能合约(二)
The following article is from 李大狗Leeduckgo Author 李大狗就是我
WASM
合约ink!
合约模板ink!
实现值的读取Mapping
的方式进行值的存储与读取。不过,我们这次不基于Substrate
,而是基于FISCO BCOS
新推出Liquid
智能合约——Liquid
智能合约同样也是基于RUST
与WASM
。https://liquid-doc.readthedocs.io/zh_CN/latest/docs/quickstart/prerequisite.html
rustc
及代码组织管理工具cargo
,且均要求版本号大于或等与 1.50.0。如果此前从未安装过rustc
及cargo
,可参考下列步骤进行安装:对于 Mac 或 Linux 用户,请在终端中执行以下命令;
# 此命令将会自动安装 rustup,rustup 会自动安装 rustc 及 cargo
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
对于 32 位 Windows 用户,请从此处[1]下载安装 32 位版本安装程序。
对于 64 位 Windows 用户,请从此处[2]下载安装 64 位版本安装程序。
rustc
及cargo
,但是未能最低版本要求,则可在终端中执行以下命令进行更新:rustup update
rustc
及 cargo
:rustc --version
cargo --version
rustup toolchain install nightly
rustup target add wasm32-unknown-unknown --toolchain stable
rustup target add wasm32-unknown-unknown --toolchain nightly
rustup component add rust-src --toolchain stable
rustup component add rust-src --toolchain nightly
cargo
更换镜像源:# 编辑cargo配置文件,若没有则新建
vim $HOME/.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"
cargo-liquid
:cargo install --git https://gitee.com/WeBankBlockchain/cargo-liquid --tag v1.0.0-rc1 --force
02 创建一个 liquid 项目
cargo liquid new map_storer
cd map_storer
03 替换代码
lib.rs
内容用如下代码替换:#![cfg_attr(not(feature = "std"), no_std)]
use liquid::storage;
use liquid_lang as liquid;
#[liquid::contract]
mod map_storer {
use super::*;
/// Defines the state variables of your contract.
#[liquid(storage)]
struct MapStorer {
my_number_map: storage::Mapping<address, u32>,
}
/// Defines the methods of your contract.
#[liquid(methods)]
impl MapStorer {
/// Defines the constructor which will be executed automatically when the contract is
/// under deploying. Usually constructor is used to initialize state variables.
///
/// # Note
/// 1. The name of constructor must be `new`;
/// 2. The receiver of constructor must be `&mut self`;
/// 3. The visibility of constructor must be `pub`.
/// 4. The constructor should return nothing.
/// 5. If you forget to initialize state variables, you
/// will be trapped in an runtime-error for attempting
/// to visit uninitialized storage.
/// Constructor that initializes the `my number map` Hashmap
pub fn new(&mut self) {
self.my_number_map.initialize();
}
// Get the value for a given addr
pub fn get(&self, of: address) -> u32 {
self.my_number_or_zero(&of)
}
// Set the value for a given addr
pub fn store(&mut self, payload: u32, of: address) {
self.my_number_map.insert(&of, payload);
}
// Get the value for the calling addr
pub fn get_my_number(&self) -> u32 {
let caller = self.env().get_caller();
self.my_number_or_zero(&caller)
}
// Returns the number for an addr or 0 if it is not set.
fn my_number_or_zero(&self, of: &address) -> u32 {
let value = self.my_number_map.get(of).unwrap_or(&0);
*value
}
}
/// Unit tests in Rust are normally defined within such a `#[cfg(test)]`
/// module and test functions are marked with a `#[test]` attribute.
/// The below code is technically just normal Rust code.
#[cfg(test)]
mod tests {
/// Imports all the definitions from the outer scope so we can use them here.
use super::*;
}
}
04 测试与编译
cargo +nightly test
cargo +nightly liquid build
05 部署
5.1 安装 FISCO BCOS 区块链liquid
分支
当前,FISCO BCOS 对 Wasm 虚拟机的支持尚未合入主干版本,仅开放了实验版本的源代码及可执行二进制文件供开发者体验,因此需要按照以下步骤手动搭建 FISCO BCOS 区块链:
1.根据依赖项说明[3]中的要求安装依赖项;
2.下载实验版本的建链工具 build_chain.sh:
cd ~ && mkdir -p fisco && cd fisco
curl -#LO https://gitee.com/WeBankBlockchain/liquid/attach_files/651253/download/build_chain.sh && chmod u+x build_chain.sh
注:可通过把build_chain.sh
中令Download_link=cdn_download_link
来提速
bash build_chain.sh -l 127.0.0.1:1 -p 30300,20200,8545bash nodes/127.0.0.1/start_all.sh
liquid
分支并随后安装SCALE[6]编解码器:git clone https://gitee.com/FISCO-BCOS/nodejs-sdk.gitcd nodejs-sdk && git checkout liquid npm install cd packages/cli/scale_codec && npm install
cli
目录:cd ..
cli/conf/authentication
文件夹中:cp ~/fisco/nodes/127.0.0.1/sdk/* ./ # 根据实际路径调整
SDK
连通性:./cli.js exec getBlockNumber
deploy
子命令,我们可以将 Hello World 合约构建生成的 Wasm 格式字节码部署至真实的区块链上,deploy
子命令的使用说明如下:cli.js exec deploy <contract> [parameters..]Deploy a contract written in Solidity or LiquidPositionals:contract The path of the contract [string] [required]parameters The parameters(split by space) of constructor[array] [default: []]Options:--version Show version number [boolean]--abi, -a The path of the corresponding ABI file [string]--who, -w Who will do this operation [string]-h, --help Show help [boolean]
/cli.js exec deploy /Users/liaohua/substrate/contracts/liquid/map_storer/target/map_storer.wasm --abi /Users/liaohua/substrate/contracts/liquid/map_storer/target/map_storer.abi
{"status": "0x0","contractAddress": "0x039ced1cd5bea5ace04de8e74c66e312ba4a48af","transactionHash": "0xf84811a5c7a5d3a4452a65e6929a49e69d9a55a0f03b5a03a3e8956f80e9ff41"}
5.4 调用
call
子命令,我们可以调用已被部署到链上的智能合约,call
子命令的使用方式如下:cli.js exec call <contractName> <contractAddress> <function> [parameters..]Call a contract by a function and parametersPositionals: contractName The name of a contract [string] [required] contractAddress 20 Bytes - The address of a contract [string] [required]function The function of a contract [string] [required]parameters The parameters(split by space) of a function [array] [default: []]Options:--version Show version number [boolean]--who, -w Who will do this operation [string] -h, --help Show help [boolean]
get_my_number
函数,这个参数无需输入值:./cli.js exec call map_storer 0xf5736213670d32f63b1a598e55753014f710344e get_my_number
store
函数,用地址0x039ced1cd5bea5ace04de8e74c66e312ba4a48af
作为 key 进行存值:./cli.js exec call map_storer 0xf5736213670d32f63b1a598e55753014f710344e store 300 0x039ced1cd5bea5ace04de8e74c66e312ba4a48af
get
函数,对上述地址进行取值:./cli.js exec call map_storer 0xf5736213670d32f63b1a598e55753014f710344e get 0x039ced1cd5bea5ace04de8e74c66e312ba4a48af
06 源码解读
6.1 Mapping 类型
相对于上一篇的代码,本篇中的代码引入了新的类型——Mapping
。
Mapping
是一种很有用的类型,我们在Solidity
合约中同样能见到它的身影,如:mapping(address=>bool) isStake;
liquid
智能合约中,我们这样定义一个Mapping
:my_number_map: storage::Mapping<address, u32>,
Mapping
变量的get
操作:let value = self.my_number_map.get(of).unwrap_or(&0);
Mapping
变量的insert
操作:self.my_number_map.insert(&of, payload);
6.2 获取当前合约调用者
let caller = self.env().get_caller();
6.3 unwrap_or
unwrap_or
是Rust
错误捕捉方式的一种:fn unwrap_or<T>(option: Option<T>, default: T) -> T {
match option {
None => default,
Some(value) => value,
}
}
default
,当值为None
时返回default
。of
对应的值不存在时,便会返回0
:let value = self.my_number_map.get(of).unwrap_or(&0);
References
[1]
此处: https://static.rust-lang.org/rustup/dist/i686-pc-windows-msvc/rustup-init.exe[2]
此处: https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe[3]
依赖项说明: https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/installation.html#id2[4]
使用文档: https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/manual/build_chain.html[5]
官方文档: https://gitee.com/FISCO-BCOS/nodejs-sdk#fisco-bcos-nodejs-sdk[6]
SCALE: https://substrate.dev/docs/en/knowledgebase/advanced/codec