PDX Utopia區(qū)塊鏈協(xié)議棧使用Solidity調(diào)用wasm智能合約

在這個瞬息萬變的世界中阵子,智能合約已成為所有平臺中強有力的服務。Solidity是一種趨勢磷雇,PDX Utopia區(qū)塊鏈協(xié)議棧使用Solidity調(diào)用wasm智能合約羔砾。

什么是Solidity?

Solidity是一種語法類似JavaScript的高級語言相艇。它被設計成以編譯的方式生成以太坊虛擬機代碼颖杏,這種語言的突出優(yōu)點是安全。

Solidity語言是靜態(tài)類型語言坛芽,支持繼承留储、庫和復雜的用戶定義類型,可以使用Solidity語言創(chuàng)建區(qū)塊鏈上運行的投票咙轩、眾籌获讳、錢包等各種類型的智能合約。

以太坊合約中的Solidity

合約是以太坊去中心化應用程序的基本構(gòu)建模塊活喊。所有變量和函數(shù)都是合約的一部分丐膝,這是所有項目的起點。一個名為MyFirst的空合約看起來像這樣:

version pragma ^0.4.19;contract MyFirst{}

Solidity文件的布局

源文件可以包含任意數(shù)量的合約定義钾菊,包括指令和pragma指令帅矗。

注釋

就像任何其他語言一樣,Solidity可以使用單行和多行注釋煞烫。

// This is a single-line comment./*This is amulti-line comment*/

Solidity中的值類型

以下類型也稱為值類型浑此,因為這些類型的變量將始終按值傳遞。

Solidity數(shù)據(jù)結(jié)構(gòu)

Solidity提供三種類型的數(shù)據(jù)結(jié)構(gòu):

使用Solidity調(diào)用wasm智能合約

定義 ABI

>在 `hello-wasm-abi/src/abi.rs` 中定義了 Contract 對象滞详,包含了 `hello-wasm` 樣例中的 <br>凛俱;

>`put/get/getcounter` 三個方法的 `ABI` 描述紊馏,注意,我們還不能直接用 `JSON` 來描述 `ABI`最冰;

>必須使用 `pdxabi::Contract` 來定義聲明灵再。

建議通過以下三步來生成 ABI :

1. 使用 `solidity` 編寫 `contract interface`;

2. 使用 `remix` 編譯 `contract interface` 得到對應的 `ABI` 描述;

3. 參照 `ABI` 描述文件編寫 `pdxabi::Contract`亭敢;

部署 wasm 合約后可以使用合約地址和 contract interface 在 remix 里對合約進行實例化培慌,方便測試。

Solidity Contract Interface

在 [Remix IDE](http://remix.ethereum.org/#optimize=false&version=soljson-v0.5.3+commit.10d17f24.js&evmVersion=null&appVersion=0.7.7) 中編寫合約接口篇裁,并編譯solidity沛慢。

pragma solidity ^0.5.3;contract hello_wasm_abi {? ? ? function getcounter() public view returns(uint256);? ? ? function get(string memory key) public view returns(string memory);? ? function put(string memory key,string memory val) public payable;}

JSON ABI

編譯合約接口可以得到對應的 `ABI JSON` 描述,提供合約地址和此 `JSON ABI` 文檔达布。

`DAPP` 開發(fā)者即可實例化 `hello_wasm_abi` 合約团甲,并使用其中的三個函數(shù):

json[? ? {? ? ? ? "constant": false,? ? "inputs": [? ? ? ? ? {? ? ? ? "name": "key",? ? ? ? "type": "string"? ? ? ? ? },? ? ? {? ? ? ? "name": "val",? ? ? ? "type": "string"? ? ? ? ? ? ? ? ? }? ? ],? ? "name": "put",? ? "outputs": [],? ? ? "payable": true,? ? "stateMutability": "payable",? ? "type": "? ? function"? },? {? ? "constant": true,? ? "inputs": [? ? ? ? ? {? ? ? ? "name": "key",? ? ? ? "type": "string"? ? ? }? ? ],? ? "name": "get",? ? "outputs": [? ? ? ? ? {? ? ? ? "name": "",? ? ? ? "type": "string"? ? ? ? ? }? ? ],? ? "payable": false,? ? "stateMutability": "view",? ? "type": "function"? },? {? ? "constant": true,? ? "inputs": [],? ? "name": "getcounter",? ? "outputs": [? ? {? ? ? ? "name": "",? ? ? ? "type": "uint256"? ? ? }? ? ],? ? "payable": false,? ? "stateMutability": "view",? ? "type": "function"? }]

pdxabi::Contract

根據(jù) `JSON ABI` 描述實例化 `pdxabi::Contract` 對象,用來對合約的 `input/output` 進行序列化和反序列化黍聂。

rustpub fn get_contract_abi() -> pdxabi::Contract {? ? let mut functions: HashMap<String, pdxabi::Function> = HashMap::new();? ? ? let fn_put = pdxabi::Function {? ? ? ? ? ? constant: false,? ? ? ? ? ? name: String::from("put"),? ? ? ? ? ? inputs: Vec::from(vec![? ? ? ? ? ? ? ? ? ? pdxabi::Param { name: String::from("key"), kind: pdxabi::param_type::ParamType::String },? ? ? ? ? ? ? ? ? pdxabi::Param { name: String::from("val"), kind: pdxabi::param_type::ParamType::String },? ? ? ? ? ? ? ]),? ? ? ? outputs: Vec::default(),? ? };? ? let fn_get = pdxabi::Function {? ? ? ? ? ? ? ? ? constant: true,? ? ? ? ? ? ? ? name: String::from("get"),? ? ? ? ? ? ? ? ? ? inputs: Vec::from(vec![? ? ? ? ? ? ? ? ? ? ? ? ? ? pdxabi::Param { name: String::from("key"), kind: pdxabi::param_type::ParamType::String },? ? ? ? ? ? ? ? ? ? ? ]),? ? ? ? ? ? ? ? ? ? ? ? ? outputs: Vec::from(vec![? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pdxabi::Param { name: String::default(), kind: pdxabi::param_type::ParamType::String },? ? ? ? ? ? ? ? ? ? ? ? ? ]),? ? };? ? let fn_getcounter = pdxabi::Function? ? ? ? ? ? ? ? ? ? {? ? ? ? constant: true,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? name: String::from("getcounter"),? ? ? ? ? ? ? ? ? ? ? ? ? ? inputs: Vec::default(),? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? outputs: Vec::from(vec![? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pdxabi::Param { name: String::default(),? ? ? ? ? ? ? ? ? ? ? ? ? kind: pdxabi::param_type::ParamType::Uint(256) },? ? ? ? ]),? };? ? functions.insert(fn_put.clone().name, fn_put.clone());? functions.insert(fn_get.clone().name, fn_get.clone());? ? functions.insert(fn_getcounter.clone().name, fn_getcounter.clone());? pdxabi::Contract {? ? ? ? constructor: None,? ? ? ? functions: functions,? ? ? events: HashMap::default(),? ? ? ? ? fallback: false,? ? ? ? ? ? ? signers: HashMap::default(),? ? }}

使用 `ABI`

>在 hello-wasm-abi 合約中:

```rustextern crate wasm_bindgen;extern crate ewasm_api;??use wasm_bindgen::prelude::*;use ewasm_api::types::*;use ewasm_api::pdx::utils::*;

倒入處理 abi 的開發(fā)庫

use ewasm_api::pdxabi;

pdxabi::Contract 定義的對象放在 abi 模塊中:

pub mod abi;??const COUNTER_KEY: Bytes32 = Bytes32{ bytes: [255; 32] };??fn inc_counter() {? ? let old_v = ewasm_api::storage_load(&COUNTER_KEY);? ? let old_i = bytes_to_uint(&old_v.bytes[..]);? let new_i = old_i + 1;? ? let val = u32_to_bytes32(new_i as u32);? let value = Bytes32 { bytes: val };? ? ewasm_api::storage_store(&COUNTER_KEY, &value);}? ?fn get_counter() -> Vec<u8>? {? ? let v = ewasm_api::storage_load(&COUNTER_KEY);? ? Vec::from(&v.bytes[..])}??fn put_data(k: String, v: String){? ? ewasm_api::pdx::storage_store(k.as_bytes(), v.as_bytes());}??fn get_data(k: String) -> Vec<u8>? {? ? ewasm_api::pdx::storage_load(k.as_bytes())}??#[wasm_bindgen]pub fn main() {? ? inc_counter();? ? let input = ewasm_api::calldata_acquire();? if !input.is_empty() {

從 input 獲取方法簽名躺苦,按照 ABI 規(guī)范,input 的前 4 個 byte 為方法簽名产还。

let fn_sig = &Vec::from(&input[..4]);

根據(jù)方法簽名獲取 function 對象匹厘。

? let function = contract.function_by_sig(fn_sig).expect("error_fn_sig");

通過 function.name 來匹配相應的 handler。

match function.name.as_str() {? ? ? ? ? ? ? "getcounter" => { // function getcounter() public view returns(uint256);

調(diào)用 get_counter 得到返回值脐区,轉(zhuǎn)換成 uint

let rtn = ewasm_api::pdx::utils::bytes_to_uint(get_counter().as_slice());

此方法沒有輸入值愈诚,只有輸出,通過 function.encode_output 序列化輸出 牛隅。

let data = function.encode_output(&[pdxabi::Token::Uint(rtn.into())]).unwrap();

將結(jié)果返回給合約調(diào)用者炕柔。

ewasm_api::finish_data(data.as_slice());? ? ? ? ? ? }? ? ? ? ? "get" => { // function get(string memory key) public view returns(string memory);

此方法有定義輸入 string key , 先用 function.decode_input 解析 input, 得到輸入列表 。

let tokens = function.decode_input(input.as_slice()).expect("error_put_input");

接口中 input 只定義了一個參數(shù)媒佣,所以 key = tokens[0]匕累。

? let key = tokens.get(0).expect("error_put_key");

調(diào)用 get_data(key) 函數(shù),得到 val 的字節(jié)數(shù)組丈攒。

? ? let val = get_data(key.clone().to_string().unwrap());

接口描述輸出值為 string哩罪,所以要將 val 轉(zhuǎn)換為 string。

let rtn = String::from_utf8(val).expect("error_get_val");

使用 function.encode_output 對返回值進行序列化巡验。

let data = function.encode_output(&[pdxabi::Token::String(rtn)]).expect("error_get_output");

將結(jié)果返回給合約調(diào)用者际插。

ewasm_api::finish_data(data.as_slice());? ? ? ? ? }? ? ? ? ? ? "put" => { // function put(string memory key,string memory val) public payable;

此方法有定義輸入 [string key,string val] , 先用 function.decode_input 解析 input, 得到輸入列表。

let tokens = function.decode_input(input.as_slice()).expect("error_put_input");

接口中定義了兩個參數(shù)显设,分別對應 key = tokens[0] , val = tokens[1]框弛。

? let key = tokens.get(0).expect("error_put_key");? ? ? ? ? ? ? let val = tokens.get(1).expect("error_put_val");

調(diào)用 put_data(key,val)。

put_data(key.clone().to_string().unwrap(), val.clone().to_string().unwrap());

結(jié)束調(diào)用捕捂,此方法沒有返回值瑟枫。

ewasm_api::finish()? ? ? ? ? ? }? ? ? ? ? ? _ => ewasm_api::finish()

如果方法匹配失敗斗搞,則直接返回不做任何處理。

? ? ? ? }? ? }}

部署與使用

* 部署合約方式與 `hello-wasm` 樣例相同慷妙,可以參照

[README.md](https://github.com/PDXbaap/ewasm-rust-demo/blob/master/README.md) 中關(guān)于`部署`的描述:

* 調(diào)用合約:部署成功后會得到 `Contract Address` 僻焚,如果使用 `web3` 系列 `SDK` 可以使用 `JSON ABI` + `Contract Address` 來實例化合約,并進行調(diào)用膝擂,如果使用 `remix IDE` 進行測試調(diào)用虑啤,可以使用 `Solidity Contract Interface` + `Contract Address` 來實例化合約并調(diào)用關(guān)于 web3 提供的 SDK 和 remix IDE 的詳細資料請參閱 web3 基金會的相關(guān)資料。

Solidity 調(diào)用 Wasm 合約

用 `sol` 合約來調(diào)用 `wasm` 合約架馋,與 `sol` 調(diào)用 `sol` 方式相同狞山,假設已經(jīng)部署過 `hello-wasm-abi` 這個合約,并得到合約地址 `0xda3ce11d916ffba4a1289cef66a7f142ec5a0f74`,通過 `hello-wasm-abi` 合約接口和地址叉寂,即可實例化這個合約萍启,之后用法與 `sol` 調(diào)用 `sol` 一致。

例如:

soliditypragma solidity ^0.5.3;

hello-wasm-abi 合約接口

contract hello_wasm_abi {? ? ? function getcounter() public view returns(uint256);? ? ? function get(string memory key) public view returns(string memory);? ? function put(string memory key,string memory val) public payable;}? ? ? ?

使用 hello-wasm-abi 合約的 solidity 合約

contract foobar {? ? ? ? function fetch(address addr,string memory key) public view returns(string memory) {

第一個參數(shù) addr 為 wasm 合約地址屏鳍,通過接口和地址實例化合約對象勘纯。

? ? ? ? hello_wasm_abi hello = hello_wasm_abi(addr);

調(diào)用 wasm 合約方法

? return hello.get(key);? ? }? ? ? function set(address addr,string memory key,string memory val) public payable? {? ? ? ? hello_wasm_abi hello = hello_wasm_abi(addr);? ? ? ? hello.put(key,val);? ? }??}

部署 `foobar` 合約后,使用 `hello-wasm-abi` 的合約地址:

`0xda3ce11d916ffba4a1289cef66a7f142ec5a0f74` 作為第一個參數(shù)分別調(diào)用 `fetch` 和 `set` 方法钓瞭,完成對 `hello-wasm-abi` 合約的 `get` 和 `put` 的調(diào)用屡律。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市降淮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搏讶,老刑警劉巖佳鳖,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異媒惕,居然都是意外死亡系吩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門妒蔚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穿挨,“玉大人,你說我怎么就攤上這事肴盏】剖ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵菜皂,是天一觀的道長贞绵。 經(jīng)常有香客問我,道長恍飘,這世上最難降的妖魔是什么榨崩? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任谴垫,我火速辦了婚禮,結(jié)果婚禮上母蛛,老公的妹妹穿的比我還像新娘翩剪。我一直安慰自己,他們只是感情好彩郊,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布前弯。 她就那樣靜靜地躺著,像睡著了一般焦辅。 火紅的嫁衣襯著肌膚如雪博杖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天筷登,我揣著相機與錄音剃根,去河邊找鬼。 笑死前方,一個胖子當著我的面吹牛狈醉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播惠险,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼苗傅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了班巩?” 一聲冷哼從身側(cè)響起渣慕,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抱慌,沒想到半個月后逊桦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡抑进,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年强经,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寺渗。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡匿情,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出信殊,到底是詐尸還是另有隱情炬称,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布涡拘,位于F島的核電站转砖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜府蔗,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一晋控、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧姓赤,春花似錦赡译、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至誓斥,卻和暖如春只洒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背劳坑。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工毕谴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人距芬。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓涝开,卻偏偏與公主長得像,于是被迫代替她去往敵國和親框仔。 傳聞我的和親對象是個殘疾皇子舀武,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容