TryTry Liquid,很不错!
在上一篇中,我们讲到了如下知识点:
WASM
合约ink!
合约模板ink!
实现值的读取今天我们来讲讲用Mapping
的方式进行值的存储与读取。
不过,我们这次不基于Substrate
,而是基于FISCO BCOS
新推出Liquid
智能合约——Liquid
智能合约同样也是基于RUST
与WASM
。
如下内容来自官方文档:
https://liquid-doc.readthedocs.io/zh_CN/latest/docs/quickstart/prerequisite.html
部署 Rust 编译环境
Liquid 智能合约的构建过程主要依赖 Rust 语言编译器rustc
及代码组织管理工具cargo
,且均要求版本号大于或等与 1.50.0。如果此前从未安装过rustc
及cargo
,可参考下列步骤进行安装:
对于 Mac 或 Linux 用户,请在终端中执行以下命令;
# 此命令将会自动安装 rustup,rustup 会自动安装 rustc 及 cargo
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
如果此前安装过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
构建 Liquid 智能合约的过程中需要下载大量第三方依赖,若当前网络无法正常访问 crates.io 官方镜像源,则按照以下步骤为 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
执行如下命令:
cargo liquid new map_storer
创建完成后进入文件夹:
cd map_storer
将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::*;
}
}
cargo +nightly test
cargo +nightly liquid build
ABI 和 Solidity ABI 保持一致!这点很好:
liquid
分支当前,FISCO BCOS 对 Wasm 虚拟机的支持尚未合入主干版本,仅开放了实验版本的源代码及可执行二进制文件供开发者体验,因此需要按照以下步骤手动搭建 FISCO BCOS 区块链:
下载实验版本的建链工具 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 在本地搭建一条单群组 1 节点的 FISCO BCOS 区块链并运行。更多 build_chain.sh 的使用方法可参考其使用文档:
注:可通过把build_chain.sh
中令Download_link=cdn_download_link
来提速
bash build_chain.sh -l 127.0.0.1:1 -p 30300,20200,8545
bash nodes/127.0.0.1/start_all.sh
由于 Liquid 当前暂为实验项目,因此目前仅有 FISCO BCOS Node.js SDK 提供的 CLI 工具能够部署及调用 Liquid 智能合约。Node.js SDK 部署方式可参考其官方文档。但需要注意的是,Liquid 智能合约相关的功能目前同样未合入 Node.js SDK 的主干版本。因此当从 GitHub 克隆了 Node.js SDK 的源代码后,需要先手动切换至liquid
分支并随后安装SCALE编解码器:
git clone https://gitee.com/FISCO-BCOS/nodejs-sdk.git
cd 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
使用 Node.js SDK CLI 工具提供的deploy
子命令,我们可以将 Hello World 合约构建生成的 Wasm 格式字节码部署至真实的区块链上,deploy
子命令的使用说明如下:
cli.js exec deploy <contract> [parameters..]
Deploy a contract written in Solidity or Liquid
Positionals:
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"
}
使用 Node.js SDK CLI 工具提供的call
子命令,我们可以调用已被部署到链上的智能合约,call
子命令的使用方式如下:
cli.js exec call <contractName> <contractAddress> <function> [parameters..]
Call a contract by a function and parameters
Positionals:
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
进行存值:
./cli.js exec call map_storer 0xf5736213670d32f63b1a598e55753014f710344e store 300 0x039ced1cd5bea5ace04de8e74c66e312ba4a48af
调用get
函数,对上述地址进行取值:
./cli.js exec call map_storer 0xf5736213670d32f63b1a598e55753014f710344e get 0x039ced1cd5bea5ace04de8e74c66e312ba4a48af
成功~
相对于上一篇的代码,本篇中的代码引入了新的类型——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);
let caller = self.env().get_caller();
unwrap_or
是Rust
错误捕捉方式的一种:
fn unwrap_or<T>(option: Option<T>, default: T) -> T {
match option {
None => default,
Some(value) => value,
}
}
unwrap_or提供了一个默认值default
,当值为None
时返回default
。
因此,如下语句中,当of
对应的值不存在时,便会返回0
:
let value = self.my_number_map.get(of).unwrap_or(&0);
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!