3 个不稳定版本
0.2.1 | 2020年8月31日 |
---|---|
0.2.0 | 2020年6月12日 |
0.1.0 | 2020年2月11日 |
#23 in #ontology
在 ontio-std 中使用
18KB
420 行
ontio-cdk
英文 | 中文
一套使用 rust
开发 ontology WebAssembly 智能合约的工具。
特性
- 区块链交互的运行时 API
- 合约级别存储管理
- 合约测试框架
- Abi 和客户端代码生成
构建开发环境
- 安装 rustup,非 Windows 环境,可以直接执行以下代码:
curl https://sh.rustup.rs -sSf | sh
- 安装 rust 编译器
rustup install nightly
并将默认编译设置为使用 nightly 版本
rustup default nightly
- 安装 wasm32 编译目标
rustup target add wasm32-unknown-unknown
- 集成开发环境
选择编程 IDE 或编辑器,例如 IntelliJ、VSCode、vim 等。
如何编写合约
- 创建项目
cargo new --lib mycontract
- 编辑
Cargo.toml
,添加ontio-cdk
依赖项
[package]
name = "mycontract"
version = "0.1.0"
authors = ["laizy <[email protected]>"]
edition = "2018"
[lib]
crate-type = ["cdylib"] #Compile as a dynamic link library
[dependencies]
ontio-std = {git = "https://github.com/ontio/ontology-wasm-cdt-rust"}
[features]
mock = ["ontio-std/mock"]
- 在
src/lib.rs
中开发合约,合约的基本结构如下:
#![no_std]
use ontio_std::runtime;
// The entry function of the contract, using no_mangle to make it a export function of the wasm contract after compilation.
#[no_mangle]
pub fn invoke() {
runtime::ret(b"hello, world");
}
以下是一个简单的代币合约示例
#![no_std]
extern crate ontio_std as ostd;
use ostd::abi::{Encoder, Sink, Source};
use ostd::prelude::*;
use ostd::{database, runtime};
const KEY_TOTAL_SUPPLY: &str = "total_supply";
const NAME: &str = "wasm_token";
const SYMBOL: &str = "WTK";
const TOTAL_SUPPLY: U128 = 100000000000;
fn initialize() -> bool {
database::put(KEY_TOTAL_SUPPLY, TOTAL_SUPPLY);
true
}
fn balance_of(owner: &Address) -> U128 {
database::get(owner).unwrap_or(0)
}
fn transfer(from: &Address, to: &Address, amount: U128) -> bool {
assert!(runtime::check_witness(from));
let mut frmbal = balance_of(from);
let mut tobal = balance_of(to);
if amount == 0 || frmbal < amount {
return false;
}
database::put(from, frmbal - amount);
database::put(to, tobal + amount);
notify(("Transfer", from, to, amount));
true
}
fn total_supply() -> U128 {
database::get(KEY_TOTAL_SUPPLY).unwrap()
}
#[no_mangle]
pub fn invoke() {
let input = runtime::input();
let mut source = Source::new(&input);
let action = source.read().unwrap();
let mut sink = Sink::new(12);
match action {
"init" => sink.write(initialize()),
"name" => sink.write(NAME),
"symbol" => sink.write(SYMBOL),
"totalSupply" => sink.write(total_supply()),
"balanceOf" => {
let addr = source.read().unwrap();
sink.write(balance_of(addr));
}
"transfer" => {
let (from, to, amount) = source.read().unwrap();
sink.write(transfer(from, to, amount));
}
_ => panic!("unsupported action!"),
}
runtime::ret(sink.bytes())
}
fn notify<T: Encoder>(msg: T) {
let mut sink = Sink::new(16);
sink.write(msg);
runtime::notify(sink.bytes());
}
- 编译合约:由于默认栈大小为 1MB,对于合约来说太大,我们需要将其减小,对于大多数用例,32KB 就足够了。
RUSTFLAGS="-C link-arg=-zstack-size=32768" cargo build --release --target wasm32-unknown-unknown
过程宏
合约通常从输入参数的字节数组开始编写,解析特定的调用方法和调用参数,然后跳转到相应的函数执行,最后将执行结果序列化为字节数组并返回。这与网络服务器从网络中检索字节流、解析特定请求、执行相应的处理函数、并将结果序列化为字节流发送回网络的方式相似。因此,这种繁琐的工作可以以相同的方式处理,这样合约开发者就可以专注于合约本身的开发展。代码生成宏 ontio_std
提供了基于合约接口在编译时自动生成辅助代码的功能。代码生成宏的基本合约结构如下
#[ontio_std::abi_codegen::contract]
pub trait MyToken {
//Define the external interface of the contract
fn initialize(&mut self, owner: &Address) -> bool;
fn name(&self) -> String;
fn balance_of(&self, owner: &Address) -> U128;
fn transfer(&mut self, from: &Address, to: &Address, amount: U128) -> bool;
fn approve(&mut self, approves: &Address, receiver: &Address, amount:U128) -> bool;
fn transfer_from(&mut self, receiver: &Address,approves: &Address, amount:U128) -> bool;
fn allowance(&mut self, approves: &Address, receiver: &Address) -> U128;
fn total_supply(&self) -> U128;
//defining Event of the contract
#[event]
fn Transfer(&self, from: &Address, to: &Address, amount: U128) {}
#[event]
fn Approve(&self, approves:&Address, receiver: &Address, amount: U128) {}
}
pub(crate) struct MyTokenInstance;
#[no_mangle]
pub fn invoke() {
// MyTokenDispatcher is auto generated by abi_codegen::contract,Implements automatic dispatch of contract requests and serialization of results
let mut dispatcher = MyTokenDispatcher::new(MyTokenInstance);
runtime::ret(&dispatcher.dispatch(&runtime::input()));
}
//The specific logic of the realization of the contract
impl MyToken for MyTokenInstance {
fn initialize(&mut self, owner:&Address) -> bool {
///....
}
fn balance_of(&self, owner: &Address) -> U128 {
//...
}
//... Implementation of other functions
}
合约测试
ontio_std::mock
是合约的测试框架,它提供了与链交互的 API 模拟,使得合约开发者能够轻松编写合约测试代码,无需与实际链交互。
要使用测试功能,需要在 Cargo.toml 中设置特性
[features]
mock = ["ontio-std/mock"]
编写测试用例后,使用以下命令运行合约测试:cargo test --features=mock
。
许可证
本项目采用 MIT 许可证。
第三方软件
为了快速探索 wasm 合约开发的可行性,最初开发基于第三方的工作
contract
宏和某些 API 设计基于 pwasm-std,该软件采用 MIT 许可证或 Apache 许可证(版本 2.0)。- 合约测试特性包括对 pwasm-test 的复制和修改
更多详细信息请参阅源代码文件。
第三方许可证的副本可在 LICENSE-THIRD-PARTY 中找到。
依赖
~3MB
~63K SLoC