#运行时 #本体 #ABI #智能合约 #WASM #API #编写

nightly 无 std ontio-std

本体 wasm 合约开发库

5 个版本 (破坏性更新)

0.5.0 2020年12月7日
0.4.0 2020年9月4日
0.3.0 2020年8月31日
0.2.0 2020年6月12日
0.1.0 2020年2月11日

#17 in #本体

Apache-2.0/MIT

130KB
3K SLoC

ontio-cdk

Build Status

英文 | 中文

一套使用 Rust 开发本体 WebAssembly 智能合约的工具。

特性

  • 区块链交互的运行时 API
  • 合约级别的存储管理
  • 合约测试框架
  • ABI 和客户端代码生成

构建开发环境

  1. 安装 rustup,非 Windows 环境可以直接执行以下代码:
curl https://sh.rustup.rs -sSf | sh
  1. 安装 rust 编译器
rustup install nightly

并将默认版本设置为 nightly 版本

rustup default nightly
  1. 安装 wasm32 编译目标
rustup target add wasm32-unknown-unknown
  1. 集成开发环境

选择编程 IDE 或编辑器,例如 IntelliJ、VSCode、vim 等。

如何编写合约

  1. 创建项目
cargo new --lib mycontract
  1. 编辑 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"]
  1. 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());
}
  1. 编译合约:由于默认的栈大小为 1M,这对于合约来说太大,我们需要将其减少,对于大多数用例来说,32KB 就足够了。
RUSTFLAGS="-C link-arg=-zstack-size=32768" cargo build --release --target wasm32-unknown-unknown

过程宏

合同通常从输入参数的字节数组开始编写,解析特定的调用方法和调用参数,然后跳转到相应的函数执行,最后将执行结果序列化为字节数组并返回。这类似于网络服务器从网络中检索字节流,解析出特定的请求,执行相应的处理函数,并将结果序列化为字节流发送回网络。因此,这种繁琐的工作可以像处理Web开发框架一样处理,以便合同开发者专注于合同本身的开发生成宏。 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 中找到。

依赖关系

~3.5MB
~72K SLoC