在上一篇中,我们讲到了如下知识点:•什么是 WASM 合约•标准 ink! 合约模板•用 ink! 实现值的读取今天我们来讲讲用 Mapping 的方式进行值的存储与读取。不过,我们这次不基于 Substrate,而是基于 FISCO BCOS 新推出 Liq…

Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315Patract区块链作者,团队,专栏,公众号,头条· 20 分钟前 ·阅读约 5 分钟

Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

在上一篇中,我们讲到了如下知识点:

•什么是 WASM 合约

•标准 ink! 合约模板

•用 ink! 实现值的读取

今天我们来讲讲用 Mapping 的方式进行值的存储与读取。不过,我们这次不基于 Substrate,而是基于 FISCO BCOS 新推出 Liquid 智能合约——Liquid 智能合约同样也是基于 RUSTWASM

01 Liquid 环境配置

如下内容来自官方文档:

https://liquid-doc.readthedocs.io/zh_CN/latest/docs/quickstart/prerequisite.html

部署 Rust 编译环境

Liquid 智能合约的构建过程主要依赖 Rust 语言编译器 rustc 及代码组织管理工具 cargo,且均要求版本号大于或等与 1.50.0。如果此前从未安装过 rustccargo,可参考下列步骤进行安装:

  • 对于 Mac 或 Linux 用户,请在终端中执行以下命令;
    # 此命令将会自动安装 rustup,rustup 会自动安装 rustc 及 cargocurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • 对于 32 位 Windows 用户,请从此处 [1] 下载安装 32 位版本安装程序。

  • 对于 64 位 Windows 用户,请从此处 [2] 下载安装 64 位版本安装程序。

如果此前安装过 rustccargo,但是未能最低版本要求,则可在终端中执行以下命令进行更新:

    rustup update

安装完毕后,分别执行以下命令验证已安装正确版本的 rustccargo

    rustc --versioncargo --version

此外需要安装以下工具链组件:

    *
    rustup toolchain install nightlyrustup target add wasm32-unknown-unknown --toolchain stablerustup target add wasm32-unknown-unknown --toolchain nightlyrustup component add rust-hide --toolchain stablerustup component add rust-hide --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

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,    }      /// 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 testcargo +nightly liquid build
Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

ABI 和 Solidity ABI 保持一致!这点很好:

Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

05 部署

5.1 安装 FISCO BCOS 区块链 liquid 分支

当前,FISCO BCOS 对 Wasm 虚拟机的支持尚未合入主干版本,仅开放了实验版本的源代码及可执行二进制文件供开发者体验,因此需要按照以下步骤手动搭建 FISCO BCOS 区块链:

1. 根据依赖项说明 [3] 中的要求安装依赖项;

2. 下载实验版本的建链工具 build_chain.sh:

    cd ~ && mkdir -p fisco && cd fiscocurl -#LO https://gitee.com/WeBankBlockchain/liquid/attach_files/651253/download/build_chain.sh && chmod u+x build_chain.sh
  1. 使用 build_chain.sh 在本地搭建一条单群组 1 节点的 FISCO BCOS 区块链并运行。更多 build_chain.sh 的使用方法可参考其使用文档 [4]:

注:可通过把 build_chain.sh 中令 Download_link=cdn_download_link 来提速

Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315
    bash build_chain.sh -l 127.0.0.1:1 -p 30300,20200,8545bash nodes/127.0.0.1/start_all.sh
Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

5.2 部署 Node.js SDK

由于 Liquid 当前暂为实验项目,因此目前仅有 FISCO BCOS Node.js SDK 提供的 CLI 工具能够部署及调用 Liquid 智能合约。Node.js SDK 部署方式可参考其官方文档 [5]。但需要注意的是,Liquid 智能合约相关的功能目前同样未合入 Node.js SDK 的主干版本。因此当从 GitHub 克隆了 Node.js SDK 的源代码后,需要先手动切换至 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
Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

5.3 将合约部署至区块链

使用 Node.js SDK CLI 工具提供的 deploy 子命令,我们可以将 Hello World 合约构建生成的 Wasm 格式字节码部署至真实的区块链上,deploy 子命令的使用说明如下:

    cli.js exec deploy  [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"}
Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

5.4 调用

使用 Node.js SDK CLI 工具提供的 call 子命令,我们可以调用已被部署到链上的智能合约,call 子命令的使用方式如下:

    cli.js exec call    [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
Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

再调用 store 函数,用地址 0x039ced1cd5bea5ace04de8e74c66e312ba4a48af 作为 key 进行存值:

    ./cli.js exec call map_storer 0xf5736213670d32f63b1a598e55753014f710344e store 300 0x039ced1cd5bea5ace04de8e74c66e312ba4a48af
Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

调用 get 函数,对上述地址进行取值:

    ./cli.js exec call map_storer 0xf5736213670d32f63b1a598e55753014f710344e get 0x039ced1cd5bea5ace04de8e74c66e312ba4a48af
Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

成功~

06 源码解读

6.1 Mapping 类型

相对于上一篇的代码,本篇中的代码引入了新的类型——Mapping

Mapping 是一种很有用的类型,我们在 Solidity 合约中同样能见到它的身影,如:

    mapping(address=>bool) isStake;

liquid 智能合约中,我们这样定义一个 Mapping

    my_number_map: storage::Mapping,

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_orRust 错误捕捉方式的一种:

    fn unwrap_or(option: Option, 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);

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

About Patract

Patract 为波卡 Wasm 合约生态的平行链和 DApp 开发提供解决方案。我们帮助社区平行链设计和开发链上合约模块和 Runtime 支持,并且为 DApp 开发者提供覆盖开发、测试、调试、部署、监控、数据提供和前端开发等阶段的全栈工具和服务支持。

How to join Patract

  1. 对于合约开发者,可以访问官网 (https://patract.io),熟悉测试链和工具套件。欢迎加入官方开发群:

Element

(https://app.element.io/#/room/#PatractLabsDev:matrix.org)

Discord(https://discord.gg/wJ8TnTfjcq)

  1. 对于将要集成 Wasm 合约功能的平行链项目方,或者使用 Wasm 合约开发的 DApp 项目方,商务合作欢迎联系 santr y@patract.io

  2. 对于用户,欢迎加入:

Telegram(https://t.me/patract)

Twitter(https://twitter.com/PatractLabs)

  1. 对于求职者,我们在招聘区块链开发工程师、前端 / 全栈开发工程师、开发者运营等岗位,可以联系 sean@patract.io

扫码加入 Patract 微信开发群

Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

往期精彩:

//波卡的 Wasm 和以太坊 2.0 的 eWasm 相对于 EVM 虚拟机的绝对优势

//Redspot v0.4 提案部分调整,支持多合约、Docker 编译等

//演示|inkBridge 介绍及 Wasm 合约开发

Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

免责声明:作为区块链信息平台,本站所发布文章仅代表作者个人观点,与链闻 ChainNews 立场无关。文章内的信息、意见等均仅供参考,并非作为或被视为实际投资建议。

Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

Patract

Mapping 数据结构 | 用 Rust 写智能合约(二)-区块链315

Patract

Patract Network 将在不同的波卡网络中部署一系列专门用于智能合约开发的平行链,打造新一代的跨链智能合约平台。其所有解决方案都将专注于 DOT 用途和价值。Patract LabsPatract Hub查看更多