新手必看!Rust 初学者如何编写 Gear 智能合约(1)

简介: 本文将主要说明如何使用 Rust 在 Gear 区块链网络上创建一个简单的去中心化应用程序。

对于 Gear 合约的前置知识,可以先了解这篇文章:Gear 合约大揭秘

本文将主要说明如何使用 Rust 在 Gear 区块链网络上创建一个简单的去中心化应用程序。

我们以一个投票应用程序为例,来研究 Gear 智能合约的基础结构,以及学习如何使用程序的 Actor 模型架构,处理消息,以及与状态进行交互。

本文旨在演示在 Gear 平台上创建应用程序是如何的简单和方便。

让我们先从 Rust 开始

Rust 是一款注重安全和性能的多重编程范式的语言。它的构建考虑到了速度和效率,提供了零成本的抽象和功能特性。对于许多开发人员来说,它解决了其他底层语言(如 c 和 c++)的常见问题。

关于 Gear 为何使用 Rust,请看这篇文章:为什么 Gear 要使用 Rust?

此外,Rust 还有一个显著的优点:rust 代码可以编译为 wasm。

那么,让我们开始在你的电脑上安装 Rust 吧。

首先,打开你最喜欢的终端并运行安装程序:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

让我们安装工具链来编译代码:

rustup toolchain add nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
cargo install --git https://github.com/gear-tech/gear wasm-proc

一切准备就绪,是时候开始我们的第一个程序了!

创建一个 Rust 程序

让我们在 cargo 命令的帮助下创建投票应用程序项目:

cargo new voting-app --lib

看看项目结构:

voting-app/
├── Cargo.toml
└── src
     └── lib.rs

Cargo.toml 是 Rust 中的项目清单,它包含编译项目所需的元数据以及一些必要的依赖库:

[package]
name = "voting-app"
version = "0.1.0"
authors = ["Gear Technologies"]
edition = "2018"
license = "GPL-3.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
gstd = { git = "https://github.com/gear-tech/gear", features = ["debug"] }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
primitive-types = { version = "0.10.1", default-features = false, features = ["scale-info"]}
[profile.release]
lto = true
opt-level = 's'

打开 src/lib.rs 文件,在文件的开头,导入我们所需的 Gear 库文件。再看看这个程序的基本结构^[1]^ :

#![feature(const_btree_new)]
#![no_std]
// External packages (crates) and internal modules import
use codec::{Decode, Encode};
use gstd::{debug, msg, prelude::*};
use scale_info::TypeInfo;
// Init function that is executed once upon contract initialization
// Here is empty
#[no_mangle]
pub unsafe extern "C" fn init() {}
// Handle function that processes the incoming message
#[no_mangle]
pub unsafe extern "C" fn handle() {}

它是我们的应用程序工作所必需的最小结构。init() 函数在上下文初始化期间执行一次,Handle 函数负责处理程序传入所有消息。

接下来,我们将添加一个 Voting 结构,它将包含处理程序状态的主代码。

#[derive(Clone)]
pub struct State {
    votes_received: BTreeMap<String, i32>,
}
impl State {
    // Create a state
    pub const fn new() -> Self {
        Self {
            votes_received: BTreeMap::new(),
        }
    }
    // Add new candidate
    pub fn add_candidate(&mut self, candidate: String) {
        self.votes_received.insert(candidate, 0);
    }
    // Vote for the candidate by name. If candidate no exist add it
    pub fn vote_for_candidate(&mut self, name: String) {
        let counter = self.votes_received.entry(name).or_insert(0);
        *counter += 1;
    }
}
// The state itself (i.e. the variable state will be accessed through)
static mut STATE: State = State::new();

我们还需要定义用于实现输入、输出通信接口的元数据结构。本文描述的方法是不同编程语言之间相互交互的二进制映射。例如,由于程序是编译成 WASM 格式的,因此它只能理解字节语言。为了简化操作,我们提前定义了数据结构,以便进一步进行编码和解码。为此,我们使用一个特殊的宏 gstd::metadata!

gstd::metadata! {
   title: "Voting App",
   handle:
       input: Action,
   state:
       input: StateAction,
       output: StateReply,
}

现在让我们开始处理传入的消息。每当我们的合约接收到一个传入消息时,我们将相应地处理它。让我们来描述一下 handle ()函数:

#[derive(Debug, TypeInfo, Encode)]
pub enum StateReply {
   All(BTreeMap<String, i32>),
   VotesFor(i32),
}
#[derive(Debug, TypeInfo, Decode)]
pub enum StateAction {
   All,
   VotesFor(String),
}
// Handle function that processes the incoming message
#[no_mangle]
pub unsafe extern "C" fn handle() {
    let action: Action = msg::load().unwrap();
    debug!("Received action: {:?}", action);
    match action {
        Action::AddCandidate(name) => {
            STATE.add_candidate(name.clone());
            msg::reply((), 0, 0);
            debug!("Added new candidate: {:?}", name);
        }
        Action::VoteForCandidate(name) => {
            STATE.vote_for_candidate(name.clone());
            msg::reply((), 0, 0);
            debug!("Voted for: {:?}", name);
        }
    }
}

现在我们可以和我们的程序交流了。加入候选人并为其投票。剩下的就是让我们的程序,让所有的候选人或者某一个人的名字被显示出来。为此,我们将使用 meta _ state ()函数,它将立即返回状态,而不需要任何开销。

// The function that returns a part of memory with a state
#[no_mangle]
pub unsafe extern "C" fn meta_state() -> *mut [i32; 2] {
   let query: StateAction = msg::load().expect("failed to decode input argument");
   let encoded = match query {
       StateAction::All => StateReply::All(STATE.votes_received.clone()).encode(),
       StateAction::VotesFor(name) => {
           let votes_for_candidate = STATE
               .votes_received
               .get(&name)
               .expect("Can't find any candidate");
           StateReply::VotesFor(votes_for_candidate.clone()).encode()
       }
   };
   let result = gstd::macros::util::to_wasm_ptr(&encoded[..]);
   core::mem::forget(encoded);
   result
}

源文件:https://github.com/gear-tech/VotingApp

构建 Gear 程序

我们的智能合约准备好了! 现在它需要被编译并上传到 Gear 区块链。让我们开始吧!

在投票应用程序文件夹中,我们编译了我们的智能合约:

RUSTFLAGS="-C link-args=--import-memory" cargo +nightly build --release --target=wasm32-unknown-unknown
wasm-proc --path ./target/wasm32-unknown-unknown/release/voting_app.wasm

我们的应用程序编译成功后,最终的目标文件在 target/wasm32-unknown-unknown/release/voting-app.opt.wasmtarget/wasm32-unknown-unknown/release/voting-app.meta.wasm(meta.wasm 是一个用于与 javascript 程序交互的二进制接口)

需要用到的其他工具

📦 安装 Polkadot.js 扩展

下载并安装 Polkadot.js 浏览器扩展:** https://polkadot.js.org/extension/**

👛 创建账户

使用 Polkadot.js 扩展创建一个新帐户。不要忘记将助记词和密码保存在安全的地方。

✉️ 上传程序

  • 跳转到** https://idea.gear-tech.io/**
  • 使用 Connect 按钮连接到你的账户,允许网站访问你的 Polkadot.js 插件中的钱包。
  • 使用“获取测试帐户”按钮为你的测试帐户充值。此按钮可以按几次。
  • 上传程序 (.opt.wasm) 和 元数据(.meta.wasm) ,并给程序起一个有意义的名字,并将 gas 上限设置为 100,000,000。使用 Polkadot.js 插件对交易进行签名。
  • 在最近上传的程序页面找到该程序并复制它的地址。

📒 增加新的候选人/投票给候选人

  • 在“所有程序”部分中找到你的程序并打开消息发送表单。
  • 增加一个新的候选人或者投票给现有的候选人
  • 将 gas 限制设置为 300,000,000,然后单击 Send request。

📒 读取状态

  • 跳转到在程序页面中读取状态
  • 输入候选人名字作获得它的选票数量,或者让输入为空,查看目前所有的候选人。

在下一篇文章中,我们将会学习如何使用 gtest 库为 Gear 智能合约编写测试用例。


参考资料

[1] 程序的基本结构: https://wiki.gear-tech.io/developing-contracts/gear-program/

关于 GearFans

Gear 是波卡生态的计算组件,GearFans 是 Gear 爱好者社区。

目录
相关文章
|
6月前
|
Rust 安全 Linux
如何使用Rust进行系统编程?
在 Rust 中,要调用系统调用并与底层 C 函数进行交互,通常会使用 `libc` crate。`libc` 提供了 Rust 到 C 的 FFI(Foreign Function Interface)绑定,允许 Rust 代码调用和使用底层的 C 函数和系统调用。
179 0
|
1月前
|
Rust 算法 安全
如何学习Rust编程?
【10月更文挑战第12天】如何学习Rust编程?
42 1
|
1月前
|
Rust 编译器 开发者
Rust宏之derive的设计及实战
【10月更文挑战第18天】在 Rust 中,`derive` 宏是一种自动生成代码的工具,可为结构体和枚举类型自动实现特定 trait,减少重复代码。它通过语法糖简化代码,支持 Debug、Clone、PartialEq 等 trait 的自动实现,并允许开发者自定义 `derive` 宏以扩展功能。
|
2月前
|
Rust 网络协议 安全
Rust在系统编程中的案例分享
Rust在系统编程中的案例分享
57 10
|
2月前
|
Rust 安全 前端开发
30天拿下Rust之图形编程
30天拿下Rust之图形编程
38 0
|
5月前
|
存储 移动开发 Rust
【Rust学习】02_猜谜游戏
让我们一起动手完成一个项目,来快速上手 Rust!本章将介绍 Rust 中一些常用概念,并向您展示如何在实际项目中运用它们。您将会学到 let、match、方法、关联函数、引用外部 crate 等知识!后续章节会深入探讨这些概念的细节。
36 0
|
6月前
|
Web App开发 Rust 安全
一名C++程序员的Rust入门初体验
作者最近尝试写了一些Rust代码,本文主要讲述了对Rust的看法和Rust与C++的一些区别。
|
Rust IDE 编译器
「Rust语言」最全的Rust初学者的完全免费资源(上)
「Rust语言」最全的Rust初学者的完全免费资源
|
Rust IDE 前端开发
「Rust语言」最全的Rust初学者的完全免费资源(下)
「Rust语言」最全的Rust初学者的完全免费资源
|
Rust 安全 JavaScript
你的下一个项目应该使用 Rust 编程的 7 个理由
你的下一个项目应该使用 Rust 编程的 7 个理由
1890 0