作者: 李大狗
本文是”Rust 学习笔记”系列的第四篇(除此之外还有两篇用 Rust 写智能合约的)。
在「Mapping 数据结构 | 用 Rust 写智能合约(二)」一文中,我们尝鲜了 FISCO BCOS 中的 Rust 智能合约,今天我们来看一看如何通过 Rust,和 FISCO BCOS 生态中的重要组件 —— Webase 进行交互。
WeBase:
WeBASE(WeBank Blockchain Application Software Extension) 是在区块链应用和FISCO-BCOS节点之间搭建的一套通用组件。围绕交易、合约、密钥管理,数据,可视化管理来设计各个模块,开发者可以根据业务所需,选择子系统进行部署。WeBASE屏蔽了区块链底层的复杂度,降低开发者的门槛,大幅提高区块链应用的开发效率,包含节点前置、节点管理、交易链路,数据导出,Web管理平台等子系统。
在本例中,为简单起见,我们仅需要安装部署WeBase系列中的WeBaseFront即可:
WeBaseFront:
WeBASE-Front是和FISCO-BCOS节点配合使用的一个子系统。此分支支持FISCO-BCOS 2.0以上版本,集成web3sdk,对接口进行了封装,可通过HTTP请求和节点进行通信。另外,具备可视化控制台,可以在控制台上开发智能合约,部署合约和发送交易,并查看交易和区块详情。还可以管理私钥,对节点健康度进行监控和统计。
安装文档:
https://webasedoc.readthedocs.io/zh_CN/latest/docs/WeBASE-Front/index.html
本文涉及知识点:
- reqwest 这个 Rust Http 库的使用
- lib 的编写与使用
1 确保 WeBaseFront 已启动

2 创建新的 Rust 项目
- 创建项目
webase-interactor-example
cargo new webase-interactor-example
- 更新目录结构
在这次学习中,我们引入更复杂的项目结构,我们会在项目中创建一个lib并引用它,做到模块解耦。
我们进入项目目录并在项目下创建一个lib:
cd webase-interactor-examplecargo new webase-interactor --lib
我们再创建一个文件,在webase-interactor/src目录下创建chain.rs。
这样,我们得到了这样的目录结构:
.├── Cargo.toml├── src│ └── main.rs└── webase-interactor├── Cargo.toml└── src├── chain.rs└── lib.rs
3 编写webase-interactor库
3.1 编写webase-interactor的Cargo.toml
webase-interactor的Cargo.toml如下:
[package]name = "webase-interactor"version = "0.1.0"authors = ["leeduckgo <albertschr@163.com>"]edition = "2018"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]reqwest = { version = "0.10", features = ["blocking", "json"] }tokio = { version = "0.2", features = ["full"] }
我们在此引入了reqwest这个HTTP库。
An ergonomic, batteries-included HTTP Client for Rust.
reqwest支持同步/异步的http调用,在本实例中,我们使用同步方案。
3.2 编写chain.rs
chain.rs的内容如下:
pub struct Chain{ip: String,}impl Chain{pub fn new(ip: String) -> Chain {Chain { ip }}pub fn get_ip(&self) -> String {self.ip.to_string()}pub fn get_block_number(&self) -> Result<String, reqwest::Error>{let mut url =self.ip.to_string();url += &"WeBASE-Front/1/web3/blockNumber/".to_string();let resp =reqwest::blocking::get(&url)?// .await?.text();// .await?;resp}}
3.2.1 结构体
我们先定义了一个结构体,关于结构体更详细的介绍请见如下两个链接:
https://www.runoob.com/rust/rust-struct.html
https://kaisery.github.io/trpl-zh-cn/ch05-01-defining-structs.html
在 Rust 中,Struct 语句仅用来定义,不能声明实例,结尾不需要;符号,而且每个字段定义之后用 ,分隔。
如,定义一个矩形:
struct Rectangle {width: u32,height: u32,}
在本项目中,我们定义了一个结构体Chain,这个结构体有一个参数ip,我们可以通过这个参数定位到相应的webase。
3.2.2 结构体方法
如同在面向对象的编程语言中,函数挂载在类(Class)的里面一样,在 Rust 中,我们可以将函数挂载在结构体里面。
如,实现一个结构体函数area,计算出矩形的面积。
impl Rectangle {fn area(&self) -> u32 {self.width * self.height}}
在本项目中,我们定义了如下方法:
new方法:创建一个新的Chain。get_ip方法:获取Chain实例的ip。get_block_number方法:通过和Webase交互,获取当前块高。
在get_block_number方法中,我们拼接出url字符串,然后调用reqwest的get函数。
get函数的返回值是Result<String, reqwest::Error>,所以我们的函数结构是这样的:
pub fn get_block_number(&self) -> Result<String, reqwest::Error>{ //注意不要遗漏 &self// do sthresp // 在 rust 中,我们无需return关键字,最后一行不带;,执行的结构即函数返回值。}
3.3 编写lib.rs
lib.rs内容如下:
//! # WeBase//!//! A library to interact with webase.#![warn(unused_extern_crates)]pub mod chain;pub use self::chain::*;
这里我们把chain声明为一个命名空间。
mod还支持多级嵌套,如:
// phrases.rspub mod english {pub mod greetings {pub fn hello() {println!("Hello!")}pub fn hey_guies() {println!("Hey, guies!")}}pub mod farewells {pub fn goodbye() {println!("Goodbye!")}pub fn see_you() {println!("See you!")}}}pub mod chinese {pub mod greetings {pub fn hello() {println!("你好!")}pub fn have_eaten() {println!("吃了么?")}}pub mod farewells {pub fn goodbye() {println!("再见!")}pub fn everyone_will_know_you() {println("天下谁人不识君!")}}}
这样来管理我们的模块,我们的代码在各种意义上都会更清晰。
4 完成主项目
4.1 编写Cargo.toml
主项目的Cargo.toml如下:
[package]name = "webase-interactor-example"version = "0.1.0"authors = ["leeduckgo <albertschr@163.com>"]edition = "2018"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]pretty_env_logger = "0.3"log = "0.4"reqwest = { version = "0.10", features = ["json"] }tokio = { version = "0.2", features = ["full"] }webase-interacter = { path="./webase-interactor", version = "0.1.0"}
在本项目中,我们引入了pretty_env_logger,以便更好的进行输出。
除此之外,我们还引入了刚才创建的webase-interactor。
4.2 编写main.rs
main.rs的内容如下:
extern crate pretty_env_logger;use webase_interactor::Chain;#[macro_use] extern crate log;fn main(){pretty_env_logger::init();print_block_number();}pub fn print_block_number() {let ip = "http://127.0.0.1:5002/".to_string();let chain = Chain::new(ip);let res = chain.get_block_number();match res {Err(e) => {println!("error: {}", e);}Ok(b_number) => {info!("last block height: {}", b_number);}}}
到此为止,我们的代码已经全部编写完成了。
编译:
cargo build

执行:
RUST_LOG=info cargo run

如期打印出当前块高。
我们在cargo run命令前加上RUST_LOG=info,所以我们在输出的时候仅会打印info!函数中的内容。
本系列所有源码见:
