对于 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 nightlyrustup target add wasm32-unknown-unknown --toolchain nightlycargo 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 = trueopt-level = 's'
关上 src/lib.rs 文件,在文件的结尾,导入咱们所需的 Gear 库文件。再看看这个程序的根本构造 :
#![feature(const_btree_new)]#![no_std]// External packages (crates) and internal modules importuse 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/...
构建 Gear 程序
咱们的智能合约筹备好了! 当初它须要被编译并上传到 Gear 区块链。让咱们开始吧!
在投票应用程序文件夹中,咱们编译了咱们的智能合约:
RUSTFLAGS="-C link-args=--import-memory" cargo +nightly build --release --target=wasm32-unknown-unknownwasm-proc --path ./target/wasm32-unknown-unknown/release/voting_app.wasm
咱们的应用程序编译胜利后,最终的指标文件在 target/wasm32-unknown-unknown/release/voting-app.opt.wasm
和 target/wasm32-unknown-unknown/release/voting-app.meta.wasm
(meta.wasm 是一个用于与 javascript 程序交互的二进制接口)
须要用到的其余工具
装置 Polkadot.js 扩大
下载并装置 Polkadot.js 浏览器扩大:https://polkadot.js.org/exten...
创立账户
应用 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 智能合约编写测试用例。
对于 GearFans
Gear 是波卡生态的计算组件,GearFans 是 Gear 爱好者社区。
- 官网:https://gear-tech.io/
- Twitter:https://twitter.com/gear_techs
- GitHub:https://github.com/gear-tech
- Discord:https://discord.com/invite/7B...
- Telegram 群:https://t.me/gear_tech
- Telegram 中文群:https://t.me/Gear_CN
- Telegram 中文开发群:https://t.me/gear_dev_cn
- QQ 群:677703337