Ontology Wasm 自從上線測試網(wǎng)以來,得到了社區(qū)開發(fā)人員的極大關注。因為這項技術使得業(yè)務邏輯復雜的 dApp 合約上鏈成本降低,極大豐富 dApp
Ontology Wasm 自從上線測試網(wǎng)以來,得到了社區(qū)開發(fā)人員的極大關注。因為這項技術使得業(yè)務邏輯復雜的 dApp 合約上鏈成本降低,極大豐富 dApp 生態(tài)。
Ontology Wasm 目前支持使用 Rust 和 C++兩種語言開發(fā)。其中 Rust 語言對 Wasm 的支持更好,生成的字節(jié)碼更加精簡,可以進一步降低合約調(diào)用的費用。那么如何使用 Rust 進行 Ontology 的合約開發(fā)?
一、使用 Rust 進行 Wasm 合約開發(fā)
1.1 新建合約
Cargo 是開發(fā) Rust 程序時一款不可多得的項目構(gòu)建和包管理工具,它可以幫助開發(fā)者更好地組織代碼和第三方庫依賴。新建一個 Ontology Wasm 空合約,僅只需要執(zhí)行下面的命令:
cargo new --lib hello-world
其生成的項目結(jié)構(gòu)是:
|-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}
利用這個依賴項,開發(fā)者可以調(diào)用與本體區(qū)塊鏈交互的接口以及參數(shù)序列化等工具。
1.2 合約入口函數(shù)
每個程序都有一個入口函數(shù),比如我們常見的 main 函數(shù),但是合約并沒有 main 函數(shù)。在用 Rust 開發(fā) Wasm 合約時,默認用 invoke 函數(shù)作為合約執(zhí)行的入口函數(shù)。將 Rust 源代碼編譯成虛擬機可以執(zhí)行的字節(jié)碼時,會對 Rust 中的函數(shù)名進行混淆。為了防止編譯器生成多余的字節(jié)碼,減小合約大小,invoke 函數(shù)要加上#[no_mangle]注解。
Invoke 函數(shù)如何獲得交易執(zhí)行的參數(shù)?ontio_std 庫提供了 runtime::input()函數(shù)用于接收交易執(zhí)行的參數(shù),開發(fā)者可以使用 ZeroCopySource 對接收到的字節(jié)數(shù)組進行反序列化。其中,讀出來的第一個字節(jié)數(shù)組是調(diào)用的方法名,后面讀到的是方法參數(shù)。
合約執(zhí)行結(jié)果是如何返回?ontio_std 庫提供的runtime::ret 函數(shù)可以將方法執(zhí)行結(jié)果返回出去。
一個完整的 invoke 函數(shù)如下:
#[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 合約數(shù)據(jù)序列化和反序列化
在合約開發(fā)過程中,開發(fā)者總會遇到序列化和反序列化的問題,即如何把一個 struct 類型的數(shù)據(jù)保存到數(shù)據(jù)庫中以及從數(shù)據(jù)庫中讀到的字節(jié)數(shù)組如何進行反序列化以獲得 struct 類型的數(shù)據(jù)。
Ontio_std 庫提供了 Decoder 和 Encoder 接口對數(shù)據(jù)進行序列化和反序列化。Struct 結(jié)構(gòu)體的字段也要實現(xiàn) Decoder 和 Encoder 接口,這樣該 struct 才可以實現(xiàn)序列化和反序列化。在對各種數(shù)據(jù)類型進行序列化的時候,需要用到 Sink 實例。Sink 實例有個集合類型的字段 buf,該字段存的是字節(jié)類型數(shù)據(jù),所有序列化的數(shù)據(jù)都會存到 buf 中。
對于固定長度的數(shù)據(jù)(例如:byte、u16、u32和 u64等),直接將該數(shù)據(jù)轉(zhuǎn)換字節(jié)數(shù)組然后存入 buf 中;對于長度不固定的數(shù)據(jù),序列化時需要先序列化長度,然后序列化數(shù)據(jù)(例如不知大小的無符號整數(shù),包括 u16、u32或 u64等)。
反序列化和序列化正好相反。對于所有的序列化方法,都有對應的反序列化方法。反序列化需要用到 Source 實例。該實例有兩個字段 buf 和 pos。Buf 用來存儲要反序列化的數(shù)據(jù),pos 用來存儲當前讀取的位置。讀取指定類型數(shù)據(jù)的時候,如果知道其長度,可以直接讀;對于長度未知的數(shù)據(jù),要先讀出來長度,然后再讀內(nèi)容。
1.4 訪問和更新鏈上的數(shù)據(jù)
Ontology-wasm-cdt-rust 已經(jīng)封裝了鏈上數(shù)據(jù)的操作方法,能夠方便開發(fā)者實現(xiàn)鏈上數(shù)據(jù)的增刪改查等操作。其中:
Ø database::get(key) 用來從鏈上查詢數(shù)據(jù), key 要求實現(xiàn) AsRef 接口。
Ø database::put(key, value) 用來將數(shù)據(jù)存到鏈上,key 要求實現(xiàn) AsRef 接口,value 要求實現(xiàn) Encoder 接口。
Ø database::delete(key) 用來從鏈上刪除數(shù)據(jù),key 要求實現(xiàn) AsRef 接口。
1.5 合約測試
合約方法執(zhí)行時需要訪問鏈上的數(shù)據(jù)并且需要相應的虛擬機進行執(zhí)行合約字節(jié)碼,所以一般需要將合約部署到鏈上才能進行相關測試。但這樣的測試方法比較麻煩。為了使開發(fā)者更方便地測試合約,ontio_std 庫提供了 mock 測試模塊。該模塊提供了鏈上數(shù)據(jù)的模擬,方便開發(fā)者對合約中的方法進行單元測試。
具體案例可參考:
https://github.com/ontio/ontology-wasm-cdt-rust/blob/master/examples/oep5token/src/test.rs
1.6 調(diào)試合約
開發(fā)者可以使用 console::debug(msg) 在合約調(diào)試過程中輸出相關調(diào)試信息。其中, msg 信息會在節(jié)點 log 日志里打印出來。這里有個前置條件,即 Ontology 本地測試節(jié)點啟動的時候日志級別需要設置為 debug 模式。
另外, 開發(fā)者也可以使用 runtime::notify(msg) 在合約調(diào)試過程中輸出相關調(diào)試信息。該方法會將打印出來的信息保存到鏈上,可以通過 getSmartCodeEvent 方法從鏈上查詢。
二、 總結(jié)
Ontology 作為領先公鏈,率先支持 Wasm 合約,為 Wasm 技術的成熟貢獻力量。同時,我們也歡迎更多的 Wasm 技術愛好者加入本體開發(fā)社區(qū),共同打造技術生態(tài)。(Lucas)
關鍵詞: Ontology Wasm 合約調(diào)用 入口函數(shù)