本體技術視點 | 手把手教你Wasm合約開發

買賣虛擬貨幣

Ontology Wasm 自從上線測試網以來,得到了社羣開發人員的極大關注。因為這項技術使得業務邏輯複雜的 dApp 合約上鍊成本降低,極大豐富 dApp 生態。

Ontology Wasm 目前支援使用 Rust 和 C++兩種語言開發。其中 Rust 語言對 Wasm 的支援更好,生成的位元組碼更加精簡,可以進一步降低合約呼叫的費用。那麼如何使用 Rust 進行 Ontology 的合約開發?

一、使用 Rust 進行 Wasm 合約開發

1.1 新建合約

Cargo 是開發 Rust 程式時一款不可多得的專案構建和包管理工具,它可以幫助開發者更好地組織程式碼和第三方庫依賴。新建一個 Ontology Wasm 空合約,僅只需要執行下面的命令:

cargo new --lib hello-world

其生成的專案結構是:

|-Cargo.toml
|-src
   |-lib.rs

其中,Cargo.toml 檔案用來配置專案基本資訊和依賴庫資訊等,檔案中的[lib]段必須設定成 crate-type = ["cdylib"];而 lib.rs 檔案用來編寫合約邏輯程式碼。另外,需要在配置檔案 Cargo.toml 的[dependencies]段中加入依賴項設定:

ontio-std={https://github.com/ontio/ontology-wasm-cdt-rust}

利用這個依賴項,開發者可以呼叫與本體區塊鏈互動的介面以及引數序列化等工具。

1.2 合約入口函式

每個程式都有一個入口函式,比如我們常見的 main 函式,但是合約並沒有 main 函式。在用 Rust 開發 Wasm 合約時,預設用 invoke 函式作為合約執行的入口函式。將 Rust 原始碼編譯成虛擬機器可以執行的位元組碼時,會對 Rust 中的函式名進行混淆。為了防止編譯器生成多餘的位元組碼,減小合約大小,invoke 函式要加上#[no_mangle]註解。

Invoke 函式如何獲得交易執行的引數?ontio_std 庫提供了 runtime::input()函式用於接收交易執行的引數,開發者可以使用 ZeroCopySource 對接收到的位元組陣列進行反序列化。其中,讀出來的第一個位元組陣列是呼叫的方法名,後面讀到的是方法引數。

合約執行結果是如何返回?ontio_std 庫提供的runtime::ret 函式可以將方法執行結果返回出去。

一個完整的 invoke 函式如下:

#[no_mangle]
pub fn invoke() {
    let input = runtime::input();
    let mut source = ZeroCopySource::new(&input);
    let action: &[u8] = source.read().unwrap();
    let mut sink = Sink::new(12);
    match action {
        b"hello" => sink.write(say_hello()),
        _ => panic!("unsupported action!"),
    }
    runtime::ret(sink.bytes())
}

1.3 合約資料序列化和反序列化

在合約開發過程中,開發者總會遇到序列化和反序列化的問題,即如何把一個 struct 型別的資料儲存到資料庫中以及從資料庫中讀到的位元組陣列如何進行反序列化以獲得 struct 型別的資料。

Ontio_std 庫提供了 Decoder 和 Encoder 介面對資料進行序列化和反序列化。Struct 結構體的欄位也要實現 Decoder 和 Encoder 介面,這樣該 struct 才可以實現序列化和反序列化。在對各種資料型別進行序列化的時候,需要用到 Sink 例項。Sink 例項有個集合型別的欄位 buf,該欄位存的是位元組型別資料,所有序列化的資料都會存到 buf 中。

對於固定長度的資料(例如:byte、u16、u32和 u64等),直接將該資料轉換位元組陣列然後存入 buf 中;對於長度不固定的資料,序列化時需要先序列化長度,然後序列化資料(例如不知大小的無符號整數,包括 u16、u32或 u64等)。

反序列化和序列化正好相反。對於所有的序列化方法,都有對應的反序列化方法。反序列化需要用到 Source 例項。該例項有兩個欄位 buf 和 pos。Buf 用來儲存要反序列化的資料,pos 用來儲存當前讀取的位置。讀取指定型別資料的時候,如果知道其長度,可以直接讀;對於長度未知的資料,要先讀出來長度,然後再讀內容。

1.4 訪問和更新鏈上的資料

Ontology-wasm-cdt-rust 已經封裝了鏈上資料的操作方法,能夠方便開發者實現鏈上資料的增刪改查等操作。其中:
Ø   database::get(key) 用來從鏈上查詢資料, key 要求實現 AsRef 介面。
Ø   database::put(key, value) 用來將資料存到鏈上,key 要求實現 AsRef 介面,value 要求實現 Encoder 介面。
Ø   database::delete(key) 用來從鏈上刪除資料,key 要求實現 AsRef 介面。

1.5 合約測試

合約方法執行時需要訪問鏈上的資料並且需要相應的虛擬機器進行執行合約位元組碼,所以一般需要將合約部署到鏈上才能進行相關測試。但這樣的測試方法比較麻煩。為了使開發者更方便地測試合約,ontio_std 庫提供了 mock 測試模組。該模組提供了鏈上資料的模擬,方便開發者對合約中的方法進行單元測試。

具體案例可參考:
https://github.com/ontio/ontology-wasm-cdt-rust/blob/master/examples/oep5token/src/test.rs

1.6 除錯合約

開發者可以使用 console::debug(msg) 在合約除錯過程中輸出相關除錯資訊。其中, msg 資訊會在節點 log 日誌裡列印出來。這裡有個前置條件,即 Ontology 本地測試節點啟動的時候日誌級別需要設定為 debug 模式。

另外, 開發者也可以使用 runtime::notify(msg) 在合約除錯過程中輸出相關除錯資訊。該方法會將列印出來的資訊儲存到鏈上,可以透過 getSmartCodeEvent 方法從鏈上查詢。

二、 總結

Ontology 作為領先公鏈,率先支援 Wasm 合約,為 Wasm 技術的成熟貢獻力量。同時,我們也歡迎更多的 Wasm 技術愛好者加入本體開發社羣,共同打造技術生態。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;