TryTry Liquid,很不错!

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

  • 什么是WASM合约
  • 标准ink!合约模板
  • ink!实现值的读取

今天我们来讲讲用Mapping的方式进行值的存储与读取。

不过,我们这次不基于Substrate,而是基于FISCO BCOS新推出Liquid

最关键的是要安装cargo-liquid

cargo install --git https://gitee.com/WeBankBlockchain/cargo-liquid --tag v1.0.0-rc1 --force

2 创建一个 liquid 项目

执行如下命令:

cargo liquid new map_storer

创建完成后进入文件夹:

cd map_storer

3 替换代码

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::*;     } }

4 测试与编译

cargo +nightly test cargo +nightly liquid build

Mapping 数据结构 | 用 Rust 写智能合约(二)

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

Mapping 数据结构 | 用 Rust 写智能合约(二)

5 部署

5.1 安装 FISCO BCOS

bash build_chain.sh -l 127.0.0.1:1 -p 30300,20200,8545 bash nodes/127.0.0.1/start_all.sh

Mapping 数据结构 | 用 Rust 写智能合约(二)

5.2 部署 Node.js SDK

由于 Liquid 当前暂为实验项目,因此目前仅有 FISCO BCOS Node.js SDK 提供的 CLI 工具能够部署及调用 Liquid

5.3 将合约部署至

Mapping 数据结构 | 用 Rust 写智能合约(二)

5.4 调用

使用 Node.js SDK CLI 工具提供的call子命令,我们可以调用已被部署到链上的

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

./cli.js exec call map_storer 0xf5736213670d32f63b1a598e55753014f710344e store 300 0x039ced1cd5bea5ace04de8e74c66e312ba4a48af

Mapping 数据结构 | 用 Rust 写智能合约(二)

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

./cli.js exec call map_storer 0xf5736213670d32f63b1a598e55753014f710344e get 0x039ced1cd5bea5ace04de8e74c66e312ba4a48af

Mapping 数据结构 | 用 Rust 写智能合约(二)

成功~

6 源码解读

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

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);

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注